github.com/isyscore/isc-gobase@v1.5.3-0.20231218061332-cbc7451899e9/validate/matcher/range.go (about) 1 package matcher 2 3 import ( 4 "fmt" 5 "github.com/isyscore/isc-gobase/constants" 6 "reflect" 7 "regexp" 8 "strconv" 9 "strings" 10 t0 "time" 11 "unicode/utf8" 12 13 "github.com/antonmedv/expr" 14 "github.com/antonmedv/expr/compiler" 15 "github.com/antonmedv/expr/parser" 16 "github.com/antonmedv/expr/vm" 17 "github.com/isyscore/isc-gobase/logger" 18 "github.com/isyscore/isc-gobase/time" 19 ) 20 21 type RangeMatch struct { 22 BlackWhiteMatch 23 24 RangeExpress string 25 Script string 26 Begin any 27 End any 28 BeginNow bool 29 EndNow bool 30 Program *vm.Program 31 } 32 33 type RangeEntity struct { 34 beginAli string 35 begin any 36 end any 37 endAli string 38 dateFlag bool 39 dynamicTime bool 40 beginNow bool 41 endNow bool 42 } 43 44 type DynamicTimeNum struct { 45 plusOrMinus bool 46 years int 47 months int 48 days int 49 hours int 50 minutes int 51 seconds int 52 } 53 54 type Predicate func(subCondition string) bool 55 56 // []:时间或者数字范围匹配 57 var rangeRegex = regexp.MustCompile("^([(\\[])(.*)([,,])(\\s)*(.*)([)\\]])$") 58 59 // digitRegex 全是数字匹配(整数,浮点数,0,负数) 60 var digitRegex = regexp.MustCompile("^(0)|^[-+]?([1-9]+\\d*|0\\.(\\d*)|[1-9]\\d*\\.(\\d*))$") 61 62 // 时间的前后计算匹配:(-|+)yMd(h|H)msS 63 var timePlusRegex = regexp.MustCompile("^([-+])?(\\d*y)?(\\d*M)?(\\d*d)?(\\d*H|\\d*h)?(\\d*m)?(\\d*s)?$") 64 65 func (rangeMatch *RangeMatch) Match(_ map[string]interface{}, _ any, field reflect.StructField, fieldValue any) bool { 66 env := map[string]any{ 67 "begin": rangeMatch.Begin, 68 "end": rangeMatch.End, 69 } 70 71 fieldKind := field.Type.Kind() 72 if IsCheckNumber(fieldKind) { 73 env["value"] = fieldValue 74 } else if fieldKind == reflect.String { 75 env["value"] = utf8.RuneCountInString(fmt.Sprintf("%v", fieldValue)) 76 } else if fieldKind == reflect.Slice { 77 env["value"] = reflect.ValueOf(fieldValue).Len() 78 } else if field.Type.String() == "time.Time" { 79 env["value"] = fieldValue.(t0.Time).UnixNano() 80 if rangeMatch.BeginNow { 81 env["begin"] = time.Now().UnixNano() 82 } else if rangeMatch.EndNow { 83 env["end"] = time.Now().UnixNano() 84 } 85 } else { 86 return true 87 } 88 89 output, err := expr.Run(rangeMatch.Program, env) 90 if err != nil { 91 logger.Error("脚本 %v 执行失败: %v", rangeMatch.Script, err.Error()) 92 return false 93 } 94 95 result, err := CastBool(fmt.Sprintf("%v", output)) 96 if err != nil { 97 return false 98 } 99 100 if result { 101 if field.Type.Kind() == reflect.String { 102 if len(fmt.Sprintf("%v", fieldValue)) > 1024 { 103 rangeMatch.SetBlackMsg("属性 [%v] 值 [%v] 字符串长度位于禁用的范围 [%v] 中", field.Name, fieldValue, rangeMatch.RangeExpress) 104 } else { 105 rangeMatch.SetBlackMsg("属性 [%v] 值 [%v] 字符串长度位于禁用的范围 [%v] 中", field.Name, fieldValue, rangeMatch.RangeExpress) 106 } 107 } else if IsCheckNumber(field.Type.Kind()) { 108 rangeMatch.SetBlackMsg("属性 [%v] 值 [%v] 位于禁用的范围 [%v] 中", field.Name, fieldValue, rangeMatch.RangeExpress) 109 } else if field.Type.Kind() == reflect.Slice { 110 if reflect.ValueOf(fieldValue).Len() > 1024 { 111 rangeMatch.SetBlackMsg("属性 [%v] 值 [%v] 数组长度位于禁用的范围 [%v] 中", field.Name, fieldValue, rangeMatch.RangeExpress) 112 } else { 113 rangeMatch.SetBlackMsg("属性 [%v] 值 [%v] 数组长度位于禁用的范围 [%v] 中", field.Name, fieldValue, rangeMatch.RangeExpress) 114 } 115 } else if field.Type.String() == "time.Time" { 116 rangeMatch.SetBlackMsg("属性 [%v] 值 [%v] 时间位于禁用时间段 [%v] 中", field.Name, fieldValue, rangeMatch.RangeExpress) 117 } else { 118 return true 119 } 120 return true 121 } else { 122 if field.Type.Kind() == reflect.String { 123 if len(fmt.Sprintf("%v", fieldValue)) > 1024 { 124 rangeMatch.SetWhiteMsg("属性 [%v] 值 [%v] 长度没有命中只允许的范围 [%v]", field.Name, fieldValue, rangeMatch.RangeExpress) 125 } else { 126 rangeMatch.SetWhiteMsg("属性 [%v] 值 [%v] 长度没有命中只允许的范围 [%v]", field.Name, fieldValue, rangeMatch.RangeExpress) 127 } 128 } else if IsCheckNumber(field.Type.Kind()) { 129 rangeMatch.SetWhiteMsg("属性 [%v] 值 [%v] 没有命中只允许的范围 [%v]", field.Name, fieldValue, rangeMatch.RangeExpress) 130 } else if field.Type.Kind() == reflect.Slice { 131 if reflect.ValueOf(fieldValue).Len() > 1024 { 132 rangeMatch.SetWhiteMsg("属性 [%v] 值 [%v] 数组长度没有命中只允许的范围 [%v]", field.Name, fieldValue, rangeMatch.RangeExpress) 133 } else { 134 rangeMatch.SetWhiteMsg("属性 [%v] 值 [%v] 数组长度没有命中只允许的范围 [%v]", field.Name, fieldValue, rangeMatch.RangeExpress) 135 } 136 } else if field.Type.String() == "time.Time" { 137 rangeMatch.SetWhiteMsg("属性 [%v] 值 [%v] 时间没有命中只允许的时间段 [%v] 中", field.Name, fieldValue, rangeMatch.RangeExpress) 138 } else { 139 return true 140 } 141 return false 142 } 143 } 144 145 func (rangeMatch *RangeMatch) IsEmpty() bool { 146 return rangeMatch.Script == "" 147 } 148 149 func BuildRangeMatcher(objectTypeFullName string, fieldKind reflect.Kind, objectFieldName string, tagName string, subCondition string, errMsg string) { 150 if constants.MATCH != tagName { 151 return 152 } 153 154 if !strings.Contains(subCondition, constants.Range) || !strings.Contains(subCondition, constants.EQUAL) { 155 return 156 } 157 158 index := strings.Index(subCondition, "=") 159 value := subCondition[index+1:] 160 161 rangeEntity := parseRange(fieldKind, value) 162 if rangeEntity == nil { 163 return 164 } 165 166 beginAli := rangeEntity.beginAli 167 begin := rangeEntity.begin 168 end := rangeEntity.end 169 endAli := rangeEntity.endAli 170 beginNow := rangeEntity.beginNow 171 endNow := rangeEntity.endNow 172 173 var script string 174 if begin == nil { 175 if end == nil { 176 if beginNow { 177 if constants.LeftEqual == beginAli { 178 script = "begin <= value" 179 } else if constants.LeftUnEqual == beginAli { 180 script = "begin < value" 181 } 182 } else if endNow { 183 if constants.RightEqual == endAli { 184 script = "value <= end" 185 } else if constants.RightUnEqual == endAli { 186 script = "value < end" 187 } 188 } else { 189 return 190 } 191 } else { 192 if beginNow { 193 if constants.LeftEqual == beginAli && constants.RightEqual == endAli { 194 script = "begin <= value && value <= end" 195 } else if constants.LeftEqual == beginAli && constants.RightUnEqual == endAli { 196 script = "begin <= value && value < end" 197 } else if constants.LeftUnEqual == beginAli && constants.RightEqual == endAli { 198 script = "begin < value && value <= end" 199 } else if constants.LeftUnEqual == beginAli && constants.RightUnEqual == endAli { 200 script = "begin < value && value < end" 201 } 202 } else { 203 if constants.RightEqual == endAli { 204 script = "value <= end" 205 } else if constants.RightUnEqual == endAli { 206 script = "value < end" 207 } 208 } 209 } 210 } else { 211 if end == nil { 212 if endNow { 213 if constants.LeftEqual == beginAli && constants.RightEqual == endAli { 214 script = "begin <= value && value <= end" 215 } else if constants.LeftEqual == beginAli && constants.RightUnEqual == endAli { 216 script = "begin <= value && value < end" 217 } else if constants.LeftUnEqual == beginAli && constants.RightEqual == endAli { 218 script = "begin < value && value <= end" 219 } else if constants.LeftUnEqual == beginAli && constants.RightUnEqual == endAli { 220 script = "begin < value && value < end" 221 } 222 } else { 223 if constants.LeftEqual == beginAli { 224 script = "begin <= value" 225 } else if constants.LeftUnEqual == beginAli { 226 script = "begin < value" 227 } 228 } 229 } else { 230 if constants.LeftEqual == beginAli && constants.RightEqual == endAli { 231 script = "begin <= value && value <= end" 232 } else if constants.LeftEqual == beginAli && constants.RightUnEqual == endAli { 233 script = "begin <= value && value < end" 234 } else if constants.LeftUnEqual == beginAli && constants.RightEqual == endAli { 235 script = "begin < value && value <= end" 236 } else if constants.LeftUnEqual == beginAli && constants.RightUnEqual == endAli { 237 script = "begin < value && value < end" 238 } 239 } 240 } 241 242 tree, err := parser.Parse(script) 243 if err != nil { 244 logger.Error("脚本:%v 解析异常:%v", script, err.Error()) 245 return 246 } 247 248 program, err := compiler.Compile(tree, nil) 249 if err != nil { 250 logger.Error("脚本: %v 编译异常:%v", script, err.Error()) 251 return 252 } 253 254 addMatcher(objectTypeFullName, objectFieldName, &RangeMatch{Program: program, Begin: begin, End: end, Script: script, RangeExpress: value, BeginNow: beginNow, EndNow: endNow}, errMsg, true) 255 } 256 257 func parseRange(fieldKind reflect.Kind, subCondition string) *RangeEntity { 258 subData := rangeRegex.FindAllStringSubmatch(subCondition, -1) 259 if len(subData) > 0 { 260 beginAli := subData[0][1] 261 begin := subData[0][2] 262 end := subData[0][5] 263 endAli := subData[0][6] 264 265 if (begin == "nil" || begin == "") && (end == "nil" || end == "") { 266 logger.Error("range匹配器格式输入错误,start和end不可都为null或者空字符, input=%v", subCondition) 267 return nil 268 } else if begin == "past" || begin == "future" { 269 logger.Error("range匹配器格式输入错误, start不可含有past或者future, input=%v", subCondition) 270 return nil 271 } else if end == "past" || end == "future" { 272 logger.Error("range匹配器格式输入错误, end不可含有past或者future, input=%v", subCondition) 273 return nil 274 } 275 276 // 如果是数字,则按照数字解析 277 if (begin != "" && digitRegex.MatchString(begin)) || (end != "" && digitRegex.MatchString(end)) { 278 beginNum := parseNum(fieldKind, begin) 279 endNum := parseNum(fieldKind, end) 280 281 return &RangeEntity{beginAli: beginAli, begin: beginNum, end: endNum, endAli: endAli, dateFlag: true} 282 } else if (begin != "" && timePlusRegex.MatchString(begin)) || (end != "" && timePlusRegex.MatchString(end)) { 283 // 解析动态时间 284 dynamicBegin := parseDynamicTime(begin) 285 dynamicEnd := parseDynamicTime(end) 286 if dynamicBegin == time.EmptyTime && dynamicEnd == time.EmptyTime { 287 return nil 288 } 289 290 if dynamicBegin == time.EmptyTime { 291 return &RangeEntity{beginAli: beginAli, begin: nil, end: dynamicEnd.UnixNano(), endAli: endAli, dateFlag: true} 292 } else if dynamicEnd == time.EmptyTime { 293 return &RangeEntity{beginAli: beginAli, begin: dynamicBegin.UnixNano(), end: nil, endAli: endAli, dateFlag: true} 294 } else { 295 return &RangeEntity{beginAli: beginAli, begin: dynamicBegin.UnixNano(), end: dynamicEnd.UnixNano(), endAli: endAli, dateFlag: true} 296 } 297 } else { 298 var beginNow bool 299 var endNow bool 300 var beginTime t0.Time 301 var endTime t0.Time 302 if begin == constants.Now { 303 beginNow = true 304 } else { 305 beginTime = time.ParseTime(begin) 306 } 307 308 if end == constants.Now { 309 endNow = true 310 } else { 311 endTime = time.ParseTime(end) 312 } 313 314 beginTimeIsEmpty := time.IsTimeEmpty(beginTime) 315 endTimeIsEmpty := time.IsTimeEmpty(endTime) 316 317 if !beginTimeIsEmpty && !endTimeIsEmpty { 318 if beginTime.After(endTime) { 319 logger.Error("时间的范围起始点不正确,起点时间不应该大于终点时间") 320 return nil 321 } 322 return &RangeEntity{beginAli: beginAli, begin: beginTime.UnixNano(), end: endTime.UnixNano(), endAli: endAli, dateFlag: true, beginNow: beginNow, endNow: endNow} 323 } else if beginTimeIsEmpty && endTimeIsEmpty { 324 logger.Error("range 匹配器格式输入错误,解析数字或者日期失败, time: %v", subData) 325 } else { 326 if !beginTimeIsEmpty { 327 return &RangeEntity{beginAli: beginAli, begin: beginTime.UnixNano(), end: nil, endAli: endAli, dateFlag: true, beginNow: beginNow, endNow: endNow} 328 } else if !endTimeIsEmpty { 329 return &RangeEntity{beginAli: beginAli, begin: nil, end: endTime.UnixNano(), endAli: endAli, dateFlag: true, beginNow: beginNow, endNow: endNow} 330 } else { 331 return nil 332 } 333 } 334 } 335 } else { 336 // 匹配过去和未来的时间 337 if subCondition == constants.Past { 338 // 过去,则范围为(null, now) 339 return &RangeEntity{beginAli: constants.LeftUnEqual, begin: nil, end: nil, endAli: constants.RightUnEqual, dateFlag: true, endNow: true} 340 } else if subCondition == constants.Future { 341 // 未来,则范围为(now, null) 342 return &RangeEntity{beginAli: constants.LeftUnEqual, begin: nil, end: nil, endAli: constants.RightUnEqual, dateFlag: true, beginNow: true} 343 } 344 return nil 345 } 346 return nil 347 } 348 349 func parseNum(fieldKind reflect.Kind, valueStr string) any { 350 if IsCheckNumber(fieldKind) { 351 result, err := Cast(fieldKind, valueStr) 352 if err != nil { 353 return nil 354 } 355 return result 356 } else if fieldKind == reflect.String || fieldKind == reflect.Slice { 357 result, err := strconv.Atoi(valueStr) 358 if err != nil { 359 return nil 360 } 361 return result 362 } else { 363 return nil 364 } 365 } 366 367 func parseDynamicTime(valueStr string) t0.Time { 368 valueStr = strings.TrimSpace(valueStr) 369 if valueStr == "" { 370 return time.EmptyTime 371 } 372 subData := timePlusRegex.FindAllStringSubmatch(valueStr, -1) 373 if len(subData) > 0 { 374 plusOrMinus := subData[0][1] 375 var years, months, days int 376 yearStr := subData[0][2] 377 monthStr := subData[0][3] 378 dayStr := subData[0][4] 379 if yearStr != "" { 380 yearStr = yearStr[:len(yearStr)-1] 381 } 382 if monthStr != "" { 383 monthStr = monthStr[:len(monthStr)-1] 384 } 385 if dayStr != "" { 386 dayStr = dayStr[:len(dayStr)-1] 387 } 388 years, _ = strconv.Atoi(fmt.Sprintf("%v%v", plusOrMinus, yearStr)) 389 months, _ = strconv.Atoi(fmt.Sprintf("%v%v", plusOrMinus, monthStr)) 390 days, _ = strconv.Atoi(fmt.Sprintf("%v%v", plusOrMinus, dayStr)) 391 392 hours := subData[0][5] 393 minutes := subData[0][6] 394 seconds := subData[0][7] 395 396 resultTime := time.AddYears(time.Now(), years) 397 resultTime = time.AddMonths(resultTime, months) 398 resultTime = time.AddDays(resultTime, days) 399 resultTime = time.AddHour(resultTime, plusOrMinus, hours) 400 resultTime = time.AddMinutes(resultTime, plusOrMinus, minutes) 401 resultTime = time.AddSeconds(resultTime, plusOrMinus, seconds) 402 403 return resultTime 404 } 405 return time.EmptyTime 406 }