init.go 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661
  1. package main
  2. import (
  3. "context"
  4. "esindex/config"
  5. "flag"
  6. "fmt"
  7. "github.com/RoaringBitmap/roaring"
  8. es7 "github.com/olivere/elastic/v7"
  9. "go.uber.org/zap"
  10. "io/ioutil"
  11. util "jygit.jydev.jianyu360.cn/data_processing/common_utils"
  12. "jygit.jydev.jianyu360.cn/data_processing/common_utils/elastic"
  13. "jygit.jydev.jianyu360.cn/data_processing/common_utils/log"
  14. "jygit.jydev.jianyu360.cn/data_processing/common_utils/mongodb"
  15. "jygit.jydev.jianyu360.cn/data_processing/common_utils/mysqldb"
  16. "os"
  17. "path/filepath"
  18. "strings"
  19. "sync"
  20. "time"
  21. )
  22. var (
  23. ProjectField = make(map[string]string, 500) //项目字段
  24. ProjectListF = make(map[string]string, 200)
  25. BiddingField = make(map[string]string, 200) //bidding_processing_field, level=1 最外层字段,
  26. BiddingLevelField = make(map[string]map[string]string) //level=2 的第二层字段
  27. PreProcessField = make(map[string]string, 500) //预处理流程 bidding字段
  28. dbfile = flag.String("dbfile", "./db", "数据库文件")
  29. cache = roaring.NewBitmap()
  30. cacheModify = false //控制10秒 定时写入文件
  31. mutex sync.Mutex // 互斥锁,用于保护 cache 的并发写入操作
  32. MatchArr = make([]TagMatching, 0) // 存放移动标签规则
  33. globalRegs = make([]TagMatching, 0) //移动标签关键词规则,是标签规则大前提
  34. )
  35. // InitLog @Description
  36. // @Author J 2022/7/26 15:30
  37. func InitLog() {
  38. now := time.Now()
  39. logcfg := config.Conf.Log
  40. err := log.InitLog(
  41. log.Path(logcfg.LogPath),
  42. log.Level(logcfg.LogLevel),
  43. log.Compress(logcfg.Compress),
  44. log.MaxSize(logcfg.MaxSize),
  45. log.MaxBackups(logcfg.MaxBackups),
  46. log.MaxAge(logcfg.MaxAge),
  47. log.Format(logcfg.Format),
  48. )
  49. if err != nil {
  50. fmt.Printf("InitLog failed: %v\n", err)
  51. os.Exit(1)
  52. }
  53. log.Info("InitLog", zap.Any("duration", time.Since(now).Seconds()))
  54. log.Info("InitLog", zap.Any("config.db", config.Conf.DB))
  55. }
  56. func InitMgo() {
  57. now := time.Now()
  58. MgoB = &mongodb.MongodbSim{
  59. MongodbAddr: config.Conf.DB.MongoB.Addr,
  60. DbName: config.Conf.DB.MongoB.Dbname,
  61. Size: config.Conf.DB.MongoB.Size,
  62. UserName: config.Conf.DB.MongoB.User,
  63. Password: config.Conf.DB.MongoB.Password,
  64. Direct: config.Conf.DB.MongoB.Direct,
  65. }
  66. MgoB.InitPool()
  67. if config.Conf.DB.MongoB.Addr == "" || config.Conf.DB.MongoB.Dbname == "" {
  68. log.Error("InitMgo", zap.String("MgoB", "地址或者数据库为空"))
  69. }
  70. if config.Conf.DB.MongoB.Coll == "" {
  71. log.Error("InitMgo", zap.String("MgoB", "查询表为空"))
  72. }
  73. log.Info("InitMgo", zap.Any("MgoB duration", time.Since(now).Seconds()))
  74. // 判断是否接入大模型
  75. if config.Conf.Env.Ai {
  76. MgoBOld = &mongodb.MongodbSim{
  77. MongodbAddr: config.Conf.DB.MongoB.Addr,
  78. DbName: "qfw",
  79. Size: config.Conf.DB.MongoB.Size,
  80. UserName: config.Conf.DB.MongoB.User,
  81. Password: config.Conf.DB.MongoB.Password,
  82. Direct: config.Conf.DB.MongoB.Direct,
  83. }
  84. MgoBOld.InitPool()
  85. log.Info("InitMgo", zap.Any("MgoBOLD duration", time.Since(now).Seconds()))
  86. }
  87. //项目信息
  88. MgoP = &mongodb.MongodbSim{
  89. MongodbAddr: config.Conf.DB.MongoP.Addr,
  90. DbName: config.Conf.DB.MongoP.Dbname,
  91. Size: config.Conf.DB.MongoP.Size,
  92. UserName: config.Conf.DB.MongoP.User,
  93. Password: config.Conf.DB.MongoP.Password,
  94. Direct: config.Conf.DB.MongoP.Direct,
  95. }
  96. MgoP.InitPool()
  97. if config.Conf.DB.MongoP.Addr == "" || config.Conf.DB.MongoP.Dbname == "" {
  98. log.Error("InitMgo", zap.String("MongoP", "地址或者数据库为空"))
  99. }
  100. if config.Conf.DB.MongoP.Coll == "" {
  101. log.Error("InitMgo", zap.String("MongoP", "查询表为空"))
  102. }
  103. log.Info("InitMgo", zap.Any("MgoP duration", time.Since(now).Seconds()))
  104. //中标单位定时同步
  105. MgoQ = &mongodb.MongodbSim{
  106. MongodbAddr: config.Conf.DB.MongoQ.Addr,
  107. DbName: config.Conf.DB.MongoQ.Dbname,
  108. Size: config.Conf.DB.MongoQ.Size,
  109. UserName: config.Conf.DB.MongoQ.User,
  110. Password: config.Conf.DB.MongoQ.Password,
  111. Direct: config.Conf.DB.MongoQ.Direct,
  112. }
  113. MgoQ.InitPool()
  114. if config.Conf.DB.MongoQ.Addr == "" || config.Conf.DB.MongoQ.Dbname == "" {
  115. log.Error("InitMgo", zap.String("MongoQ", "地址或者数据库为空"))
  116. }
  117. log.Info("InitMgo", zap.Any("MgoQ duration", time.Since(now).Seconds()))
  118. //181 特殊企业,采购单位验证
  119. MgoS = &mongodb.MongodbSim{
  120. MongodbAddr: config.Conf.DB.MongoS.Addr,
  121. DbName: config.Conf.DB.MongoS.Dbname,
  122. Size: config.Conf.DB.MongoS.Size,
  123. UserName: config.Conf.DB.MongoS.User,
  124. Password: config.Conf.DB.MongoS.Password,
  125. Direct: config.Conf.DB.MongoS.Direct,
  126. }
  127. MgoS.InitPool()
  128. if config.Conf.DB.MongoS.Addr == "" || config.Conf.DB.MongoS.Dbname == "" {
  129. log.Error("InitMgo", zap.String("MongoS", "地址或者数据库为空"))
  130. }
  131. log.Info("InitMgo", zap.Any("MgoS duration", time.Since(now).Seconds()))
  132. }
  133. func InitMysql() {
  134. //采购单位
  135. now := time.Now()
  136. Mysql = &mysqldb.Mysql{
  137. Address: config.Conf.DB.MysqlB.Addr,
  138. DBName: config.Conf.DB.MysqlB.Dbname,
  139. UserName: config.Conf.DB.MysqlB.Username,
  140. PassWord: config.Conf.DB.MysqlB.Password,
  141. }
  142. Mysql.Init()
  143. if config.Conf.DB.MysqlB.Addr == "" || config.Conf.DB.MysqlB.Dbname == "" {
  144. log.Error("InitMysql", zap.String("Mysql", "地址或者数据库为空"))
  145. }
  146. log.Info("InitMysql", zap.Any("MysqlB duration", time.Since(now).Seconds()))
  147. }
  148. func InitEs() {
  149. now := time.Now()
  150. Es = &elastic.Elastic{
  151. S_esurl: config.Conf.DB.Es.Addr,
  152. I_size: config.Conf.DB.Es.Size,
  153. Username: config.Conf.DB.Es.Username,
  154. Password: config.Conf.DB.Es.Password,
  155. }
  156. Es.InitElasticSize()
  157. log.Info("InitEs", zap.String("阿里云", config.Conf.DB.Es.Addr))
  158. if config.Conf.DB.Es.Addr == "" {
  159. log.Error("InitEs", zap.String("ES", "地址或者数据库为空"))
  160. }
  161. if config.Conf.DB.Es.IndexB == "" {
  162. log.Error("InitEs", zap.String("IndexB", "indexb bidding 索引为空,请检查"))
  163. } else {
  164. log.Debug("InitEs", zap.String("IndexB", config.Conf.DB.Es.IndexB))
  165. }
  166. if config.Conf.DB.Es.IndexP == "" {
  167. log.Error("InitEs", zap.String("IndexB", "projectset 项目索引为空,请检查"))
  168. } else {
  169. log.Debug("InitEs", zap.String("IndexP", config.Conf.DB.Es.IndexP))
  170. }
  171. if config.Conf.DB.Es.IndexTmp == "" {
  172. log.Error("InitEs", zap.String("IndexTmp 为空", "请检查是否需要配置;该配置主要生产环境需要"))
  173. }
  174. if config.Conf.DB.Es.IndexWinner == "" {
  175. log.Error("InitEs", zap.String("IndexWinner", "中标单位 索引为空,请检查"))
  176. } else {
  177. log.Debug("InitEs", zap.String("IndexWinner", config.Conf.DB.Es.IndexWinner))
  178. }
  179. if config.Conf.DB.Es.IndexBuyer == "" {
  180. log.Error("InitEs", zap.String("IndexBuyer", "采购单位 索引为空,请检查"))
  181. } else {
  182. log.Debug("InitEs", zap.String("IndexBuyer", config.Conf.DB.Es.IndexBuyer))
  183. }
  184. ////采集爬虫 单服务器部署的es;目前已使用华为云
  185. //Es1 = &elastic.Elastic{
  186. // S_esurl: config.Conf.DB.Es.AddrP,
  187. // I_size: config.Conf.DB.Es.Size,
  188. // Username: config.Conf.DB.Es.Username,
  189. // Password: config.Conf.DB.Es.Password,
  190. //}
  191. //Es1.InitElasticSize()
  192. //华为云 部署的es
  193. if config.Conf.DB.Es.Addr2 != "" {
  194. Es2 = &elastic.Elastic{
  195. S_esurl: config.Conf.DB.Es.Addr2,
  196. I_size: config.Conf.DB.Es.Size,
  197. Username: config.Conf.DB.Es.Username2,
  198. Password: config.Conf.DB.Es.Password2,
  199. }
  200. Es2.InitElasticSize()
  201. log.Info("InitEs", zap.String("华为云Addr2", config.Conf.DB.Es.Addr2))
  202. }
  203. // 华为云新集群,迁移原来阿里云集群数据,标讯、项目、凭安
  204. if config.Conf.DB.Es.Addr3 != "" {
  205. Es3 = &elastic.Elastic{
  206. S_esurl: config.Conf.DB.Es.Addr3,
  207. I_size: config.Conf.DB.Es.Size,
  208. Username: config.Conf.DB.Es.Username3,
  209. Password: config.Conf.DB.Es.Password3,
  210. }
  211. Es3.InitElasticSize()
  212. log.Info("InitEs", zap.String("华为云Addr3", config.Conf.DB.Es.Addr3))
  213. }
  214. if config.Conf.DB.Es.IndexPD == "" {
  215. log.Warn("InitEs", zap.String("项目详情索引 ", "缺少项目详情索引配置,请检查是否需要配置项目详情"))
  216. }
  217. // 详情字段,默认设置50000汉字长度限制
  218. if config.Conf.DB.Es.DetailCount == 0 {
  219. config.Conf.DB.Es.DetailCount = 50000
  220. }
  221. log.Info("InitEs", zap.Any("duration", time.Since(now).Seconds()))
  222. }
  223. func InitField() {
  224. now := time.Now()
  225. info, _ := MgoB.Find("bidding_processing_field", `{"stype": "project"}`, nil, nil, false, -1, -1)
  226. if len(*info) > 0 {
  227. for _, m := range *info {
  228. if util.IntAll(m["level"]) == 1 {
  229. ProjectField[util.ObjToString(m["field"])] = util.ObjToString(m["ftype"])
  230. } else if util.IntAll(m["level"]) == 2 {
  231. ProjectListF[util.ObjToString(m["field"])] = util.ObjToString(m["ftype"])
  232. }
  233. }
  234. }
  235. log.Info("InitField", zap.Int("ProjectField", len(ProjectField)), zap.Int("ProjectListF", len(ProjectListF)))
  236. log.Info("InitField", zap.Any("duration", time.Since(now).Seconds()))
  237. }
  238. // InitPreProcessField 预处理阶段字段
  239. func InitPreProcessField() {
  240. now := time.Now()
  241. info, _ := MgoB.Find("bidding_processing_field", `{"stype": "pre_process"}`, nil, nil, false, -1, -1)
  242. if len(*info) > 0 {
  243. for _, m := range *info {
  244. if util.IntAll(m["level"]) == 1 {
  245. PreProcessField[util.ObjToString(m["field"])] = util.ObjToString(m["ftype"])
  246. }
  247. }
  248. }
  249. log.Info("InitPreProcessField", zap.Int("PreProcessField", len(ProjectField)))
  250. log.Info("InitPreProcessField", zap.Any("duration", time.Since(now).Seconds()))
  251. }
  252. // InitPreEsClient 实例化预处理 索引客户端
  253. //func InitPreEsClient() {
  254. // if len(config.Conf.Pre) > 0 {
  255. // for k, v := range config.Conf.Pre {
  256. // cli := &elastic.Elastic{
  257. // S_esurl: v.Addr,
  258. // I_size: 30,
  259. // Username: v.Username,
  260. // Password: v.Password,
  261. // }
  262. // cli.InitElasticSize()
  263. // PreEs[k] = cli
  264. // }
  265. // }
  266. //}
  267. // InitEsBiddingField 初始化 bidding 索引字段
  268. func InitEsBiddingField() {
  269. now := time.Now()
  270. info, _ := MgoB.Find("bidding_processing_field", `{"stype": "bidding"}`, nil, nil, false, -1, -1)
  271. if len(*info) > 0 {
  272. for _, m := range *info {
  273. if util.IntAll(m["level"]) == 1 {
  274. BiddingField[util.ObjToString(m["field"])] = util.ObjToString(m["ftype"])
  275. } else if util.IntAll(m["level"]) == 2 {
  276. pfield := util.ObjToString(m["pfield"])
  277. pfieldMap := BiddingLevelField[pfield]
  278. if pfieldMap == nil {
  279. pfieldMap = make(map[string]string, 0)
  280. }
  281. pfieldMap[util.ObjToString(m["field"])] = util.ObjToString(m["ftype"])
  282. BiddingLevelField[pfield] = pfieldMap
  283. }
  284. }
  285. }
  286. log.Info("InitEsBiddingField", zap.Int("BiddingField es 一级字段数量", len(BiddingField)))
  287. log.Info("InitEsBiddingField", zap.Int("BiddingLevelField es 二级字段数量", len(BiddingLevelField)))
  288. log.Info("InitEsBiddingField", zap.Any("duration", time.Since(now).Seconds()))
  289. }
  290. // verifyESFields 验证es 定义字段类型和 MongoDB 数据字段
  291. func verifyESFields() {
  292. now := time.Now()
  293. log.Info("verifyESFields", zap.String("开始类型检测", ""))
  294. client, _ := es7.NewClient(
  295. es7.SetURL(config.Conf.DB.Es.Addr),
  296. es7.SetBasicAuth(config.Conf.DB.Es.Username, config.Conf.DB.Es.Password),
  297. es7.SetSniff(false),
  298. )
  299. index := config.Conf.DB.Es.IndexB //索引表 bidding
  300. // 获取 Elasticsearch 索引的 mapping 信息
  301. mapping, err := client.GetMapping().Index(index).Do(context.Background())
  302. if err != nil {
  303. log.Error("verifyESFields", zap.Any("getting Elasticsearch mapping:", err))
  304. }
  305. indexName, _ := GetIndexName(client, index)
  306. if indexName == "" || mapping == nil {
  307. log.Error("verifyESFields", zap.String("索引不存在,请检查索引", index))
  308. os.Exit(-1)
  309. return
  310. }
  311. if mapping[indexName].(map[string]interface{})["mappings"] == nil || mapping[indexName].(map[string]interface{})["mappings"].(map[string]interface{})["properties"] == nil {
  312. log.Error("verifyESFields", zap.String("索引不存在或状态不对,请检查索引", index))
  313. os.Exit(-1)
  314. return
  315. }
  316. properties := mapping[indexName].(map[string]interface{})["mappings"].(map[string]interface{})["properties"].(map[string]interface{})
  317. var errField = make([]string, 0)
  318. var okField = make([]string, 0)
  319. var analyzerMap = make(map[string]string) // 分词信息
  320. var esMap = make(map[string]string) //存储es 字段类型
  321. //
  322. for field, ftype := range BiddingField {
  323. eftypeMap, _ := properties[field].(map[string]interface{})
  324. var etype string
  325. var analyzer string
  326. if fftype, ok := eftypeMap["type"]; ok {
  327. etype = fftype.(string)
  328. esMap[field] = etype
  329. }
  330. if ffanalyzer, ok := eftypeMap["analyzer"]; ok {
  331. analyzer = ffanalyzer.(string)
  332. analyzerMap[field] = analyzer
  333. }
  334. if ftype != "" {
  335. if chargeType(ftype, etype) {
  336. okField = append(okField, field)
  337. } else {
  338. errField = append(errField, field)
  339. }
  340. } else {
  341. if field == "_id" {
  342. continue
  343. } else if field == "purchasinglist" || field == "package" || field == "winnerorder" || field == "procurementlist" {
  344. if eproperties, ok := eftypeMap["properties"]; ok {
  345. if eproMap, ok := eproperties.(map[string]interface{}); ok {
  346. for k, v := range eproMap {
  347. if innerMap, ok := v.(map[string]interface{}); ok {
  348. if innerType, ok := innerMap["type"]; ok {
  349. innerLevel := BiddingLevelField[field]
  350. esMap[fmt.Sprintf("%s.%s", field, k)] = innerType.(string)
  351. if chargeType(innerLevel[k], innerType.(string)) {
  352. okField = append(okField, fmt.Sprintf("%s.%s", field, k))
  353. } else {
  354. errField = append(errField, fmt.Sprintf("%s.%s", field, k))
  355. }
  356. }
  357. }
  358. }
  359. }
  360. }
  361. }
  362. }
  363. }
  364. if len(errField) > 0 {
  365. log.Error("verifyESFields", zap.Int("错误字段数量", len(errField)))
  366. for _, field := range errField {
  367. if strings.Contains(field, ".") {
  368. fe := strings.Split(field, ".")
  369. log.Error(fmt.Sprintf("%s 字段类型错误", field), zap.String(fmt.Sprintf("数据库类型为:%s,但是es字段类型是:", BiddingLevelField[fe[0]][fe[1]]), esMap[field]))
  370. } else {
  371. log.Error(fmt.Sprintf("%s 字段类型错误", field), zap.String(fmt.Sprintf("数据库类型为:%s,但是es字段类型是:", BiddingField[field]), esMap[field]))
  372. }
  373. }
  374. os.Exit(-1)
  375. } else {
  376. log.Info("es 字段类型检测结束,", zap.Int("所有字段都符合,检测字段数量为:", len(okField)))
  377. }
  378. log.Info("verifyESFields", zap.Any("duration", time.Since(now).Seconds()))
  379. }
  380. func GetIndexName(client *es7.Client, name string) (string, error) {
  381. // 判断 name 是否为一个别名
  382. res, err := client.Aliases().Alias(name).Do(context.Background())
  383. if err != nil {
  384. // 错误处理
  385. if err.(*es7.Error).Status != 404 && err.(*es7.Error).Details != nil {
  386. return "", err
  387. }
  388. }
  389. if res != nil {
  390. for k, v := range res.Indices {
  391. for _, vv := range v.Aliases {
  392. if vv.AliasName == name {
  393. return k, nil
  394. }
  395. }
  396. }
  397. }
  398. // 判断 name 是否为一个正式索引名称
  399. resa, err := client.IndexExists(name).Do(context.Background())
  400. if err != nil {
  401. // 错误处理
  402. return "", err
  403. }
  404. if resa {
  405. return name, nil
  406. }
  407. // 如果 name 既不是别名,也不是正式索引名称,则返回空字符串
  408. return "", nil
  409. }
  410. // InitBitmap 初始化项目名称副标题 bitmap
  411. func InitBitmap2() {
  412. if config.Conf.Env.Dbfile != "" {
  413. dbfile = &config.Conf.Env.Dbfile
  414. }
  415. _, err := os.Stat(*dbfile)
  416. log.Info("InitBitmap", zap.String("dbfile", *dbfile))
  417. if !os.IsNotExist(err) {
  418. bs, err := ioutil.ReadFile(*dbfile)
  419. if err != nil {
  420. log.Info("InitBitmap", zap.Error(err))
  421. }
  422. if len(bs) > 0 {
  423. _, err := cache.FromBuffer(bs)
  424. if err != nil {
  425. log.Info("InitBitmap", zap.Any("cache.FromBuffer", err))
  426. }
  427. }
  428. } else {
  429. log.Info("InitBitmap", zap.Any(*dbfile, "文件不存在"))
  430. }
  431. log.Info("InitBitmap", zap.Any("cache.FromBuffer", "success"))
  432. //监听,写入文件保存
  433. go func() {
  434. for {
  435. time.Sleep(10 * time.Second)
  436. if cacheModify {
  437. saveDb()
  438. cacheModify = false
  439. }
  440. }
  441. }()
  442. }
  443. func InitBitmap() {
  444. if config.Conf.Env.Dbfile != "" {
  445. dbfile = &config.Conf.Env.Dbfile
  446. }
  447. log.Info("InitBitmap", zap.String("dbfile", *dbfile))
  448. // 1. 尝试加载主文件
  449. if tryLoadBitmapFile(*dbfile) {
  450. log.Info("InitBitmap", zap.String("状态", "主文件加载成功"))
  451. } else if tryLoadBitmapFile(*dbfile + ".bak") {
  452. log.Warn("InitBitmap", zap.String("警告", "主文件加载失败,已回退至备份文件"))
  453. } else {
  454. // 2. 两个文件都失败,删除旧文件并加载指定备份文件
  455. log.Error("InitBitmap", zap.String("错误", "主文件和备份文件都无法加载,使用手动备份恢复"))
  456. // 删除损坏的文件
  457. os.Remove(*dbfile)
  458. os.Remove(*dbfile + ".bak")
  459. // 3. 加载你指定的备份文件 db_0730
  460. backupFile := strings.TrimSuffix(*dbfile, filepath.Ext(*dbfile)) + "_0730"
  461. if tryLoadBitmapFile(backupFile) {
  462. log.Info("InitBitmap", zap.String("恢复状态", "成功加载手动备份 "+backupFile))
  463. } else {
  464. log.Error("InitBitmap", zap.String("恢复状态", "手动备份文件也加载失败,将初始化空缓存"))
  465. cache = roaring.NewBitmap()
  466. }
  467. }
  468. // 后台保存逻辑保持不变
  469. go func() {
  470. for {
  471. time.Sleep(10 * time.Second)
  472. if cacheModify {
  473. saveDb()
  474. cacheModify = false
  475. }
  476. }
  477. }()
  478. }
  479. // tryLoadBitmapFile 读取并尝试解析到 cache(不 panic,返回是否成功)
  480. func tryLoadBitmapFile(path string) bool {
  481. bs, err := ioutil.ReadFile(path)
  482. if err != nil {
  483. log.Warn("读取失败", zap.String("文件", path), zap.Error(err))
  484. return false
  485. }
  486. if len(bs) == 0 {
  487. log.Warn("文件内容为空", zap.String("文件", path))
  488. return false
  489. }
  490. _, err = cache.FromBuffer(bs)
  491. if err != nil {
  492. log.Warn("文件解析失败(可能损坏)", zap.String("文件", path), zap.Error(err))
  493. return false
  494. }
  495. return true
  496. }
  497. // InitRule 初始化移动标签规则
  498. func InitRule() {
  499. //关键词
  500. globalKeys := "X 型,指挥控制系统,指控系统,指挥信息系统,信息基础设施,基础信息设施,基础设施,信息化,动员大数据,动员潜力,动员信息系统,动员指挥系统军事职业教育,职业教育条件,教学条件建设,训练条件建设,电子对抗,职教条件,教室条件,在线学习室,智慧保卫,智慧政工,智慧靶场,智慧训练场,智慧基地,智慧边疆,边境管控,戍边,训练场,训练室,体育锻炼,体能训练,图传光缆支线建设及传输系统扩容,通信电缆,裸纤,电路租赁,电路租用,互联网接入,互联网出口,无人机,北斗指挥调度,情报保障,气象服务信息共享平台,机动通信,数据中心,云,5G 专网,基础通信业务,专线业务,信息基础设施,增值服务,训练管理,军事职业教育,模拟训练,体育训练,体能训练,实验室,科研建设,智慧校园,智慧党建,宣传工作,保卫工作,智慧监察,管,后勤战勤,智慧财务,智慧军需,智慧营房,智慧军交,智慧卫勤,智慧仓储,装备战勤,枪弹管理,通装管理,战备值勤,日常办公,会议系统,会议室,日常管理,智慧安防,保密管理,心理攻防,野战管理,智能终端管控,网络安全,智慧磐石,国防动员,民兵管理,智慧人防,退役军人服务平台,退役系统创新应用,智慧融通,智慧融办,边海防管控,AICDE,设备采购,综合信息化指挥控制系统,气象服务信息共享平台,应急通信,应急车,应急基站,应急平台,IDC,数据中心,5G 专网,语音系统,话音,裸光纤,机房建设,机房改造,机房运维,体能考核,在线学习室,模拟训练中心,体能考核,无人系统实验平台,智慧教室,智慧党建,红色教育基地,智慧保卫,智慧监察,智慧监管,后勤管理,财务系统,油料保障,营房管理,智慧物流,智慧卫勤,智慧医疗,智慧仓储,装备大数据,枪弹库安防,器材管理,值班系统,办公系统,办公平台,视频,音频会议,会议系统,会议室,门禁,道闸系统,闸机,视频监控,人脸识别,车辆识别,AI 执法巡逻,密集柜管理,心理辅导,野战安防,手机管控,终端管控,网络防御,疫情防控,智慧磐石,国防动员,人防信息化,人防数字化,人防指挥信息系统,退役军人服务平台,云资源租赁,智慧酒店,智慧园区,边海防管控,人工智能,信息化设备,无人机,短波电台,短波台站,私有云,5G 基站,物联网,数据专线,综合网管,智慧靶场,靶机,报靶,桌面云,云桌面,战术模拟,操扬训练条件,智慧课堂,AR 党建,军史场馆,舆情监控,舆情监测,后勤大数据,军粮信息化,营房信息化,智慧运输,医院信息化,装备战备值班,智能枪弹柜,器材库建设,联动报警,网上办公,车辆管理,安防建设,安防设备,安防设施,安防系统,资料管理,心理攻防训练,野战枪弹管理,账外机,流量监测,行程码,一室一站,动员大数据,人防通信及网络,智慧 J 休所,智慧文旅,J 民融合创业园区,J 警民联防,云计算,其他设备,视频指挥,动中通,公有云,5G,专网,局域网,互联网专线,动环监控,训练计划,推门听课,训练辅助,射击仿真,投弹仿真,军体考核,智慧图书馆,VR 党建,数字史馆,智慧监狱,资产管理,智慧食堂,水电信息化,铁路输送,体检管理,装备保障信息系统,装备维修,执勤信息化,档案室,会议设备,警戒系统,报警系统,电子围栏,SM 载体管理,反无人机,信号侦测,信息安全,体温登记,一室两站,国防教育,智慧 J 供站,智慧地产,信息化服务平台,党政军警民联防,大数据,集群指挥,指挥所,自组网,5G 核心网,无线覆盖,光缆支线及设备采购,综合布线,训练档案,在线课程,在线开放课程,AR训练,VR 训练,XR 训练,新闻媒体,物品管理,车辆管理,数字化装备场,执法仪,档案系统,人员管理,枪弹管理,反红外侦查,网上行为监管,安全保密,测温系统,一平台三系统,Z兵系统,退役军人培训,智慧展示平台,边防基础设施建设,数据库,数据采集,数据分析,应急指挥,北斗,卫星通信,VPN,二三级网,弱电工程,训练资源,慕课制作,电子沙盘,演播大厅,智慧养老,接运服务,LED,大屏,显示屏,档案管理,访客管理,周界防护,野外驻训营区管控,黑白名单,量子加密,外来人员判断,反恐,征兵平台,烈士纪念设施保护,海防基础设施建设,CDN,CDN软件,防护工程,可移动基站,4G 网络,传输网,容灾,DICT 实训,JY 网吧,虚拟仿真,融媒体中心,视频通信,网站建设,广播通报,电子巡更,手机智能管控,特安通,重大问题应急处置,维稳,民兵管理,智慧光荣院,可编程企业网关键技术研究与应用验证,辅助决策系统,车载通信,程控交换机,通信服务,训练大数据,训练条件,数字展厅,动态勤务管控,台式机,服务器,军营广播,学习园地,在线考试,油库管理,天幕信息管控,5G网络安全,警务通,智慧武装部,地球通终端,通信车辆,线路整修,训练场,训练基地,训练设备,条件建设,网络中心,智慧小区,智慧消防,灵控辅助定位,导调中心,梯队通联,课程制作,移动旗舰,LED,大屏,显示屏,智慧管理系统,无人机巡检,绿盾痕迹点验,地理信息系统,对讲机,动中通,训练系统,训练软件,J 史场馆,演播大厅,机器人巡逻,低慢小,态势感知,战术机动网络,电视广播,雷达监控,指挥中心,卫星通信,监控系统,监控设施,监控设备,电子对抗,通信设备,大屏系统"
  501. //关键词的规则
  502. global := TagMatching{
  503. tagName: "关键词",
  504. //标题、详情、项目名称、标的物、附件
  505. matchField: []string{"title", "detail", "projectname", "purchasing", "filetext"},
  506. matchKey: globalKeys,
  507. matchKeyReg: GetRegex(globalKeys),
  508. addField: []string{"title", "detail", "projectname", "purchasing", "filetext"},
  509. addKey: "军队,军队采购网,全军武器装备采购信息网,装备科研生产单位,保密二级,保密一级,涉密信息系统集成,参谋,助理,干事",
  510. addKeyReg: GetRegex("军队,军队采购网,全军武器装备采购信息网,装备科研生产单位,保密二级,保密一级,涉密信息系统集成,参谋,助理,干事"),
  511. excludeField: []string{"title", "detail", "projectname", "purchasing", "filetext"},
  512. excludeKey: "网上超市",
  513. excludeKeyReg: GetRegex("网上超市"),
  514. }
  515. globalRegs = append(globalRegs, global)
  516. //军队类
  517. jundui := "解放军,军队,部队,国防,国防部,军委,军事,战争,军用,军史,军营,野战,后勤保障部,武器装备,装备发展部,战区,陆军,海军,空军,火箭军,战略支援部队,联勤保障,军事法院,军事检察院,军事法庭,卫生勤务,卫勤,运输投送,军需,卫戍区,集团军,训练基地," +
  518. "卫星发射中心,试训基地,试验训练基地,国防教育,舰,舰队,舰载,航空兵,雷达兵,电子对抗,军装备研究院,炮兵,空降兵,烈士,舟桥,边海防," +
  519. "人民防空,人防,防空,边海空防,海防,边疆,边防团,国防动员,省军区,军分区,警备区,武装部,人武部,军区&!点军区,经济动员," +
  520. "科技动员,信息动员,交通动员,卫生动员,征兵,民兵,预备役,转业,军人招待所,军休所,兵员,战争院,军事科学院,国防大学,国防科技大学,国防科大,陆军指挥学院,陆军工程大学,步兵学院,装甲兵学院,炮兵防空兵学院,航空兵学院,陆军特种作战学院,边海防学院,防化学院,陆军勤务学院,陆军军事交通学院,海军指挥学院,海军工程大学,海军大连舰艇学院,海军潜艇学院,海军航空大学,海军勤务学院,海军士官学校,空军指挥学院,空军工程大学,空军航空大学,空军预警学院,空军哈尔滨飞行学院,空军石家庄飞行学院,空军西安飞行学院,空军勤务学院,空军通信士官学校,火箭军指挥学院,火箭军工程大学,火箭军士官学校,战略支援军航天工程大学,战略支援军信息工程大学,武装警察部队指挥学院,武警指挥学院,武装警察部队工程大学,武警工程大学,武装警察部队警官学院,武警警官学院,武装警察部队特种警察学院,武警特种警察学院,武装警察部队后勤学院,武警后勤学院,武装警察部队士官学校,武警士官学校,中国人民解放军国际关系学院,国际关系学院,国防信息学院,解放军西安通信学院,解放军电子工程学院,中国人民解放军理工大学,国防大学政法学院,国防大学政治学院,参谋学院,军事文化学院,后装保障学院,气象海洋学院,军医大学," +
  521. "军&&医院,部队&&医院,军医大学附属&&医院,安庆医院,武警&&医院,医学中心&&军"
  522. tagJ1 := TagMatching{
  523. tagName: "军队类",
  524. tagCode: "001",
  525. matchField: []string{"buyer"},
  526. matchKey: jundui,
  527. matchKeyReg: GetRegex(jundui),
  528. }
  529. MatchArr = append(MatchArr, tagJ1)
  530. tagJ2 := TagMatching{
  531. tagName: "军队类",
  532. tagCode: "001",
  533. matchField: []string{"buyer"},
  534. matchKey: "第一采购服务站,第二采购服务站,第三采购服务站,第四采购服务站,第五采购服务站",
  535. matchKeyReg: GetRegex("第一采购服务站,第二采购服务站,第三采购服务站,第四采购服务站,第五采购服务站"),
  536. addField: []string{"site"},
  537. addKey: "军队,军队采购网",
  538. addKeyReg: GetRegex("军队,军队采购网"),
  539. }
  540. MatchArr = append(MatchArr, tagJ2)
  541. //武警类
  542. tagW1 := TagMatching{
  543. tagName: "武警类",
  544. matchField: []string{"buyer"},
  545. matchKey: "海警,水警,军警",
  546. matchKeyReg: GetRegex("海警,水警,军警"),
  547. }
  548. MatchArr = append(MatchArr, tagW1)
  549. tagW2 := TagMatching{
  550. tagName: "武警类",
  551. matchField: []string{"buyer"},
  552. matchKey: "武警,武装警察",
  553. matchKeyReg: GetRegex("武警,武装警察"),
  554. excludeField: []string{"title", "detail", "filetext"},
  555. excludeKey: "黄金,森林,水电",
  556. excludeKeyReg: GetRegex("黄金,森林,水电"),
  557. }
  558. MatchArr = append(MatchArr, tagW2)
  559. tagW3 := TagMatching{
  560. tagName: "武警类",
  561. matchField: []string{"title", "projectname"},
  562. matchKey: "反恐,维稳,军警民,一室一站,一室两站,海警工作站",
  563. matchKeyReg: GetRegex("反恐,维稳,军警民,一室一站,一室两站,海警工作站"),
  564. }
  565. MatchArr = append(MatchArr, tagW3)
  566. //融通类
  567. trong := "融通房地产集团,融通地产,兰州君达中山宾馆,三亚君达大东海壹号酒店,太原长安饭店,北京融通远望楼宾馆,合肥君达徽尚酒店,厦门君联天成宾馆," +
  568. "九江君联庐山酒店,南京君通华江饭店,天津君联京津宾馆,南京融通华山饭店,南京君达金宇饭店,沈阳君达蓝鹰宾馆,成都融通望江宾馆,济南融通联勤宾馆," +
  569. "武汉君通珞珈山宾馆,武汉君通梅园宾馆,南平君达九峰宾馆,上海君通云峰宾馆,三亚君达海景酒店,西安君达桃园宾馆,昆明君通南疆宾馆,黄山君通翡翠度假村," +
  570. "湛江君达南疆宾馆,融旅在线(北京)旅游科技,北京融通西直门宾馆,广州君达华海宾馆,拉萨融通珠峰宾馆,烟台君达毓璜顶宾馆,无锡君联飞鸿宾馆,重庆融通红楼宾馆," +
  571. "济南君达鲁中酒店,青岛君达山海酒店,烟台融通新时代酒店,烟台君通蓝天宾馆,石家庄君达盛华宾馆,郑州君达豫鹰宾馆,海口君联宏翔宾馆,武汉君达蓝天宾馆," +
  572. "三亚融通海棠湾 9 号度假酒店,怀化君达怀荣宾馆,广州君达金城酒店,泰安君达山海酒店,郑州融通紫荆山宾馆,上海君达天鹅宾馆,广州融通天河宾馆,南宁君通桃源饭店," +
  573. "北京融通天泰宾馆,天津君联长城宾馆,广州君通荔圃温泉酒店,广州君达金鹰宾馆,济南融通燕子山庄,天津君通津卫酒店,呼和浩特君达凯盛酒店,广州融通东山宾馆,成都融通新华宾馆," +
  574. "广州君达东风宾馆,成都融通祥宇宾馆,北京融通京海大厦,上海君通蓝天宾馆,秦皇岛君达长城酒店,兰州君通长城宾馆,泰安君通东都宾馆,福州融通梅峰宾馆,成都融通金河宾馆,上海融通延安饭店," +
  575. "乌鲁木齐君达鸿雁宾馆,昆明融通西南宾馆,杭州融通华北饭店,贵阳君通南天宾馆,北京融通华北宾馆,南京君达华达宾馆,海口君联京航酒店,南京融通华东饭店,南京君通九华饭店,南京君达东宫酒店," +
  576. "济南君达长城宾馆,长春君达北煦宾馆,北京君达赵家楼饭店,北京君达京通宾馆,南京君达高楼门饭店,广州君通华泰宾馆,广州融通珠江宾馆,南京融通中央饭店,济南君达黄河宾馆,厦门融通白鹭宾馆," +
  577. "长沙融通长城宾馆,兰州融通西北宾馆,武汉融通中南花园酒店,井冈山君达长城宾馆,杭州君联东海宾馆,西安君达长城宾馆,西宁君联西陲宾馆,济南君达汇源宾馆,中国融通财产保险,融通财险,融寓旅家(北京)公寓管理," +
  578. "融通农业发展(北京),融通农业发展(沈阳),融通农业发展(南京),融通农业发展(乌鲁木齐),融通农业发展(济南),融通农业发展(成都),融通农业发展(昆明),融通农业发展(武汉),融通农业发展(广州)," +
  579. "融通农业发展(杭州),融通农发惠君(青岛),融通粮食产业发展,中国融通财产保险,融通特种物流,融通安保服务,融通安防,融通安保,融通物流,融发能源,融通运输,融通被装发展,陕西融通军民服务社," +
  580. "融通海油能源,融通运输(上海),融通运输(广州),宜宾五商股权投资基金(有限合伙),中海油山东销售,中化物产(青岛),莱州市中海油销售,东营中海油交发油品,淄博赛福橡塑,总参谋部第六十研究所," +
  581. "中国融通科学研究院集团,融通科学院,融通科学研究院,融通资源(海南),融通资源安徽,融通传媒,北京君通银轮宾馆,新华工程咨询,中国融通安庆医院,上海411医院,安庆116医院,淮安82医院,莆田95医院,鹰潭184医院,镇江359医院,苏州100医院,连云港149医院,马鞍山86医院,宁德442医院,郴州198医院,柳州158医院,曲靖69医院,成都81骨科医院,新疆474医院,临夏7医院,泰安88医院,淄博148医院,包头291医院,沈阳121医院,开封155医院,郑州460医院,信阳154医院,天津272医院,正定256医院,邯郸285医院"
  582. tagT1 := TagMatching{
  583. tagName: "融通类",
  584. matchField: []string{"buyer"},
  585. matchKey: trong,
  586. matchKeyReg: GetRegex(trong),
  587. }
  588. MatchArr = append(MatchArr, tagT1)
  589. //退役类
  590. tagTY := TagMatching{
  591. tagName: "退役类",
  592. matchField: []string{"buyer"},
  593. matchKey: "退役,军事供应站,军供站,转业军官培训中心,光荣医院,优抚医院,军粮,军休所,离退休干部休养所,军事休养所,烈士陵园,烈士纪念设施保护中心",
  594. matchKeyReg: GetRegex("退役,军事供应站,军供站,转业军官培训中心,光荣医院,优抚医院,军粮,军休所,离退休干部休养所,军事休养所,烈士陵园,烈士纪念设施保护中心"),
  595. }
  596. MatchArr = append(MatchArr, tagTY)
  597. //融办类
  598. tagRB := TagMatching{
  599. tagName: "融办类",
  600. matchField: []string{"buyer"},
  601. matchKey: "军民融合发展,军民融合发展委员会办公室,军民融合办,军融办,边海防委员会,边防委员会,海防委员会,国防动员委员会,核工业地质局,国防科工局",
  602. matchKeyReg: GetRegex("军民融合发展,军民融合发展委员会办公室,军民融合办,军融办,边海防委员会,边防委员会,海防委员会,国防动员委员会,核工业地质局,国防科工局"),
  603. }
  604. MatchArr = append(MatchArr, tagRB)
  605. //某某类
  606. tagMM := TagMatching{
  607. tagName: "某某类",
  608. matchField: []string{"title", "detail", "filetext"},
  609. matchKey: "某部,某部队,某单位,某校,某学院,某大学,某院",
  610. matchKeyReg: GetRegex("某部,某部队,某单位,某校,某学院,某大学,某院"),
  611. addField: []string{"title", "detail", "filetext"},
  612. addKey: "军队采购网,全军武器装备采购信息网,军队,装备科研生产单位,保密二级,保密一级,涉密信息系统集成",
  613. addKeyReg: GetRegex("军队采购网,全军武器装备采购信息网,军队,装备科研生产单位,保密二级,保密一级,涉密信息系统集成"),
  614. }
  615. MatchArr = append(MatchArr, tagMM)
  616. log.Info("InitRule", zap.Any("globalRegs", len(globalRegs)))
  617. log.Info("InitRule", zap.Any("MatchArr", len(MatchArr)))
  618. }