utils.go 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068
  1. package main
  2. import (
  3. "bytes"
  4. "encoding/json"
  5. "fmt"
  6. "github.com/cespare/xxhash/v2"
  7. "go.mongodb.org/mongo-driver/bson"
  8. "go.uber.org/zap"
  9. "golang.org/x/net/html"
  10. "jygit.jydev.jianyu360.cn/data_processing/common_utils"
  11. "jygit.jydev.jianyu360.cn/data_processing/common_utils/log"
  12. "jygit.jydev.jianyu360.cn/data_processing/common_utils/mongodb"
  13. "jygit.jydev.jianyu360.cn/data_processing/common_utils/udp"
  14. "math"
  15. "net"
  16. "os"
  17. "regexp"
  18. "sort"
  19. "strings"
  20. "time"
  21. "unicode"
  22. "unicode/utf8"
  23. )
  24. // convertToMongoID convertToMongoID
  25. func convertToMongoID(query map[string]interface{}) map[string]interface{} {
  26. result := make(map[string]interface{})
  27. if query == nil {
  28. return result
  29. }
  30. idMap := query["_id"].(map[string]interface{})
  31. if idMap != nil {
  32. tmpQ := map[string]interface{}{}
  33. for c, id := range idMap {
  34. if idStr, ok := id.(string); ok && id != "" {
  35. tmpQ[c] = mongodb.StringTOBsonId(idStr)
  36. }
  37. }
  38. result["_id"] = tmpQ
  39. }
  40. return result
  41. }
  42. // StringSliceValuesEqual 判断切片相等
  43. func StringSliceValuesEqual(a, b []string) bool {
  44. if len(a) != len(b) {
  45. return false
  46. }
  47. sort.Strings(a)
  48. sort.Strings(b)
  49. for i := range a {
  50. if a[i] != b[i] {
  51. return false
  52. }
  53. }
  54. return true
  55. }
  56. // Float64SliceSum float64 数据求和
  57. func Float64SliceSum(nums []float64) float64 {
  58. sum := 0.0
  59. for _, num := range nums {
  60. sum += num
  61. }
  62. return sum
  63. }
  64. func Float64Equal1Precision(a, b float64) bool {
  65. return int(math.Round(a*10)) == int(math.Round(b*10))
  66. }
  67. // chargeType 判断mongo 字段类型和 es 字段类型相匹配
  68. func chargeType(ftype, etype string) bool {
  69. if ftype != "" {
  70. switch ftype {
  71. case "string":
  72. if etype == "keyword" || etype == "text" {
  73. return true
  74. } else {
  75. return false
  76. }
  77. case "bool":
  78. if etype == "boolean" {
  79. return true
  80. } else {
  81. return false
  82. }
  83. case "int64", "int32", "int":
  84. if etype == "long" || etype == "integer" {
  85. return true
  86. } else {
  87. return false
  88. }
  89. case "float64", "float32":
  90. if etype == "double" || etype == "float" {
  91. return true
  92. } else {
  93. return false
  94. }
  95. }
  96. }
  97. return false
  98. }
  99. // IsHanStart 判断字符串是否以汉字开头或者以字母开头
  100. func IsHanStart(s string) bool {
  101. if s == "" {
  102. return false
  103. }
  104. return unicode.Is(unicode.Scripts["Han"], []rune(s)[0]) || unicode.IsLetter([]rune(s)[0])
  105. }
  106. // 判断字符是否为汉字
  107. func IsHan(c rune) bool {
  108. return unicode.Is(unicode.Scripts["Han"], c)
  109. }
  110. // 判断公司名称是否以圆括号开头且括号内汉字开头
  111. func IsBracketStartWithHan(s string) bool {
  112. if len(s) == 0 || s[0] != '(' {
  113. return false
  114. }
  115. // 索引 i 和 j 分别是左右圆括号的位置,如果找不到右圆括号则返回 false
  116. i, j := 0, 0
  117. for j = i + 1; j < len(s); j++ {
  118. if s[j] == ')' {
  119. break
  120. }
  121. }
  122. if j >= len(s) {
  123. return false
  124. }
  125. // 检查圆括号内是否以汉字或字母开头
  126. bracketContent := s[i+1 : j]
  127. if len(bracketContent) == 0 || (!unicode.IsLetter(rune(bracketContent[0])) && !IsHan([]rune(bracketContent)[0])) {
  128. return false
  129. }
  130. return true
  131. }
  132. // IsCompanyName 判断字符串是否以汉字开头、以括号开头并且括号里面是汉字、以"公司"结尾,其中一个条件符合即返回true,否则返回false
  133. func IsCompanyName(s string) bool {
  134. r := []rune(s)
  135. //if len(r) >= 6 && (string(r[len(r)-6:]) == "有限公司" || string(r[len(r)-6:]) == "股份有限公司") {
  136. // return (IsHanStart(s) || IsBracketStartWithHan(s))
  137. //} else if len(r) >= 2 && string(r[len(r)-2:]) == "公司" {
  138. // return (IsHanStart(s) || IsBracketStartWithHan(s))
  139. //}
  140. if len(r) > 2 {
  141. return (IsHanStart(s) || IsBracketStartWithHan(s))
  142. }
  143. return false
  144. }
  145. // GetChineseCharacters 提取字符串中的汉字
  146. func GetChineseCharacters(s string) string {
  147. re := regexp.MustCompile(`[\p{Han}]+`)
  148. return re.FindString(s)
  149. }
  150. func getCompanyName(name string) string {
  151. if IsCompanyName(name) {
  152. return name
  153. }
  154. return GetChineseCharacters(name)
  155. }
  156. func IsUnicodeStart(s string) bool {
  157. if len(s) == 0 {
  158. return false
  159. }
  160. _, size := utf8.DecodeRuneInString(s)
  161. return size > 0
  162. }
  163. // RemoveDuplicateSuffix 去除字符串末尾的重复字词
  164. func RemoveDuplicateSuffix(str string, suffix string) string {
  165. // 构建正则表达式:^(.*?)(重复的结尾词)+$
  166. re := regexp.MustCompile(fmt.Sprintf(`^(.*?)(%s)+$`, suffix))
  167. matches := re.FindStringSubmatch(str)
  168. if len(matches) == 3 {
  169. return matches[1] + matches[2]
  170. }
  171. return str
  172. }
  173. func findName(name string) []map[string]interface{} {
  174. filter := bson.M{"name": name, "status": 1}
  175. info, _ := MgoB.Find("wcc_buyer", filter, nil, nil, false, -1, -1)
  176. return *info
  177. }
  178. func findNameID(id string) []map[string]interface{} {
  179. filter := bson.M{"name_id": id, "status": 1}
  180. info, _ := MgoB.Find("wcc_buyer", filter, nil, nil, false, -1, -1)
  181. return *info
  182. }
  183. // isStringRepeating 判断字符串内字符完全重复,例如:山东大学山东大学
  184. func isStringRepeating(str string) bool {
  185. for i := 0; i < len(str); i++ {
  186. for j := i + 1; j < len(str); j++ {
  187. if str[i] != str[j] {
  188. return false
  189. }
  190. }
  191. }
  192. return true
  193. }
  194. // IsInStringArray 判断数组中是否存在字符串
  195. func IsInStringArray(str string, arr []string) bool {
  196. // 先对字符串数组进行排序
  197. sort.Strings(arr)
  198. // 使用二分查找算法查找字符串
  199. pos := sort.SearchStrings(arr, str)
  200. // 如果找到了则返回 true,否则返回 false
  201. return pos < len(arr) && arr[pos] == str
  202. }
  203. // checkName 检查名称,不符合的返回false
  204. func checkName(name string) (res bool) {
  205. res = true
  206. faleNames := []string{"管理有限公司", "有限公司", "技术公司", "制水公司", "工程有限公司", "耐材公司", "检测公司", "某公司", "现对公司", "集装箱码头分公司", "质检分公司", "司公司", "供电分公司", "建设公司", "水城分公司", "工信公司", "棉纺公司", "安装公司", "管公司", "测试公司", "四公司", "电力分公司",
  207. "原平分公司", "兰州分公司", "基础设施公司", "陆丰分公司", "郴州分公司", "大渡口分公司", "汽车四分公司", "州大学", "开有限公司", "中小学幼儿园", "上级公司下级公司", "楼及幼儿园", "冷询有限公司", "市第一医院", "发展有阶公司", "楼及号地幼儿园", "第一幼儿园", "楼急救中心", "住宅楼及局", "号国际钻井公司", "管理委员会",
  208. "区地块幼儿园", "省人民政府", "具有国家铁路局", "住宅楼及幼儿园", "丿再生资源技术有限公司", "为保证医院", "网及灾备中心", "地块幼儿园", "求石油昆仑燃气限公司", "栋楼及幼儿园", "号固井压裂公司", "号吐哈钻井公司", "股为限公司", "住宅楼及社区服务中心", "井与分公司", "场监督管局", "为了保证本次政府",
  209. "人民政府东环街遂办事处", "龙湖区分公司", "澄海侨史馆", "中国移动科", "中国移动学院", "中国移动分公司"}
  210. for _, v := range faleNames {
  211. if name == v {
  212. res = false
  213. break
  214. }
  215. }
  216. if len([]rune(name)) < 6 {
  217. if strings.HasSuffix(name, "公司") {
  218. res = false
  219. } else if strings.HasSuffix(name, "办事处") {
  220. res = false
  221. } else if strings.HasSuffix(name, "分公司") {
  222. res = false
  223. } else if strings.HasSuffix(name, "管委会") {
  224. res = false
  225. } else if strings.HasSuffix(name, "支行") {
  226. res = false
  227. } else if strings.HasSuffix(name, "网络部") {
  228. res = false
  229. } else if strings.HasSuffix(name, "部队") {
  230. res = false
  231. } else if strings.HasSuffix(name, "医科大学") {
  232. res = false
  233. }
  234. }
  235. if len([]rune(name)) < 7 && strings.HasSuffix(name, "中医药大学") {
  236. res = false
  237. }
  238. if !TDName(name) {
  239. res = false
  240. }
  241. return
  242. }
  243. // TDName 根据开头、结尾、排除词,筛选名称,筛选不符合的数据
  244. func TDName(name string) (res bool) {
  245. res = true
  246. if isStringRepeating(name) { //重复数据,山东大学山东大学
  247. res = false
  248. log.Info("TDName", zap.String("isStringRepeating", name))
  249. return res
  250. }
  251. countWords := []string{"教育局", "学校", "大学", "中学", "审计局", "郑州局", "中医院", "部队", "有限公司"}
  252. //教育局出现二次
  253. for _, c := range countWords {
  254. if strings.Count(name, c) > 1 {
  255. res = false
  256. log.Info("TDName", zap.String("countWords", c), zap.String("name", name))
  257. return res
  258. }
  259. }
  260. //公司出现三次,错误
  261. if strings.Count(name, "公司") > 2 {
  262. res = false
  263. log.Info("TDName", zap.String("公司", "公司出现三次"))
  264. return res
  265. }
  266. //公司出现 二次,但是不是分公司
  267. if strings.Count(name, "公司") > 1 {
  268. if !strings.Contains(name, "分公司") && !strings.Contains(name, "子公司") && !strings.Contains(name, "市公司") {
  269. res = false
  270. log.Info("TDName", zap.String("公司", "公司出现二次,但没有分公司, 子公司"))
  271. return res
  272. }
  273. }
  274. //开头的单词
  275. preWords := []string{"取消", "转发", "省", "会议室", "某", "某单位", "某公司", "某学校", "测试", "某大学", "某医院", "拟建", "机械", "县", "市", "区", "巡察", "人民", "对", "楼", "家长", "春季", "于", "度", "初级中学", "州", "总部", "一号", "含", "育场", "选择", "第包", "医院",
  276. "共赢资产", "从", "实验中学", "公共", "乘用车", "部与", "一直", "一体化", "现对", "现为", "之", "份", "半坡", "下属", "程", "信息", "带", "工务", "技术", "于", "号", "辖属", "造", "计财", "实验", "原", "器械", "是", "师", "雨山区", "云城区", "阜涣", "公联", "批",
  277. "总承包", "姓市", "秋季学期", "哈巴河", "保险", "开发区", "尘设资", "团", "物业", "农村", "部", "东省", "于", "章", "董", "分局", "再", "微", "明", "建", "公司", "审计", "满足", "中学", "届", "大学", "按摩", "中心", "附属", "总", "教育局", "中学", "小学", "学院",
  278. "中医院", "人医院", "学校", "输", "十六", "有限", "节能", "二公司", "分公司", "子公司", "公司", "审计", "河河", "地公司", "六公司", "三公司", "十六公司", "节能",
  279. "股份", "运输公司", "堇", "家纺", "港区"}
  280. // 开头
  281. for _, word := range preWords {
  282. if strings.HasPrefix(name, word) {
  283. res = false
  284. log.Info("TDName", zap.String("preWords", word), zap.String("name", name))
  285. return res
  286. }
  287. }
  288. //结尾单词
  289. sufWords := []string{"市", "某单位", "某公司", "某学校", "测试", "某大学", "某医院", "地产鲨", "项目组", "研究", "公国司", "物资", "评估与医院", "年室", "招标", "设有", "复限公司", "服务", "有限责任", "罩套", "有阴公", "人队", "报告", "厕所", "污水处", "检查", "镇中心",
  290. "显微镜", "段", "现场局", "租赁", "镇", "市中心", "勐阿糖厂", "展厅", "等医院", "广场室", "水利顷", "市室", "交界处", "自助银行", "若源局", "布局", "联合体", "气室", "活动场", "包装公司", "投资", "有限", "等", "设备", "急件", "有限公司有限公司", "公司公司", "分公司分公司",
  291. "大学大学", "学院学院", "大学场", "学校室", "部队部队", "内部", "部分支公司"}
  292. //结尾
  293. for _, word := range sufWords {
  294. if strings.HasSuffix(name, word) {
  295. res = false
  296. log.Info("TDName", zap.String("sufWords", word), zap.String("name", name))
  297. return res
  298. }
  299. }
  300. //包含的单词
  301. falseWords := []string{"丿", "艹", "[ ", "【", "?", "亻", "#", "~", "^", "亻", "*", "$", "、", "/", "*", "<>", "[ ", "【", "?", "某", "病历系统", "不予", "薯公", "标段", "电压互感器", "我",
  302. "省道", "询比", "学校和幼儿园", "住宅", "询价", "办公区", "项目", "中旎髓细兴", "办么", "测试", "项目部", "飞地园", "购", "鉴定", "实验室", "车务段", "可测性设计", "手术室", "第二期", "退", "催促",
  303. "协议中", "影响", "工作", "动力厂", "干扰源", "指定", "达到", "控制器", "竞价", "人哥", "络络络", "路段", "示范区", "一般用", "采供", "修缮", "招标单位", "联系方式", "沟通", "配线架", "及配建",
  304. "任务", "分包", "其他", "行为", "中标人", "教室", "安置", "暂行", "建设单位", "选聘", "合同", "转发", "勿救", "既有", "装置", "任务", "开展", "依据", "指定", "试剂", "景观", "包件", "法人", "合格",
  305. "短管", "投标文件", "核实", "测汞仪", "黑名单", "制作类", "汇编", "收购", "拿", "丿", "艹", "关于", "要求", "举办", "显示所", "右阴公司", "发射", "加装", "复印纸", "终止", "生产处", "提供", "新建",
  306. "该项目", "安装", "改造", "样品", "课室", "水毁道路", "闲置资产", "#", "南校园", "扩建", "道路维修", "等所", "维稳中心", "异议", "音乐厅", "慰问", "介入", "改建", "即", "建设", "某单位",
  307. "医院医院", "中国电建中国电建", "建设建设", "建设规模", "满足", "郑州郑州", "学校学校", "&", ";", ";", "就医院", "酒医院", "至医院", "对", "所需", "提升", "提高", "学期", "学年", "规模", "必须",
  308. "建成", "各公司", "公司与公司", "公司的公司", "公司办公司", "养护"}
  309. // 包含
  310. for _, word := range falseWords {
  311. if strings.Contains(name, word) {
  312. res = false
  313. log.Info("TDName", zap.String("falseWords", word), zap.String("name", name))
  314. return res
  315. }
  316. }
  317. return
  318. }
  319. // ruleBuyer 判断采购单位正确,名称错误返回true,名称正确返回false
  320. func ruleBuyer(input string) (res bool) {
  321. //开头的单词
  322. preWords := []string{"转发", "省", "会议室", "某", "某单位", "某公司", "某学校", "测试", "某大学", "某医院", "拟建", "机械", "县", "市", "区", "巡察", "人民路", "对", "楼", "家长", "春季", "对", "度", "初级中学", "州", "总部", "一号", "含", "育场", "选择", "第包", "医院",
  323. "共赢资产", "从", "实验中学", "公共", "乘用车", "部与", "一直", "一体化", "现对", "现为", "之", "份", "半坡", "下属", "程", "信息", "带", "工务", "技术", "于", "号", "辖属", "造", "计财", "实验", "原", "器械", "是", "师", "雨山区", "云城区", "阜涣", "公联", "批",
  324. "总承包", "姓市", "秋季学期", "哈巴河", "保险", "开发区", "尘设资", "团", "物业", "农村", "部", "二", "后勤", "位于", "选定", "依法", "口", "代表", "这", "逝沮省", "上半年", "第一", "门市", "配套", "八月", "七月", "集回", "维修", "遴选", "但", "增加",
  325. "取消", "采购", "一直", "一体化", "现对", "现为", "之", "份", "取消", "转发", "省", "会议室", "某", "某单位", "某公司", "某学校", "测试", "某大学", "某医院", "拟建", "机械", "县", "市", "区", "巡察", "人民", "对", "楼", "家长", "春季", "于", "度", "初级中学", "州", "总部", "一号", "含", "育场", "选择", "第包", "医院",
  326. "共赢资产", "从", "实验中学", "公共", "乘用车", "部与", "一直", "一体化", "现对", "现为", "之", "份", "半坡", "下属", "程", "信息", "带", "工务", "技术", "于", "号", "辖属", "造", "计财", "实验", "原", "器械", "是", "师", "阜涣", "公联", "批",
  327. "总承包", "姓市", "秋季学期", "哈巴河", "保险", "开发区", "尘设资", "团", "物业", "农村", "部", "东省", "于", "章", "董", "分局", "再", "微", "明", "建", "公司", "审计", "满足", "中学", "届", "大学", "按摩", "中心", "附属", "总", "教育局", "中学", "小学", "学院",
  328. "中医院", "人医院", "学校", "输", "十六", "有限", "节能", "二公司", "分公司", "子公司", "公司", "审计", "河河", "地公司", "六公司", "三公司", "十六公司", "节能", "股份", "运输公司", "堇", "家纺", "港区", "检查"}
  329. //开头关键词
  330. for _, word := range preWords {
  331. if strings.HasPrefix(input, word) {
  332. res = true
  333. return res
  334. }
  335. }
  336. //结尾关键词
  337. suffixes := []string{"院院", "局局", "场场", "小学室", "和社", "人屡政府", "谈话室", "小区局", "今作社", "点场", "人昆政府",
  338. "年度室", "分行银行", "人政府", "人民矢院", "教学楼局", "笺理局", "地场", "人民唉院", "瞥理局", "所院", "农业衣村局",
  339. "民丢局", "委员会老干部", "办非处", "等支局", "监督站局", "停车库局", "检查员", "办直处", "进行局", "楼局", "等局", "人民政府社",
  340. "模块局", "人民政俯", "人民医标院", "农业农局", "园局", "规则局", "人乓政府", "人事条局", "箐理所", "在政府", "重点局", "大学入场",
  341. "入场", "发也站", "赘源局", "计划生育服务中心政府", "第一人民吹院", "有限公司室", "教标育局", "一民政府", "场部", "埋局", "大学大学",
  342. "分公司分公司", "医院医院", "测试", "市", "显微镜", "段", "现场局", "租赁"}
  343. for _, word := range suffixes {
  344. if strings.HasSuffix(input, word) {
  345. res = true
  346. return res
  347. }
  348. }
  349. //包含的关键词
  350. specials := []string{"丿", "艹", "[ ", "【", "?", "亻", "#", "~", "^", "亻", "*", "$", "、", "/", "*",
  351. "<>", "[ ", "【", "?", "某", "我", "第一轮", "第一次", "第一词", "第一季", "各学校", "第一批", "完全学校",
  352. "一致同意", "X", "保体障局", "人们政府", "上设局", "开发茎", "场场", "设保", "武鳖", "集回", "项日", "代理银行",
  353. "辽宁省省会", "菖处", "口国", "人民汰院", "肉州市", "火车站社", "自然资源和规局", "萎员会", "经济技术开发茎",
  354. "有限公司厂", "测试", "建设建设", "建设规模", "满足", "郑州郑州", "学校学校", "&", ";", ";",
  355. "就医院", "酒医院", "至医院", "所需", "提升", "提高", "学期", "学年", "规模",
  356. "拿", "艹", "关于", "要求", "举办", "右阴公司", "发射", "加装", "扩建", "道路维修", "等所",
  357. "维稳中心", "异议", "任务", "分包", "其他", "行为", "中标人", "教室", "安置", "暂行", "选聘", "合同", "转发",
  358. "勿救", "既有", "装置", "开展", "依据", "指定", "一般用", "采供", "修缮", "招标单位", "联系方式", "沟通",
  359. "该项目", "安装", "样品", "课室", "水毁道路", "闲置资产", "某单位", "某公司", "某学校", "测试", "某大学", "某医院", "地产鲨", "项目组",
  360. "公国司", "评估与医院", "年室", "招标", "设有", "复限公司", "罩套", "有阴公", "人队", "报告",
  361. "显微镜", "现场局", "市中心", "勐阿糖厂", "展厅", "等医院", "广场室", "水利顷", "市室", "交界处", "自助银行",
  362. "若源局", "布局", "联合体", "气室", "活动场", "包装公司", "急件", "核实", "测汞仪", "黑名单", "制作类",
  363. "大学大学", "学院学院", "大学场", "学校室", "部队部队", "内部", "部分支公司", "短管", "投标文件", "汇编", "收购"}
  364. for _, v := range specials {
  365. if strings.Contains(input, v) {
  366. res = true
  367. return res
  368. }
  369. }
  370. return res
  371. }
  372. // SendUdpMsg 通知处理企业新增数据
  373. func SendUdpMsg(data map[string]interface{}, target *net.UDPAddr) {
  374. bytes, _ := json.Marshal(data)
  375. UdpClient.WriteUdp(bytes, udp.OP_TYPE_DATA, target)
  376. log.Info("SendUdpMsg", zap.Any("data", data), zap.Any("target", target))
  377. }
  378. // deletePreEsData 删除预处理索引数据
  379. //func deletePreEsData(preId string) {
  380. // now := time.Now()
  381. // month := int(time.Now().Month())
  382. // monthStr := strconv.Itoa(month)
  383. // year := time.Now().Year()
  384. // yearStr := strconv.Itoa(year)
  385. // //当前处理索引名称
  386. // preBiddingIndex := fmt.Sprintf("bidding_%s%s", yearStr, monthStr)
  387. // lastIndex := ""
  388. //
  389. // //按小时创建
  390. // if config.Conf.Env.SpecType == "hour" {
  391. // preBiddingIndex = preBiddingIndex + strconv.Itoa(now.Day()) + strconv.Itoa(now.Hour())
  392. // last := now.Add(-time.Hour)
  393. // month2 := int(last.Month())
  394. // monthStr2 := strconv.Itoa(month2)
  395. // year2 := last.Year()
  396. // yearStr2 := strconv.Itoa(year2)
  397. // dayStr2 := strconv.Itoa(last.Day())
  398. // //上个索引名称
  399. // lastIndex = "bidding_" + yearStr2 + monthStr2
  400. // lastIndex = lastIndex + dayStr2 + strconv.Itoa(last.Hour())
  401. //
  402. // } else if config.Conf.Env.SpecType == "day" {
  403. // //按天创建
  404. // preBiddingIndex = preBiddingIndex + strconv.Itoa(time.Now().Day())
  405. // last := now.AddDate(0, 0, -1)
  406. // month2 := int(last.Month())
  407. // monthStr2 := strconv.Itoa(month2)
  408. // year2 := last.Year()
  409. // yearStr2 := strconv.Itoa(year2)
  410. // dayStr2 := strconv.Itoa(last.Day())
  411. // //上个索引名称
  412. // lastIndex = "bidding_" + yearStr2 + monthStr2
  413. // lastIndex = lastIndex + dayStr2
  414. //
  415. // } else if config.Conf.Env.SpecType == "month" {
  416. // // 月份;
  417. // last := now.AddDate(0, -1, 0)
  418. // month2 := int(last.Month())
  419. // monthStr2 := strconv.Itoa(month2)
  420. // year2 := last.Year()
  421. // yearStr2 := strconv.Itoa(year2)
  422. // //上个索引名称
  423. // lastIndex = "bidding_" + yearStr2 + monthStr2
  424. // }
  425. //
  426. // //删除预处理 索引数据
  427. // if len(PreEs) == 0 {
  428. // time.Sleep(time.Second)
  429. // }
  430. // for _, client := range PreEs {
  431. // if client == nil {
  432. // continue
  433. // }
  434. // // 老索引有数据
  435. // if client.Count(lastIndex, nil) > 0 {
  436. // err := client.DeleteByID(lastIndex, preId)
  437. // if err != nil {
  438. // fmt.Println("deletePreEsData: ", preId, err)
  439. // }
  440. // }
  441. //
  442. // err := client.DeleteByID(preBiddingIndex, preId)
  443. // if err != nil {
  444. // fmt.Println("deletePreEsData: ", preId, err)
  445. // }
  446. // }
  447. //
  448. //}
  449. // saveDb 文件写入
  450. //func saveDb() {
  451. // mutex.Lock()
  452. // defer mutex.Unlock()
  453. // // 如果 cache 为空,则无需执行写入操作
  454. // if cache == nil {
  455. // log.Error("saveDb", zap.Any("cache", "为空"))
  456. // }
  457. //
  458. // if cache.GetCardinality() > 0 {
  459. // fo, err := os.OpenFile(*dbfile, os.O_CREATE|os.O_RDWR|os.O_SYNC|os.O_TRUNC, 0777)
  460. // if err != nil {
  461. // log.Info("saveDb", zap.Error(err))
  462. // }
  463. //
  464. // defer fo.Close()
  465. // _, err = cache.WriteTo(fo)
  466. // if err != nil {
  467. // log.Info("saveDb", zap.Any("cache.WriteTo", err))
  468. // }
  469. // }
  470. //}
  471. // saveDb 文件写入
  472. func saveDb() {
  473. mutex.Lock()
  474. defer mutex.Unlock()
  475. if cache == nil {
  476. log.Error("saveDb", zap.String("cache", "为空"))
  477. return
  478. }
  479. if cache.GetCardinality() == 0 {
  480. log.Info("saveDb", zap.String("提示", "cache 为空,未保存"))
  481. return
  482. }
  483. tmpFile := *dbfile + ".tmp"
  484. bakFile := *dbfile + ".bak"
  485. // 1. 写入临时文件
  486. tmp, err := os.OpenFile(tmpFile, os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0666)
  487. if err != nil {
  488. log.Error("saveDb", zap.String("创建 tmp 文件失败", tmpFile), zap.Error(err))
  489. return
  490. }
  491. _, err = cache.WriteTo(tmp)
  492. tmp.Close()
  493. if err != nil {
  494. log.Error("saveDb", zap.String("写入 tmp 文件失败", tmpFile), zap.Error(err))
  495. return
  496. }
  497. // 2. 原文件备份(.bak)
  498. if _, err := os.Stat(*dbfile); err == nil {
  499. err = os.Rename(*dbfile, bakFile)
  500. if err != nil {
  501. log.Warn("备份失败", zap.String("源文件", *dbfile), zap.Error(err))
  502. }
  503. }
  504. // 3. 原子替换新文件
  505. err = os.Rename(tmpFile, *dbfile)
  506. if err != nil {
  507. log.Error("saveDb", zap.String("tmp 替换 dbfile 失败", ""), zap.Error(err))
  508. return
  509. }
  510. log.Info("saveDb", zap.String("状态", "写入成功,主文件已更新"))
  511. }
  512. // getNewName 获取新的不重复名称
  513. func getNewName(tmp map[string]interface{}) string {
  514. projectName := util.ObjToString(tmp["projectname"])
  515. projectCode := util.ObjToString(tmp["projectcode"])
  516. buyer := util.ObjToString(tmp["buyer"])
  517. firsttime := util.Int64All(tmp["firsttime"])
  518. createtime := util.Int64All(tmp["createtime"])
  519. var projectDate, createDate string
  520. if firsttime > 0 {
  521. projectDate = time.Unix(firsttime, 0).Format("2006-01-02")
  522. }
  523. if createtime > 0 {
  524. createDate = time.Unix(createtime, 0).Format("2006-01-02")
  525. }
  526. var matchWords = make([]string, 0)
  527. if list, ok := tmp["list"].([]interface{}); ok {
  528. if len(list) > 0 {
  529. for _, v := range list {
  530. if da, ok := v.(map[string]interface{}); ok {
  531. title := util.ObjToString(da["title"])
  532. // 使用正则表达式进行匹配
  533. matches := GetPackages(title)
  534. for _, v := range matches {
  535. if !IsInStringArray(v, matchWords) {
  536. matchWords = append(matchWords, v)
  537. }
  538. }
  539. }
  540. }
  541. }
  542. }
  543. //pks := removeDuplicates(matchWords)
  544. packages := strings.Join(matchWords, "、")
  545. return RenameProjectName(projectName, projectCode, packages, projectDate, buyer, createDate)
  546. }
  547. // hash 计算hash
  548. func hash(src string) uint64 {
  549. return xxhash.Sum64String(src)
  550. }
  551. // RenameProjectName 获取新的不重复的项目名称
  552. func RenameProjectName(projectName, projectCode, packages, projectDate, buyer, createDate string) (newName string) {
  553. //TODO 1.判断项目名称是否重复
  554. var id uint64
  555. defer func() {
  556. if id > 0 && newName != "" {
  557. cache.Add(uint32(id))
  558. cacheModify = true
  559. }
  560. }()
  561. //1.项目名称
  562. if projectName != "" {
  563. id = hash(projectName)
  564. if !cache.Contains(uint32(id)) {
  565. newName = projectName
  566. return projectName
  567. }
  568. }
  569. //TODO 2.1 项目名称+项目编码
  570. if projectCode != "" {
  571. newName = projectName + "_" + projectCode
  572. id = hash(newName)
  573. if !cache.Contains(uint32(id)) {
  574. return newName
  575. }
  576. }
  577. //TODO 2.2 项目名称+分包信息
  578. if packages != "" {
  579. newName = projectName + "_" + packages
  580. id = hash(newName)
  581. if !cache.Contains(uint32(id)) {
  582. return newName
  583. }
  584. }
  585. //TODO 2.3 项目名称+项目时间
  586. if projectDate != "" {
  587. newName = projectName + "_" + projectDate
  588. id = hash(newName)
  589. if !cache.Contains(uint32(id)) {
  590. return newName
  591. }
  592. }
  593. //TODO 2.4 项目名称+采购单位名称
  594. if buyer != "" {
  595. newName = projectName + "_" + buyer
  596. id = hash(newName)
  597. if !cache.Contains(uint32(id)) {
  598. return newName
  599. }
  600. }
  601. //TODO 3.1 项目名称+项目编码+分包信息
  602. if projectCode != "" && packages != "" {
  603. newName = projectName + "_" + projectCode + "_" + packages
  604. id = hash(newName)
  605. if !cache.Contains(uint32(id)) {
  606. return newName
  607. }
  608. }
  609. //TODO 3.2 项目名称+项目编码+项目时间
  610. if projectCode != "" && projectDate != "" {
  611. newName = projectName + "_" + projectCode + "_" + projectDate
  612. id = hash(newName)
  613. if !cache.Contains(uint32(id)) {
  614. return newName
  615. }
  616. }
  617. //TODO 3.3 项目名称+项目编码+采购单位
  618. if projectCode != "" && buyer != "" {
  619. newName = projectName + "_" + projectCode + "_" + buyer
  620. id = hash(newName)
  621. if !cache.Contains(uint32(id)) {
  622. return newName
  623. }
  624. }
  625. //TODO 3.4 项目名称+分包+项目时间
  626. if packages != "" && projectDate != "" {
  627. newName = projectName + "_" + packages + "_" + projectDate
  628. id = hash(newName)
  629. if !cache.Contains(uint32(id)) {
  630. return newName
  631. }
  632. }
  633. //TODO 3.5 项目名称+分包+采购单位
  634. if packages != "" && buyer != "" {
  635. newName = projectName + "_" + packages + "_" + buyer
  636. id = hash(newName)
  637. if !cache.Contains(uint32(id)) {
  638. return newName
  639. }
  640. }
  641. //TODO 3.6 项目名称+项目时间+采购单位
  642. if projectDate != "" && buyer != "" {
  643. newName = projectName + "_" + projectDate + "_" + buyer
  644. id = hash(newName)
  645. if !cache.Contains(uint32(id)) {
  646. return newName
  647. }
  648. }
  649. //TODO 4.1 项目名称+项目编码+分包信息+项目时间
  650. if projectCode != "" && packages != "" && projectDate != "" {
  651. newName = projectName + "_" + projectCode + "_" + packages + "_" + projectDate
  652. id = hash(newName)
  653. if !cache.Contains(uint32(id)) {
  654. return newName
  655. }
  656. }
  657. //TODO 4.2 项目名称+项目编码+分包信息+采购单位
  658. if projectCode != "" && packages != "" && buyer != "" {
  659. newName = projectName + "_" + projectCode + "_" + packages + "_" + buyer
  660. id = hash(newName)
  661. if !cache.Contains(uint32(id)) {
  662. return newName
  663. }
  664. }
  665. //TODO 5 项目名称+项目编码+分包信息+项目时间+采购单位
  666. if projectCode != "" && packages != "" && projectDate != "" && buyer != "" {
  667. newName = projectName + "_" + projectCode + "_" + packages + "_" + projectDate + "_" + buyer
  668. id = hash(newName)
  669. if !cache.Contains(uint32(id)) {
  670. return newName
  671. }
  672. } else {
  673. newName = projectName + "_" + projectCode + "_" + packages + "_" + projectDate + "_" + buyer + "_" + createDate
  674. id = hash(newName)
  675. if !cache.Contains(uint32(id)) {
  676. return newName
  677. } else {
  678. newName = ""
  679. }
  680. }
  681. return
  682. }
  683. // GetPackages 获取对应的分包
  684. func GetPackages(title string) (res []string) {
  685. // 定义正则表达式
  686. rea := regexp.MustCompile(`包\d{1,2}[-~、]\d{1,2}|\d{1,2}[-~、]\d{1,2}包`) //1-6包;01-06包;01、02包;包1、包2
  687. //text := "中国绿发投资集团有限公司直属项目公司2023年第20批集中采购非招标项目(包10、12、14、17、18、19"
  688. packages := rea.FindAllString(util.ObjToString(title), -1) //匹配的包
  689. if len(packages) > 0 {
  690. res = append(res, packages...)
  691. }
  692. reb := regexp.MustCompile(`(标段[1-9一二三四五六七八九]|[1-9一二三四五六七八九]标段|包[1-9一二三四五六七八九]?[0-9]|[1-9一二三四五六七八九]?[0-9]包|[a-kA-K]包)`) // 标题只有一个包2
  693. pgs := reb.FindAllString(title, -1)
  694. if len(pgs) > 0 {
  695. for _, v := range pgs {
  696. if !IsInStringArray(v, res) {
  697. res = append(res, v)
  698. }
  699. }
  700. }
  701. return res
  702. }
  703. // TagMatching 标签结构体
  704. type TagMatching struct {
  705. tagName string //标签名称
  706. tagCode string //标签值(保存)
  707. matchField []string //关键词匹配字段,title,detail
  708. matchKey string //匹配的词语,多个使用逗号连接,"部队,国防,军事,军用"
  709. matchKeyReg []*RegexpInfo //关键词的正则
  710. addField []string //附加词匹配字段
  711. addKey string //附件词匹配关键词
  712. addKeyReg []*RegexpInfo //附加次的正则
  713. excludeField []string //排除词匹配字段
  714. excludeKey string //排除词匹配词
  715. excludeKeyReg []*RegexpInfo //排除词正则
  716. //clearKey []string //清理词匹配字段跟关键词一样
  717. }
  718. // RegexpInfo 关键词正则
  719. type RegexpInfo struct {
  720. keyStr string
  721. regs *regexp.Regexp
  722. }
  723. // GetRegex 根据关键词或者对应正则
  724. func GetRegex(key string) []*RegexpInfo {
  725. var infos []*RegexpInfo
  726. for _, s := range strings.Split(key, ",") {
  727. if strings.Contains(s, "&&") || strings.Contains(s, "&!") {
  728. info := &RegexpInfo{
  729. keyStr: s,
  730. regs: nil,
  731. }
  732. infos = append(infos, info)
  733. } else {
  734. info := &RegexpInfo{
  735. keyStr: s,
  736. regs: regexp.MustCompile(".*(?i)" + s + ".*"),
  737. }
  738. infos = append(infos, info)
  739. }
  740. }
  741. return infos
  742. }
  743. // TaskTags 根据数据和正则规则,验证数据标签
  744. func TaskTags(tmp map[string]interface{}, regs []TagMatching) (tags []string, keyWord, addWord string) {
  745. // 在匹配字段里,比如标题满足了关键词,详情满足附加词,关键词的匹配字段含有标题,附加词的匹配字段含有详情;就符合条件
  746. Loop:
  747. for _, v := range regs {
  748. keyR := false // 关键词匹配结果
  749. addR := false //附加次匹配结果
  750. // 1.排除词
  751. if len(v.excludeField) > 0 && len(v.excludeKeyReg) > 0 {
  752. // 遍历排除词对应的tmp字段信息
  753. for _, f := range v.excludeField {
  754. if val := util.ObjToString(tmp[f]); val != "" {
  755. if rs, _ := getRegsResult(val, v.excludeKeyReg); rs {
  756. //有排除词,直接判断下一个规则
  757. continue Loop
  758. }
  759. }
  760. }
  761. }
  762. // 清理词;目的把 类似 fuck的单词替换为空字符串
  763. //if len(v.clearKey) > 0 && len(v.matchField) > 0 {
  764. // for _, s := range v.clearKey {
  765. // for _, f := range v.matchField {
  766. // if val := util.ObjToString(tmp[f]); val != "" {
  767. // tmp[f] = strings.ReplaceAll(val, s, "")
  768. // }
  769. // }
  770. // }
  771. //}
  772. // 关键词
  773. if len(v.matchField) > 0 && len(v.matchKeyReg) > 0 {
  774. for _, f := range v.matchField {
  775. if val := util.ObjToString(tmp[f]); val != "" {
  776. if rs, da := getRegsResult(val, v.matchKeyReg); rs {
  777. keyR = true
  778. keyWord = da
  779. //log.Print("key", da)
  780. break
  781. }
  782. }
  783. }
  784. }
  785. // 附加词
  786. if len(v.addField) > 0 && len(v.addKeyReg) > 0 && keyR {
  787. for _, f := range v.addField {
  788. if val := util.ObjToString(tmp[f]); val != "" {
  789. if rs, da := getRegsResult(val, v.addKeyReg); rs {
  790. addR = true
  791. addWord = da
  792. //log.Println("add", da)
  793. break
  794. }
  795. }
  796. }
  797. } else {
  798. addR = true
  799. }
  800. // 满足 关键词和附加词条件
  801. if keyR && addR {
  802. // 去重相同标签
  803. if !IsInStringArray(v.tagName, tags) {
  804. tags = append(tags, v.tagName)
  805. }
  806. }
  807. }
  808. return
  809. }
  810. // getRegsResult 验证数据是否符合数组正则
  811. func getRegsResult(data string, regs []*RegexpInfo) (res bool, a string) {
  812. for _, e1 := range regs {
  813. if e1.regs != nil && e1.regs.MatchString(data) {
  814. return true, e1.regs.String()
  815. } else {
  816. // && 特殊处理
  817. if strings.Contains(e1.keyStr, "&&") {
  818. flag := true
  819. for _, s := range strings.Split(e1.keyStr, "&&") {
  820. if !strings.Contains(data, s) {
  821. flag = false
  822. break
  823. }
  824. }
  825. if flag {
  826. return true, e1.keyStr
  827. }
  828. }
  829. // 前面是必须有的关键词&!,后面是不能有的关键词;比如 军队&!点军队,
  830. if strings.Contains(e1.keyStr, "&!") {
  831. keys := strings.Split(e1.keyStr, "&!")
  832. if strings.Contains(data, keys[0]) && !strings.Contains(data, keys[1]) {
  833. return true, e1.keyStr
  834. }
  835. }
  836. }
  837. }
  838. return false, ""
  839. }
  840. // CleanHTMLTags 处理HTML内容并返回清理后的文本
  841. func CleanHTMLTags(htmlContent string) (string, error) {
  842. // 解析HTML内容
  843. doc, err := html.Parse(strings.NewReader(htmlContent))
  844. if err != nil {
  845. return "", err
  846. }
  847. var buf bytes.Buffer
  848. // 递归函数,用来遍历 HTML 树
  849. var f func(*html.Node)
  850. f = func(n *html.Node) {
  851. // 处理文本节点
  852. if n.Type == html.TextNode {
  853. // 去掉文本节点中的所有空格
  854. //buf.WriteString(n.Data)
  855. // 去掉文本节点中的所有空格
  856. trimmedText := strings.ReplaceAll(n.Data, " ", "") // 去掉所有空格
  857. buf.WriteString(trimmedText)
  858. }
  859. // 处理元素节点
  860. if n.Type == html.ElementNode {
  861. // 调试:输出当前节点的类型和标签名
  862. //fmt.Printf("ElementNode: %s\n", n.Data)
  863. // 处理 <br> 标签,插入换行
  864. if n.Data == "br" {
  865. buf.WriteString("\n")
  866. } else if n.Data == "table" {
  867. // 处理表格标签 <table>
  868. for tr := n.FirstChild; tr != nil; tr = tr.NextSibling {
  869. if tr.Type == html.ElementNode && tr.Data == "tr" {
  870. // 遍历每行中的 <td> 单元格
  871. for td := tr.FirstChild; td != nil; td = td.NextSibling {
  872. if td.Data == "td" {
  873. buf.WriteString("[TD] ")
  874. f(td) // 递归处理 <td> 中的内容
  875. }
  876. }
  877. }
  878. }
  879. //buf.WriteString("Table End\n")
  880. } else if n.Data == "ul" {
  881. // 处理无序列表 <ul>
  882. for li := n.FirstChild; li != nil; li = li.NextSibling {
  883. if li.Data == "li" {
  884. buf.WriteString("- ")
  885. f(li)
  886. buf.WriteString("\n")
  887. }
  888. }
  889. }
  890. }
  891. // 遍历子节点
  892. for child := n.FirstChild; child != nil; child = child.NextSibling {
  893. f(child)
  894. }
  895. }
  896. // 启动递归遍历
  897. f(doc)
  898. // 去除多余空格
  899. trimmedText := strings.TrimSpace(buf.String())
  900. return trimmedText, nil
  901. }
  902. // SplitTextByChinesePunctuation 根据中文断句,拆分语句
  903. func SplitTextByChinesePunctuation(text string) []string {
  904. // Regular expression pattern for Chinese punctuation and spaces
  905. // This pattern splits by Chinese punctuation, spaces, and keeps them for splitting.
  906. //pattern := `[。!?;,:\s]+`
  907. // 替换掉所有的 NBSP(不间断空格)为普通空格
  908. text = strings.ReplaceAll(text, "\u00A0", " ")
  909. pattern := `[,。!?、;:]|\s+`
  910. re := regexp.MustCompile(pattern)
  911. // Split the text by the pattern
  912. parts := re.Split(text, -1)
  913. // Filter out empty strings resulting from split
  914. var result []string
  915. for _, part := range parts {
  916. trimmed := strings.TrimSpace(part)
  917. if trimmed != "" {
  918. result = append(result, trimmed)
  919. }
  920. }
  921. return result
  922. }
  923. // RemoveDuplicates 去除字符串数组中重复数据;并去除被长语句包含的短语句
  924. func RemoveDuplicates(strs []string) []string {
  925. var result []string
  926. for _, str := range strs {
  927. // 检查当前短语是否已被 result 中的任何一个较长短语包含
  928. shouldAdd := true
  929. for _, resStr := range result {
  930. if strings.Contains(resStr, str) {
  931. // 如果已有的短语包含当前短语,则不添加当前短语
  932. shouldAdd = false
  933. break
  934. }
  935. }
  936. if shouldAdd {
  937. // 将当前短语添加到结果中
  938. result = append(result, str)
  939. // 再次遍历一遍,移除包含当前短语的任何较短短语
  940. for i := len(result) - 2; i >= 0; i-- {
  941. if strings.Contains(result[i], str) {
  942. // 如果之前的较短短语包含当前短语,则移除该较短短语
  943. result = append(result[:i], result[i+1:]...)
  944. }
  945. }
  946. }
  947. }
  948. return result
  949. }
  950. // CountChineseCharacters 函数统计字符串数组中汉字的总数
  951. func CountChineseCharacters(strs []string) int {
  952. var totalCount int
  953. for _, str := range strs {
  954. for _, r := range str {
  955. // 判断字符是否为汉字且不是标点符号
  956. if unicode.Is(unicode.Han, r) && !unicode.IsPunct(r) {
  957. totalCount++
  958. }
  959. }
  960. }
  961. return totalCount
  962. }