projectmeger.go 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615
  1. // projectmeger
  2. package main
  3. import (
  4. "encoding/json"
  5. "fmt"
  6. du "jy/util"
  7. "log"
  8. qu "qfw/util"
  9. "qfw/util/redis"
  10. "sort"
  11. "strings"
  12. "sync"
  13. "time"
  14. )
  15. //有效值三选一、三选二
  16. var ThreeToTow, ThreeToOne map[string]bool
  17. func init() {
  18. ThreeToTow = map[string]bool{}
  19. ThreeToOne = map[string]bool{
  20. "AAA": true,
  21. "AAB": true,
  22. }
  23. tows := [][][]string{
  24. [][]string{
  25. []string{"A", "B"},
  26. []string{"A", "B"},
  27. []string{"A", "B"},
  28. },
  29. [][]string{
  30. []string{"A", "B"},
  31. []string{"D", "E"},
  32. []string{"A", "B"},
  33. },
  34. [][]string{
  35. []string{"A", "B"},
  36. []string{"A", "B"},
  37. []string{"D", "E"},
  38. },
  39. [][]string{
  40. []string{"D", "E"},
  41. []string{"A", "B"},
  42. []string{"A", "B"},
  43. },
  44. }
  45. for _, tow := range tows {
  46. for _, v1 := range tow[0] {
  47. for _, v2 := range tow[1] {
  48. for _, v3 := range tow[2] {
  49. key := fmt.Sprintf("%s%s%s", v1, v2, v3)
  50. log.Println(key)
  51. ThreeToTow[key] = true
  52. }
  53. }
  54. }
  55. }
  56. }
  57. func startProjectMerge(thisinfo *Info, tmp map[string]interface{}) {
  58. bNormalScore := false
  59. pcbv := PCBVal(tmp)
  60. if checkInfoAlter(tmp) { //进入变更信息流程
  61. if pcbv.Val > 1 { //判断三项至少包含两项
  62. bNormalScore = true
  63. } else {
  64. extInfoTag("invalid", qu.BsonIdToSId(tmp["_id"])) //无效信息,打标记
  65. //go IS.Add("invalid") //数据统计使用
  66. return
  67. }
  68. } else {
  69. bNormalScore = true
  70. }
  71. //合并流程
  72. if bNormalScore {
  73. PNKeyMap.Store(thisinfo.PNKey, true)
  74. PBKeyMap.Store(thisinfo.PBKey, true)
  75. PCKeyMap.Store(thisinfo.PCKey, true)
  76. if pcbv.Buyer { //有采购单位
  77. hasBuyer(pcbv, thisinfo, tmp)
  78. } else { //无采购单位
  79. noBuyer(pcbv, thisinfo, tmp)
  80. }
  81. }
  82. }
  83. //判断信息是否是变更
  84. func checkInfoAlter(tmp map[string]interface{} /*新信息*/) bool {
  85. toptype := qu.ObjToString(tmp["toptype"])
  86. subtype := qu.ObjToString(tmp["subtype"])
  87. title := qu.ObjToString(tmp["title"])
  88. if subtype == "变更" || strings.Index(title, "变更公告") > -1 || strings.Index(title, "更正公告") > -1 {
  89. //当信息类型是变更或标题中含变更时
  90. if toptype == "招标" {
  91. //招标的变更公告,不作处理
  92. } else if toptype == "结果" {
  93. subtype = "变更"
  94. }
  95. }
  96. return subtype == "变更"
  97. }
  98. //有采购单位
  99. func hasBuyer(p PCBV, thisinfo *Info, tmp map[string]interface{}) {
  100. var pncb []*CompareInfo
  101. var res []interface{}
  102. sflag := ""
  103. pici := time.Now().Unix()
  104. if p.Pname || p.Pcode { //有项目名称或项目编号
  105. //获取对比项目数组
  106. res, pncb = getComeperProjects(p, thisinfo)
  107. //三选二打分
  108. scores := score3Select2(p, thisinfo, tmp, res, pncb)
  109. //项目合并
  110. sflag = mergeProject(tmp, thisinfo, scores, pncb)
  111. } else {
  112. //生成项目,不参与后续对比
  113. sflag = "alone"
  114. mess := map[string]interface{}{
  115. "meger_mess": "有采购单位,不满足三选二",
  116. "meger_sflag": sflag,
  117. }
  118. newProject(tmp, mess, pici, thisinfo)
  119. }
  120. extInfoTag(sflag, thisinfo.Id)
  121. //go IS.Add(sflag) //数据统计使用
  122. }
  123. //无采购单位
  124. func noBuyer(p PCBV, thisinfo *Info, tmp map[string]interface{}) {
  125. var pncb []*CompareInfo
  126. var res []interface{}
  127. sflag := ""
  128. pici := time.Now().Unix()
  129. if p.Pname { //有项目名称
  130. //获取对比项目数组
  131. res, pncb = getComeperProjects(p, thisinfo)
  132. if p.Pcode { //有项目编号
  133. //三选二打分
  134. scores := score3Select2(p, thisinfo, tmp, res, pncb)
  135. //项目合并
  136. sflag = mergeProject(tmp, thisinfo, scores, pncb)
  137. } else { //无项目编号
  138. if p.PnameLen > MegerFieldsLen.ProjectNamelen {
  139. //三选一打分
  140. scores := score3Select1(p, thisinfo, tmp, res, pncb)
  141. //项目合并
  142. sflag = mergeProject(tmp, thisinfo, scores, pncb)
  143. } else {
  144. //生成项目,不参与后续对比
  145. sflag = "alone"
  146. mess := map[string]interface{}{
  147. "meger_mess": "无采购单位,不满足三选一",
  148. "meger_sflag": sflag,
  149. }
  150. newProject(tmp, mess, pici, thisinfo)
  151. }
  152. }
  153. } else { //无项目名称
  154. if p.Pcode {
  155. //获取对比项目数组
  156. res, pncb = getComeperProjects(p, thisinfo)
  157. if p.PcodeLen > MegerFieldsLen.ProjectCodelen {
  158. if p.Area && p.City && p.Agency { //有省市代理机构
  159. //三选一打分
  160. scores := score3Select1(p, thisinfo, tmp, res, pncb)
  161. //项目合并
  162. sflag = mergeProject(tmp, thisinfo, scores, pncb)
  163. } else {
  164. //生成项目,不参与后续对比
  165. sflag = "alone"
  166. mess := map[string]interface{}{
  167. "meger_mess": "无采购单位,不满足三选一",
  168. "meger_sflag": sflag,
  169. }
  170. newProject(tmp, mess, pici, thisinfo)
  171. }
  172. } else {
  173. //生成项目,不参与后续对比
  174. sflag = "alone"
  175. mess := map[string]interface{}{
  176. "meger_mess": "无采购单位,不满足三选一",
  177. "meger_sflag": sflag,
  178. }
  179. newProject(tmp, mess, pici, thisinfo)
  180. }
  181. } else {
  182. //信息无效,打标记
  183. sflag = "invalid"
  184. }
  185. }
  186. extInfoTag(sflag, thisinfo.Id)
  187. //go IS.Add(sflag) //数据统计使用
  188. }
  189. //3选2打分
  190. func score3Select2(p PCBV, thisinfo *Info, tmp map[string]interface{}, res []interface{}, pncb []*CompareInfo) (scores []*CompareOne) {
  191. defer qu.Catch()
  192. if len(res) > 0 {
  193. //对比打分
  194. for k, tmps := range res { //遍历对象数组
  195. if tmp, ok := tmps.([]interface{}); ok {
  196. for _, b1 := range tmp {
  197. if b1 != nil {
  198. var info ProjectInfo
  199. err := json.Unmarshal(b1.([]byte), &info)
  200. if err != nil {
  201. log.Println(err)
  202. }
  203. cone := &CompareOne{
  204. Parent: pncb[k],
  205. Pinfo: &info,
  206. }
  207. cone.BuyerType, cone.Score = fieldPCBScore(thisinfo.Buyer, info.Buyer, cone.BuyerType, cone.Score)
  208. if p.Buyer {
  209. cone.ProjectNameType, cone.Score = fieldPCBScore(thisinfo.ProjectName, info.ProjectName, cone.ProjectNameType, cone.Score)
  210. cone.ProjectCodeType, cone.Score = fieldPCBScore(thisinfo.ProjectCode, info.ProjectCode, cone.ProjectCodeType, cone.Score)
  211. } else { //无采购单位,打分考虑长度
  212. if len([]rune(thisinfo.ProjectName)) > MegerFieldsLen.ProjectNamelen {
  213. cone.ProjectNameType, cone.Score = fieldPCBScore(thisinfo.ProjectName, info.ProjectName, cone.ProjectNameType, cone.Score)
  214. } else {
  215. cone.ProjectNameType = "D"
  216. }
  217. if len(thisinfo.ProjectCode) > MegerFieldsLen.ProjectCodelen {
  218. cone.ProjectCodeType, cone.Score = fieldPCBScore(thisinfo.ProjectCode, info.ProjectCode, cone.ProjectCodeType, cone.Score)
  219. } else {
  220. cone.ProjectCodeType = "D"
  221. }
  222. }
  223. //省市打分
  224. if thisinfo.Area != "A" && thisinfo.Area != "全国" && info.Area != "A" && info.Area != "全国" {
  225. if thisinfo.Area == info.Area && thisinfo.City == info.City {
  226. cone.Score += 2
  227. } else {
  228. cone.Score -= 1
  229. }
  230. } else {
  231. cone.Score += 1
  232. }
  233. //代理机构打分
  234. if len([]rune(info.Agency)) > 0 {
  235. if thisinfo.Agency == info.Agency { //A
  236. cone.Score += 2
  237. } else if strings.Index(info.Agency, thisinfo.Agency) > -1 || strings.Index(thisinfo.Agency, info.Agency) > -1 { //B
  238. cone.Score += 1
  239. } else {
  240. if len(thisinfo.Agency) < 1 { //E
  241. cone.Score -= 1
  242. } else { //C
  243. cone.Score -= 2
  244. }
  245. }
  246. } else { //D不计分
  247. //
  248. }
  249. skey := fmt.Sprintf("%s%s%s", cone.BuyerType, cone.ProjectNameType, cone.ProjectCodeType)
  250. cone.Cresult = skey
  251. if ThreeToTow[skey] {
  252. scores = append(scores, cone)
  253. }
  254. }
  255. }
  256. }
  257. }
  258. }
  259. return
  260. }
  261. //3选1打分
  262. func score3Select1(p PCBV, thisinfo *Info, tmp map[string]interface{}, res []interface{}, pncb []*CompareInfo) (scores []*CompareOne) {
  263. defer qu.Catch()
  264. //对比打分
  265. if len(res) > 0 { //找到项目名称、项目编号或采购单位相同时
  266. for k, tmps := range res { //遍历对象数组
  267. if tmp, ok := tmps.([]interface{}); ok {
  268. for _, b1 := range tmp {
  269. if b1 != nil {
  270. var info ProjectInfo
  271. err := json.Unmarshal(b1.([]byte), &info)
  272. if err != nil {
  273. log.Println(err)
  274. }
  275. cone := &CompareOne{
  276. Parent: pncb[k],
  277. Pinfo: &info,
  278. }
  279. if pncb[k].Field == "pn" { //比较字段项目名称
  280. if len(info.ProjectName) > 0 {
  281. if thisinfo.ProjectName == info.ProjectName { //A
  282. cone.Score += 2
  283. cone.ProjectNameType = "A"
  284. } else if strings.Index(info.ProjectName, thisinfo.ProjectName) > -1 || strings.Index(thisinfo.ProjectName, info.ProjectName) > -1 { //B
  285. cone.Score += 1
  286. cone.ProjectNameType = "B"
  287. } else { //C
  288. cone.Score -= 2
  289. cone.ProjectNameType = "C"
  290. }
  291. } else { //D不计分
  292. cone.ProjectNameType = "D"
  293. }
  294. }
  295. if pncb[k].Field == "pc" { //比较字段项目编号
  296. if len(info.ProjectCode) > 0 {
  297. if thisinfo.ProjectCode == info.ProjectCode { //A
  298. cone.Score += 2
  299. cone.ProjectCodeType = "A"
  300. } else if strings.Index(info.ProjectCode, thisinfo.ProjectCode) > -1 || strings.Index(thisinfo.ProjectCode, info.ProjectCode) > -1 { //B
  301. cone.Score += 1
  302. cone.ProjectCodeType = "B"
  303. } else { //C
  304. cone.Score -= 2
  305. cone.ProjectCodeType = "C"
  306. }
  307. } else { //D不计分
  308. cone.ProjectCodeType = "D"
  309. }
  310. }
  311. if thisinfo.Area != "A" && thisinfo.Area != "全国" && info.Area != "A" && info.Area != "全国" {
  312. if thisinfo.Area == info.Area && thisinfo.City == info.City {
  313. cone.Score += 2
  314. cone.AreaType = "A"
  315. } else {
  316. cone.Score -= 1
  317. cone.AreaType = "C"
  318. }
  319. } else {
  320. cone.Score += 1
  321. cone.AreaType = "B"
  322. }
  323. if len([]rune(info.Agency)) > 0 {
  324. if thisinfo.Agency == info.Agency { //A
  325. cone.Score += 2
  326. cone.AgencyType = "A"
  327. } else if strings.Index(info.Agency, thisinfo.Agency) > -1 || strings.Index(thisinfo.Agency, info.Agency) > -1 { //B
  328. cone.Score += 1
  329. cone.AgencyType = "B"
  330. } else {
  331. if len(thisinfo.Agency) < 1 { //E
  332. cone.Score -= 1
  333. cone.AgencyType = "E"
  334. } else { //C
  335. cone.Score -= 2
  336. cone.AgencyType = "C"
  337. }
  338. }
  339. } else { //D不计分
  340. cone.AgencyType = "D"
  341. }
  342. skey := fmt.Sprintf("%s%s%s", cone.BuyerType, cone.ProjectNameType, cone.ProjectCodeType)
  343. cone.Cresult = skey
  344. if ThreeToOne[skey] {
  345. scores = append(scores, cone)
  346. }
  347. }
  348. }
  349. }
  350. }
  351. }
  352. return
  353. }
  354. //获取对比项目数组
  355. func getComeperProjects(p PCBV, thisinfo *Info) (res []interface{}, pncb []*CompareInfo) {
  356. if p.PnameLen > 0 {
  357. pn := NewCompareInfo("pn", thisinfo.PNKey, PNKey)
  358. pncb = append(pncb, pn)
  359. }
  360. if p.PcodeLen > 0 {
  361. pc := NewCompareInfo("pc", thisinfo.PCKey, PCKey)
  362. pncb = append(pncb, pc)
  363. }
  364. if p.BuyerLen > 0 {
  365. pb := NewCompareInfo("pb", thisinfo.PBKey, PBKey)
  366. pncb = append(pncb, pb)
  367. }
  368. repeatId := map[string]bool{}
  369. IdLock.Lock() //此处加id锁,会引进多线程的死锁,对比三个大map数组,找到key相同的项目id数组,并去重
  370. for _, pv := range pncb {
  371. if pv != nil {
  372. pv.KeyMap.Lock.Lock()
  373. K := pv.KeyMap.Map[pv.Key]
  374. if K == nil {
  375. K = &Key{&[]string{}, &sync.Mutex{}}
  376. pv.KeyMap.Map[pv.Key] = K
  377. }
  378. pv.K = K
  379. pv.K.Lock.Lock()
  380. pv.KeyMap.Lock.Unlock()
  381. defer pv.K.Lock.Unlock()
  382. newarr := []string{}
  383. for _, id := range *K.Arr {
  384. if !repeatId[id] {
  385. newarr = append(newarr, id)
  386. repeatId[id] = true
  387. }
  388. }
  389. pv.IdArr = newarr
  390. }
  391. }
  392. IdLock.Unlock()
  393. for _, pv := range pncb {
  394. if len(pv.IdArr) > 0 {
  395. res = append(res, redis.Mget(REDISIDS, pv.IdArr))
  396. }
  397. }
  398. return
  399. }
  400. //合并项目
  401. func mergeProject(tmp map[string]interface{}, thisinfo *Info, scores []*CompareOne, pncb []*CompareInfo) (sflag string) {
  402. var id = ""
  403. //分值排序
  404. sort.Slice(scores, func(i, j int) bool {
  405. return scores[i].Score > scores[j].Score
  406. })
  407. if len(scores) > 0 {
  408. //分值排序
  409. sort.Slice(scores, func(i, j int) bool {
  410. return scores[i].Score > scores[j].Score
  411. })
  412. max := scores[0]
  413. if max.Score > 0 {
  414. sflag = "repeat"
  415. max.Parent.Bfind = true
  416. tmp["cresult"] = max.Cresult
  417. tmp["score"] = max.Score
  418. id = updateinfo(thisinfo, tmp, max.Pinfo, time.Now().Unix())
  419. //log.Println(sflag, id, max.Cresult)
  420. if len(scores) > 1 && (scores[0].Score == scores[1].Score) {
  421. //通知检查,异常数据存库
  422. checkNotice(thisinfo.Id, id, scores)
  423. }
  424. } else {
  425. sflag = "invalidscore"
  426. }
  427. } else {
  428. //生成项目
  429. sflag = "normal"
  430. mess := map[string]interface{}{
  431. "meger_sflag": sflag,
  432. }
  433. id = newProject(tmp, mess, time.Now().Unix(), thisinfo)
  434. }
  435. if id != "" { //更新REDISKEYS redis
  436. put := []interface{}{}
  437. for _, pv := range pncb {
  438. if pv != nil && !pv.Bfind {
  439. if BinarySearch(*pv.K.Arr, id) == -1 {
  440. *(pv.K.Arr) = append(*(pv.K.Arr), id)
  441. put = append(put, []interface{}{pv.Key, *(pv.K.Arr)})
  442. }
  443. }
  444. }
  445. if len(put) > 0 {
  446. redis.BulkPut(REDISKEYS, 0, put...)
  447. }
  448. }
  449. return sflag
  450. }
  451. //新增项目
  452. func newProject(tmp, mess map[string]interface{}, pipc int64, thisinfo *Info) (id string) {
  453. id = InsertProject(thisinfo.NewPNKey, tmp, mess, pipc, thisinfo)
  454. sflag := qu.ObjToString(mess["meger_sflag"])
  455. if sflag != "alone" {
  456. p1 := NewPinfo(id, thisinfo)
  457. redis.PutCKV(REDISIDS, id, p1)
  458. } else {
  459. du.Debug("新增项目,不参与对比", id)
  460. }
  461. return id
  462. }
  463. //抽取信息打标记
  464. func extInfoTag(sflag, id string) {
  465. MQFW.UpdateById(extractColl, id,
  466. map[string]interface{}{
  467. "$set": map[string]interface{}{
  468. "meger_sflag": sflag,
  469. },
  470. })
  471. }
  472. //检查通知
  473. func checkNotice(infoid, pid string, scores []*CompareOne) {
  474. MQFW.Update("project_unusual", `{"project_id":"`+pid+`"}`,
  475. map[string]interface{}{
  476. "$set": map[string]interface{}{
  477. "project_id": pid,
  478. "pici": time.Now().Unix(),
  479. },
  480. "$push": map[string]interface{}{
  481. "list": map[string]interface{}{
  482. "infoid": infoid,
  483. "pinfo1": scores[0].Pinfo.Id,
  484. "pinfo2": scores[1].Pinfo.Id,
  485. },
  486. },
  487. }, true, false)
  488. du.Debug("通知检查,异常数据存库", infoid, pid)
  489. }
  490. //属性判断组
  491. func PCBVal(tmp map[string]interface{}) PCBV {
  492. pcbv := PCBV{}
  493. projectName := qu.ObjToString(tmp["projectname"])
  494. if len([]rune(projectName)) > 1 {
  495. pcbv.Val += 1
  496. pcbv.Pname = true
  497. pcbv.PnameLen = len([]rune(projectName))
  498. }
  499. projectCode := qu.ObjToString(tmp["projectcode"])
  500. if len([]rune(projectCode)) > 1 {
  501. pcbv.Val += 1
  502. pcbv.Pcode = true
  503. pcbv.PcodeLen = len(projectCode)
  504. }
  505. buyer := qu.ObjToString(tmp["buyer"])
  506. if len([]rune(buyer)) > 1 {
  507. pcbv.Val += 1
  508. pcbv.Buyer = true
  509. pcbv.BuyerLen = len([]rune(buyer))
  510. }
  511. //省市代理机构
  512. if qu.ObjToString(tmp["area"]) != "" {
  513. pcbv.Area = true
  514. }
  515. if qu.ObjToString(tmp["city"]) != "" {
  516. pcbv.City = true
  517. }
  518. if qu.ObjToString(tmp["agency"]) != "" {
  519. pcbv.Agency = true
  520. }
  521. return pcbv
  522. }
  523. //项目名称、项目编号、采购单位打分
  524. func fieldPCBScore(this, info, ctype string, score int) (string, int) {
  525. if len(info) > 0 {
  526. if this == info { //A
  527. score += 5
  528. ctype = "A"
  529. } else if strings.Index(info, this) > -1 || strings.Index(this, info) > -1 { //B
  530. score += 2
  531. ctype = "B"
  532. } else {
  533. if len(this) < 1 { //E
  534. score -= 1
  535. ctype = "E"
  536. } else { //C
  537. score -= 2
  538. ctype = "C"
  539. }
  540. }
  541. } else { //D不计分
  542. ctype = "D"
  543. }
  544. return ctype, score
  545. }
  546. //pncbMap加锁
  547. func lockPNCBMap(thisinfo *Info) {
  548. for { //等待其他任务完成
  549. ok := true
  550. if len(thisinfo.PBKey) > 3 {
  551. if _, b := PBKeyMap.Load(thisinfo.PBKey); b {
  552. ok = false
  553. }
  554. }
  555. if ok && len(thisinfo.PNKey) > 3 {
  556. PNKeyMap.Range(func(k, v interface{}) bool {
  557. if strings.Contains(k.(string), thisinfo.PNKey) || strings.Contains(thisinfo.PNKey, k.(string)) {
  558. ok = false
  559. return false
  560. }
  561. return true
  562. })
  563. // if _, b := PNKeyMap.Load(thisinfo.PNKey); b {
  564. // ok = false
  565. // }
  566. }
  567. if ok && len(thisinfo.PCKey) > 3 {
  568. PCKeyMap.Range(func(k, v interface{}) bool {
  569. if strings.Contains(k.(string), thisinfo.PCKey) || strings.Contains(thisinfo.PCKey, k.(string)) {
  570. ok = false
  571. return false
  572. }
  573. return true
  574. })
  575. // if _, b := PCKeyMap.Load(thisinfo.PCKey); b {
  576. // ok = false
  577. // }
  578. }
  579. if ok {
  580. break
  581. } else {
  582. time.Sleep(10 * time.Millisecond)
  583. }
  584. }
  585. }
  586. //pncbMap解锁
  587. func unlockPNCBMap(thisinfo *Info) {
  588. //if len(thisinfo.PNKey) > 3 {
  589. PNKeyMap.Delete(thisinfo.PNKey)
  590. //}
  591. //if len(thisinfo.PCKey) > 3 {
  592. PCKeyMap.Delete(thisinfo.PCKey)
  593. //}
  594. //if len(thisinfo.PBKey) > 3 {
  595. PBKeyMap.Delete(thisinfo.PBKey)
  596. //}
  597. }