projectmeger.go 16 KB

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