projectmeger.go 16 KB

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