123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615 |
- // projectmeger
- package main
- import (
- "encoding/json"
- "fmt"
- du "jy/util"
- "log"
- qu "qfw/util"
- "qfw/util/redis"
- "sort"
- "strings"
- "sync"
- "time"
- )
- //有效值三选一、三选二
- var ThreeToTow, ThreeToOne map[string]bool
- func init() {
- ThreeToTow = map[string]bool{}
- ThreeToOne = map[string]bool{
- "AAA": true,
- "AAB": true,
- }
- tows := [][][]string{
- [][]string{
- []string{"A", "B"},
- []string{"A", "B"},
- []string{"A", "B"},
- },
- [][]string{
- []string{"A", "B"},
- []string{"D", "E"},
- []string{"A", "B"},
- },
- [][]string{
- []string{"A", "B"},
- []string{"A", "B"},
- []string{"D", "E"},
- },
- [][]string{
- []string{"D", "E"},
- []string{"A", "B"},
- []string{"A", "B"},
- },
- }
- for _, tow := range tows {
- for _, v1 := range tow[0] {
- for _, v2 := range tow[1] {
- for _, v3 := range tow[2] {
- key := fmt.Sprintf("%s%s%s", v1, v2, v3)
- log.Println(key)
- ThreeToTow[key] = true
- }
- }
- }
- }
- }
- func startProjectMerge(thisinfo *Info, tmp map[string]interface{}) {
- bNormalScore := false
- pcbv := PCBVal(tmp)
- if checkInfoAlter(tmp) { //进入变更信息流程
- if pcbv.Val > 1 { //判断三项至少包含两项
- bNormalScore = true
- } else {
- extInfoTag("invalid", qu.BsonIdToSId(tmp["_id"])) //无效信息,打标记
- //go IS.Add("invalid") //数据统计使用
- return
- }
- } else {
- bNormalScore = true
- }
- //合并流程
- if bNormalScore {
- PNKeyMap.Store(thisinfo.PNKey, true)
- PBKeyMap.Store(thisinfo.PBKey, true)
- PCKeyMap.Store(thisinfo.PCKey, true)
- if pcbv.Buyer { //有采购单位
- hasBuyer(pcbv, thisinfo, tmp)
- } else { //无采购单位
- noBuyer(pcbv, thisinfo, tmp)
- }
- }
- }
- //判断信息是否是变更
- func checkInfoAlter(tmp map[string]interface{} /*新信息*/) bool {
- toptype := qu.ObjToString(tmp["toptype"])
- subtype := qu.ObjToString(tmp["subtype"])
- title := qu.ObjToString(tmp["title"])
- if subtype == "变更" || strings.Index(title, "变更公告") > -1 || strings.Index(title, "更正公告") > -1 {
- //当信息类型是变更或标题中含变更时
- if toptype == "招标" {
- //招标的变更公告,不作处理
- } else if toptype == "结果" {
- subtype = "变更"
- }
- }
- return subtype == "变更"
- }
- //有采购单位
- func hasBuyer(p PCBV, thisinfo *Info, tmp map[string]interface{}) {
- var pncb []*CompareInfo
- var res []interface{}
- sflag := ""
- pici := time.Now().Unix()
- if p.Pname || p.Pcode { //有项目名称或项目编号
- //获取对比项目数组
- res, pncb = getComeperProjects(p, thisinfo)
- //三选二打分
- scores := score3Select2(p, thisinfo, tmp, res, pncb)
- //项目合并
- sflag = mergeProject(tmp, thisinfo, scores, pncb)
- } else {
- //生成项目,不参与后续对比
- sflag = "alone"
- mess := map[string]interface{}{
- "meger_mess": "有采购单位,不满足三选二",
- "meger_sflag": sflag,
- }
- newProject(tmp, mess, pici, thisinfo)
- }
- extInfoTag(sflag, thisinfo.Id)
- //go IS.Add(sflag) //数据统计使用
- }
- //无采购单位
- func noBuyer(p PCBV, thisinfo *Info, tmp map[string]interface{}) {
- var pncb []*CompareInfo
- var res []interface{}
- sflag := ""
- pici := time.Now().Unix()
- if p.Pname { //有项目名称
- //获取对比项目数组
- res, pncb = getComeperProjects(p, thisinfo)
- if p.Pcode { //有项目编号
- //三选二打分
- scores := score3Select2(p, thisinfo, tmp, res, pncb)
- //项目合并
- sflag = mergeProject(tmp, thisinfo, scores, pncb)
- } else { //无项目编号
- if p.PnameLen > MegerFieldsLen.ProjectNamelen {
- //三选一打分
- scores := score3Select1(p, thisinfo, tmp, res, pncb)
- //项目合并
- sflag = mergeProject(tmp, thisinfo, scores, pncb)
- } else {
- //生成项目,不参与后续对比
- sflag = "alone"
- mess := map[string]interface{}{
- "meger_mess": "无采购单位,不满足三选一",
- "meger_sflag": sflag,
- }
- newProject(tmp, mess, pici, thisinfo)
- }
- }
- } else { //无项目名称
- if p.Pcode {
- //获取对比项目数组
- res, pncb = getComeperProjects(p, thisinfo)
- if p.PcodeLen > MegerFieldsLen.ProjectCodelen {
- if p.Area && p.City && p.Agency { //有省市代理机构
- //三选一打分
- scores := score3Select1(p, thisinfo, tmp, res, pncb)
- //项目合并
- sflag = mergeProject(tmp, thisinfo, scores, pncb)
- } else {
- //生成项目,不参与后续对比
- sflag = "alone"
- mess := map[string]interface{}{
- "meger_mess": "无采购单位,不满足三选一",
- "meger_sflag": sflag,
- }
- newProject(tmp, mess, pici, thisinfo)
- }
- } else {
- //生成项目,不参与后续对比
- sflag = "alone"
- mess := map[string]interface{}{
- "meger_mess": "无采购单位,不满足三选一",
- "meger_sflag": sflag,
- }
- newProject(tmp, mess, pici, thisinfo)
- }
- } else {
- //信息无效,打标记
- sflag = "invalid"
- }
- }
- extInfoTag(sflag, thisinfo.Id)
- //go IS.Add(sflag) //数据统计使用
- }
- //3选2打分
- func score3Select2(p PCBV, thisinfo *Info, tmp map[string]interface{}, res []interface{}, pncb []*CompareInfo) (scores []*CompareOne) {
- defer qu.Catch()
- if len(res) > 0 {
- //对比打分
- for k, tmps := range res { //遍历对象数组
- if tmp, ok := tmps.([]interface{}); ok {
- for _, b1 := range tmp {
- if b1 != nil {
- var info ProjectInfo
- err := json.Unmarshal(b1.([]byte), &info)
- if err != nil {
- log.Println(err)
- }
- cone := &CompareOne{
- Parent: pncb[k],
- Pinfo: &info,
- }
- cone.BuyerType, cone.Score = fieldPCBScore(thisinfo.Buyer, info.Buyer, cone.BuyerType, cone.Score)
- if p.Buyer {
- cone.ProjectNameType, cone.Score = fieldPCBScore(thisinfo.ProjectName, info.ProjectName, cone.ProjectNameType, cone.Score)
- cone.ProjectCodeType, cone.Score = fieldPCBScore(thisinfo.ProjectCode, info.ProjectCode, cone.ProjectCodeType, cone.Score)
- } else { //无采购单位,打分考虑长度
- if len([]rune(thisinfo.ProjectName)) > MegerFieldsLen.ProjectNamelen {
- cone.ProjectNameType, cone.Score = fieldPCBScore(thisinfo.ProjectName, info.ProjectName, cone.ProjectNameType, cone.Score)
- } else {
- cone.ProjectNameType = "D"
- }
- if len(thisinfo.ProjectCode) > MegerFieldsLen.ProjectCodelen {
- cone.ProjectCodeType, cone.Score = fieldPCBScore(thisinfo.ProjectCode, info.ProjectCode, cone.ProjectCodeType, cone.Score)
- } else {
- cone.ProjectCodeType = "D"
- }
- }
- //省市打分
- if thisinfo.Area != "A" && thisinfo.Area != "全国" && info.Area != "A" && info.Area != "全国" {
- if thisinfo.Area == info.Area && thisinfo.City == info.City {
- cone.Score += 2
- } else {
- cone.Score -= 1
- }
- } else {
- cone.Score += 1
- }
- //代理机构打分
- if len([]rune(info.Agency)) > 0 {
- if thisinfo.Agency == info.Agency { //A
- cone.Score += 2
- } else if strings.Index(info.Agency, thisinfo.Agency) > -1 || strings.Index(thisinfo.Agency, info.Agency) > -1 { //B
- cone.Score += 1
- } else {
- if len(thisinfo.Agency) < 1 { //E
- cone.Score -= 1
- } else { //C
- cone.Score -= 2
- }
- }
- } else { //D不计分
- //
- }
- skey := fmt.Sprintf("%s%s%s", cone.BuyerType, cone.ProjectNameType, cone.ProjectCodeType)
- cone.Cresult = skey
- if ThreeToTow[skey] {
- scores = append(scores, cone)
- }
- }
- }
- }
- }
- }
- return
- }
- //3选1打分
- func score3Select1(p PCBV, thisinfo *Info, tmp map[string]interface{}, res []interface{}, pncb []*CompareInfo) (scores []*CompareOne) {
- defer qu.Catch()
- //对比打分
- if len(res) > 0 { //找到项目名称、项目编号或采购单位相同时
- for k, tmps := range res { //遍历对象数组
- if tmp, ok := tmps.([]interface{}); ok {
- for _, b1 := range tmp {
- if b1 != nil {
- var info ProjectInfo
- err := json.Unmarshal(b1.([]byte), &info)
- if err != nil {
- log.Println(err)
- }
- cone := &CompareOne{
- Parent: pncb[k],
- Pinfo: &info,
- }
- if pncb[k].Field == "pn" { //比较字段项目名称
- if len(info.ProjectName) > 0 {
- if thisinfo.ProjectName == info.ProjectName { //A
- cone.Score += 2
- cone.ProjectNameType = "A"
- } else if strings.Index(info.ProjectName, thisinfo.ProjectName) > -1 || strings.Index(thisinfo.ProjectName, info.ProjectName) > -1 { //B
- cone.Score += 1
- cone.ProjectNameType = "B"
- } else { //C
- cone.Score -= 2
- cone.ProjectNameType = "C"
- }
- } else { //D不计分
- cone.ProjectNameType = "D"
- }
- }
- if pncb[k].Field == "pc" { //比较字段项目编号
- if len(info.ProjectCode) > 0 {
- if thisinfo.ProjectCode == info.ProjectCode { //A
- cone.Score += 2
- cone.ProjectCodeType = "A"
- } else if strings.Index(info.ProjectCode, thisinfo.ProjectCode) > -1 || strings.Index(thisinfo.ProjectCode, info.ProjectCode) > -1 { //B
- cone.Score += 1
- cone.ProjectCodeType = "B"
- } else { //C
- cone.Score -= 2
- cone.ProjectCodeType = "C"
- }
- } else { //D不计分
- cone.ProjectCodeType = "D"
- }
- }
- if thisinfo.Area != "A" && thisinfo.Area != "全国" && info.Area != "A" && info.Area != "全国" {
- if thisinfo.Area == info.Area && thisinfo.City == info.City {
- cone.Score += 2
- cone.AreaType = "A"
- } else {
- cone.Score -= 1
- cone.AreaType = "C"
- }
- } else {
- cone.Score += 1
- cone.AreaType = "B"
- }
- if len([]rune(info.Agency)) > 0 {
- if thisinfo.Agency == info.Agency { //A
- cone.Score += 2
- cone.AgencyType = "A"
- } else if strings.Index(info.Agency, thisinfo.Agency) > -1 || strings.Index(thisinfo.Agency, info.Agency) > -1 { //B
- cone.Score += 1
- cone.AgencyType = "B"
- } else {
- if len(thisinfo.Agency) < 1 { //E
- cone.Score -= 1
- cone.AgencyType = "E"
- } else { //C
- cone.Score -= 2
- cone.AgencyType = "C"
- }
- }
- } else { //D不计分
- cone.AgencyType = "D"
- }
- skey := fmt.Sprintf("%s%s%s", cone.BuyerType, cone.ProjectNameType, cone.ProjectCodeType)
- cone.Cresult = skey
- if ThreeToOne[skey] {
- scores = append(scores, cone)
- }
- }
- }
- }
- }
- }
- return
- }
- //获取对比项目数组
- func getComeperProjects(p PCBV, thisinfo *Info) (res []interface{}, pncb []*CompareInfo) {
- if p.PnameLen > 0 {
- pn := NewCompareInfo("pn", thisinfo.PNKey, PNKey)
- pncb = append(pncb, pn)
- }
- if p.PcodeLen > 0 {
- pc := NewCompareInfo("pc", thisinfo.PCKey, PCKey)
- pncb = append(pncb, pc)
- }
- if p.BuyerLen > 0 {
- pb := NewCompareInfo("pb", thisinfo.PBKey, PBKey)
- pncb = append(pncb, pb)
- }
- repeatId := map[string]bool{}
- IdLock.Lock() //此处加id锁,会引进多线程的死锁,对比三个大map数组,找到key相同的项目id数组,并去重
- for _, pv := range pncb {
- if pv != nil {
- pv.KeyMap.Lock.Lock()
- K := pv.KeyMap.Map[pv.Key]
- if K == nil {
- K = &Key{&[]string{}, &sync.Mutex{}}
- pv.KeyMap.Map[pv.Key] = K
- }
- pv.K = K
- pv.K.Lock.Lock()
- pv.KeyMap.Lock.Unlock()
- defer pv.K.Lock.Unlock()
- newarr := []string{}
- for _, id := range *K.Arr {
- if !repeatId[id] {
- newarr = append(newarr, id)
- repeatId[id] = true
- }
- }
- pv.IdArr = newarr
- }
- }
- IdLock.Unlock()
- for _, pv := range pncb {
- if len(pv.IdArr) > 0 {
- res = append(res, redis.Mget(REDISIDS, pv.IdArr))
- }
- }
- return
- }
- //合并项目
- func mergeProject(tmp map[string]interface{}, thisinfo *Info, scores []*CompareOne, pncb []*CompareInfo) (sflag string) {
- var id = ""
- //分值排序
- sort.Slice(scores, func(i, j int) bool {
- return scores[i].Score > scores[j].Score
- })
- if len(scores) > 0 {
- //分值排序
- sort.Slice(scores, func(i, j int) bool {
- return scores[i].Score > scores[j].Score
- })
- max := scores[0]
- if max.Score > 0 {
- sflag = "repeat"
- max.Parent.Bfind = true
- tmp["cresult"] = max.Cresult
- tmp["score"] = max.Score
- id = updateinfo(thisinfo, tmp, max.Pinfo, time.Now().Unix())
- //log.Println(sflag, id, max.Cresult)
- if len(scores) > 1 && (scores[0].Score == scores[1].Score) {
- //通知检查,异常数据存库
- checkNotice(thisinfo.Id, id, scores)
- }
- } else {
- sflag = "invalidscore"
- }
- } else {
- //生成项目
- sflag = "normal"
- mess := map[string]interface{}{
- "meger_sflag": sflag,
- }
- id = newProject(tmp, mess, time.Now().Unix(), thisinfo)
- }
- if id != "" { //更新REDISKEYS redis
- put := []interface{}{}
- for _, pv := range pncb {
- if pv != nil && !pv.Bfind {
- if BinarySearch(*pv.K.Arr, id) == -1 {
- *(pv.K.Arr) = append(*(pv.K.Arr), id)
- put = append(put, []interface{}{pv.Key, *(pv.K.Arr)})
- }
- }
- }
- if len(put) > 0 {
- redis.BulkPut(REDISKEYS, 0, put...)
- }
- }
- return sflag
- }
- //新增项目
- func newProject(tmp, mess map[string]interface{}, pipc int64, thisinfo *Info) (id string) {
- id = InsertProject(thisinfo.NewPNKey, tmp, mess, pipc, thisinfo)
- sflag := qu.ObjToString(mess["meger_sflag"])
- if sflag != "alone" {
- p1 := NewPinfo(id, thisinfo)
- redis.PutCKV(REDISIDS, id, p1)
- } else {
- du.Debug("新增项目,不参与对比", id)
- }
- return id
- }
- //抽取信息打标记
- func extInfoTag(sflag, id string) {
- MQFW.UpdateById(extractColl, id,
- map[string]interface{}{
- "$set": map[string]interface{}{
- "meger_sflag": sflag,
- },
- })
- }
- //检查通知
- func checkNotice(infoid, pid string, scores []*CompareOne) {
- MQFW.Update("project_unusual", `{"project_id":"`+pid+`"}`,
- map[string]interface{}{
- "$set": map[string]interface{}{
- "project_id": pid,
- "pici": time.Now().Unix(),
- },
- "$push": map[string]interface{}{
- "list": map[string]interface{}{
- "infoid": infoid,
- "pinfo1": scores[0].Pinfo.Id,
- "pinfo2": scores[1].Pinfo.Id,
- },
- },
- }, true, false)
- du.Debug("通知检查,异常数据存库", infoid, pid)
- }
- //属性判断组
- func PCBVal(tmp map[string]interface{}) PCBV {
- pcbv := PCBV{}
- projectName := qu.ObjToString(tmp["projectname"])
- if len([]rune(projectName)) > 1 {
- pcbv.Val += 1
- pcbv.Pname = true
- pcbv.PnameLen = len([]rune(projectName))
- }
- projectCode := qu.ObjToString(tmp["projectcode"])
- if len([]rune(projectCode)) > 1 {
- pcbv.Val += 1
- pcbv.Pcode = true
- pcbv.PcodeLen = len(projectCode)
- }
- buyer := qu.ObjToString(tmp["buyer"])
- if len([]rune(buyer)) > 1 {
- pcbv.Val += 1
- pcbv.Buyer = true
- pcbv.BuyerLen = len([]rune(buyer))
- }
- //省市代理机构
- if qu.ObjToString(tmp["area"]) != "" {
- pcbv.Area = true
- }
- if qu.ObjToString(tmp["city"]) != "" {
- pcbv.City = true
- }
- if qu.ObjToString(tmp["agency"]) != "" {
- pcbv.Agency = true
- }
- return pcbv
- }
- //项目名称、项目编号、采购单位打分
- func fieldPCBScore(this, info, ctype string, score int) (string, int) {
- if len(info) > 0 {
- if this == info { //A
- score += 5
- ctype = "A"
- } else if strings.Index(info, this) > -1 || strings.Index(this, info) > -1 { //B
- score += 2
- ctype = "B"
- } else {
- if len(this) < 1 { //E
- score -= 1
- ctype = "E"
- } else { //C
- score -= 2
- ctype = "C"
- }
- }
- } else { //D不计分
- ctype = "D"
- }
- return ctype, score
- }
- //pncbMap加锁
- func lockPNCBMap(thisinfo *Info) {
- for { //等待其他任务完成
- ok := true
- if len(thisinfo.PBKey) > 3 {
- if _, b := PBKeyMap.Load(thisinfo.PBKey); b {
- ok = false
- }
- }
- if ok && len(thisinfo.PNKey) > 3 {
- PNKeyMap.Range(func(k, v interface{}) bool {
- if strings.Contains(k.(string), thisinfo.PNKey) || strings.Contains(thisinfo.PNKey, k.(string)) {
- ok = false
- return false
- }
- return true
- })
- // if _, b := PNKeyMap.Load(thisinfo.PNKey); b {
- // ok = false
- // }
- }
- if ok && len(thisinfo.PCKey) > 3 {
- PCKeyMap.Range(func(k, v interface{}) bool {
- if strings.Contains(k.(string), thisinfo.PCKey) || strings.Contains(thisinfo.PCKey, k.(string)) {
- ok = false
- return false
- }
- return true
- })
- // if _, b := PCKeyMap.Load(thisinfo.PCKey); b {
- // ok = false
- // }
- }
- if ok {
- break
- } else {
- time.Sleep(10 * time.Millisecond)
- }
- }
- }
- //pncbMap解锁
- func unlockPNCBMap(thisinfo *Info) {
- //if len(thisinfo.PNKey) > 3 {
- PNKeyMap.Delete(thisinfo.PNKey)
- //}
- //if len(thisinfo.PCKey) > 3 {
- PCKeyMap.Delete(thisinfo.PCKey)
- //}
- //if len(thisinfo.PBKey) > 3 {
- PBKeyMap.Delete(thisinfo.PBKey)
- //}
- }
|