Răsfoiți Sursa

用户行业标签;添加产权分类

wcc 2 ani în urmă
părinte
comite
4d8518f3a7
5 a modificat fișierele cu 851 adăugiri și 21 ștergeri
  1. 14 10
      src/config.json
  2. 171 4
      src/task/task.go
  3. 265 7
      src/task/updatetask.go
  4. 162 0
      src/util/charge_rule.go
  5. 239 0
      src/util/util_test.go

+ 14 - 10
src/config.json

@@ -1,18 +1,22 @@
 {
-    "mgoaddr": "192.168.3.207:27092",
+    "mgoaddr": "127.0.0.1:27017",
     "mgosize": 10,
-    "dbname": "classfication",
+    "dbname": "wcc",
     "isbidding": false,
     "dbname_dis": "classfication",
     "dbinfo": {
     	"bidding":{
 			"username": "dataAnyWrite",
    			"password": "data@dataAnyWrite"
-		}
-    }, 
+		},
+        "userkey":{
+            "username": "jianyu",
+            "password": "jylog2020_123"
+        }
+    },
     "jkmail": {
-        "to": "maxiaoshan@topnet.net.cn,zhangjinkun@topnet.net.cn",
-        "api": "http://10.171.112.160:19281/_send/_mail"
+        "to": "wangchengcheng@topnet.net.cn,zhangjinkun@topnet.net.cn",
+        "api": "http://172.17.145.179:19281/_send/_mail"
     },
     "udpport": "1781",
     "noautorun": 1,
@@ -36,7 +40,7 @@
     	"name": "敏感词分类",
         "taskid": "5f2770fadc1831746ac847ba",
         "nextNode": [
-		    {	
+		    {
 				"addr": "127.0.0.1",
 				"port": 1781,
 				"stype": "yezhu",
@@ -48,7 +52,7 @@
         "name": "kvtext招标",
         "taskid": "5e8fd751bb030d5bc5d0140e",
         "nextNode": [
-		    {	
+		    {
 				"addr": "127.0.0.1",
 				"port": 1781,
 				"stype": "hangye",
@@ -84,7 +88,7 @@
         "name":"业主",
         "taskid":"58be14cbedbcdc5e57727311",
         "nextNode": [
-		    {	
+		    {
 				"addr": "127.0.0.1",
 				"port": 1785,
 				"stype": "",
@@ -100,7 +104,7 @@
         "coll": "bidding",
         "dbtype": "bidding",
         "nextNode": [
-		    {	
+		    {
 				"addr": "127.0.0.1",
 				"port": 1781,
 				"stype": "yezhu",

+ 171 - 4
src/task/task.go

@@ -458,6 +458,8 @@ func (tt *TTask) RRunTest(s_startid, s_endid, s_query, filename string) {
 		res[1] = util.ObjToString(tmp["title"])
 		if util.IntAll(tmp["infoformat"]) == 2 { //此处增加特例
 			res = append(res, "拟建", "拟建")
+		} else if util.IntAll(tmp["infoformat"]) == 3 {
+			res = append(res, "产权", "产权")
 		} else {
 			SMap := NewClassificationRun(tt, tmp)
 			for _, k := range SMap.Keys {
@@ -529,7 +531,135 @@ OVER:
 }
 
 func newtaskrun(tt *TTask) {
-	NewTaskRunAll(tt, false, nil)
+	//针对用户行业标签,需要单独处理
+	if tt.S_name == "用户行业分类" {
+		log.Println("执行任务:->", tt.S_name)
+		DealUserKey(tt)
+	} else {
+		NewTaskRunAll(tt, false, nil)
+	}
+
+}
+
+//DealUserKey 用户行业标签分类前预处理
+func DealUserKey(tt *TTask) {
+	//最终更新的数据
+	var updateUserPool [][]map[string]interface{}
+	//开始识别
+	pool := make(chan bool, tt.I_thread)
+	wg := &sync.WaitGroup{}
+	lock := &sync.Mutex{}
+	q := make(map[string]interface{})
+
+	var lastID string
+	//1.获取查询条件
+	comeintime := time.Now().Unix() - 5*60
+	query := map[string]interface{}{
+		"l_registedate": map[string]interface{}{
+			"$lt": comeintime,
+		},
+	}
+	qId := tt.MgoTask.GetMgoConn()
+	defer tt.MgoTask.DestoryMongoConn(qId)
+	tmpData := qId.DB(tt.S_mgodb).C(tt.S_coll).Find(&query).Limit(1).Sort("-_id").Iter()
+	eId := ""
+	for tmp := make(map[string]interface{}); tmpData.Next(tmp); {
+		eId = u.BsonIdToSId(tmp["_id"])
+	}
+
+	if tt.LastId != "" {
+		sid := tt.LastId
+		if eId <= sid || eId == "" {
+			return
+		}
+		q["_id"] = map[string]interface{}{
+			"$gt":  u.StringTOBsonId(sid),
+			"$lte": u.StringTOBsonId(eId),
+		}
+	} else {
+		q["_id"] = map[string]interface{}{
+			"$lte": u.StringTOBsonId(eId),
+		}
+	}
+	//2.条件封装完毕,开始查询数据
+	sess := tt.MgoTask.GetMgoConn()
+	defer tt.MgoTask.DestoryMongoConn(sess)
+
+	log.Println(tt.S_name, " 查询条件:=>", q)
+	extractquery := sess.DB(tt.S_mgodb).C(tt.S_coll).Find(q).Select(nil).Sort("_id").Iter()
+
+	sum := 0
+	realCount := 0
+	for tmp := make(map[string]interface{}); extractquery.Next(&tmp); sum++ {
+		lastID = u.BsonIdToSId(tmp["_id"])
+
+		pool <- true
+		wg.Add(1)
+
+		go func(tmp map[string]interface{}) {
+			defer func() {
+				<-pool
+				wg.Done()
+			}()
+
+			keys := u.GetUserKeys(tmp)
+			tags := []string{}
+			for _, v := range keys {
+				tag := util.ObjToString(v)
+				tags = append(tags, tag)
+			}
+			//按顺序识别
+			update := map[string]interface{}{}
+			if len(keys) > 0 {
+				//用户关键词
+				update["key_list"] = strings.Join(tags, ",")
+			}
+
+			lock.Lock()
+			tmp["key_list"] = strings.Join(tags, ",")
+			SMap := NewClassificationRun(tt, tmp)
+			//fmt.Println("SMap=>", SMap)
+			subtype := SMap.Map["subscope_dy"]
+
+			if subtype != nil {
+				if subs, ok := subtype.([]string); ok {
+					update["subscope_dy"] = strings.Join(subs, ",")
+					updatePool := []map[string]interface{}{
+						{"_id": tmp["_id"]},
+						{"$set": update},
+					}
+					updateUserPool = append(updateUserPool, updatePool)
+					realCount++
+				}
+			}
+
+			if len(updateUserPool) > 10 {
+				tt.MgoTask.UpdateBulk(tt.S_collection, updateUserPool...)
+				updateUserPool = [][]map[string]interface{}{}
+				//log.Println("current:", tt.S_name, "number:", sum)
+			}
+			lock.Unlock()
+
+		}(tmp)
+
+		tmp = make(map[string]interface{})
+	}
+
+	wg.Wait()
+
+	if lastID > tt.LastId {
+		tt.LastId = lastID
+		setid := map[string]interface{}{
+			"$set": map[string]interface{}{
+				"s_startid":   tt.LastId,
+				"s_starttime": time.Now().Unix(),
+			},
+		}
+		go tools.MgoClass.Update("rc_task", `{"_id":"`+tt.ID+`"}`, setid, false, false)
+
+	}
+	log.Println("运行", tt.S_name, "over", sum)
+
 }
 
 //NewTaskRunAll 常规任务和udp非合并数据处理方法
@@ -652,7 +782,7 @@ func NewTaskRunAll(tt *TTask, budp bool, mapInfo map[string]interface{}) int {
 					}
 				}
 			}
-		} else { //udp查询条件
+		} else {                  //udp查询条件
 			if tt.S_query != "" { //有查询条件
 				json.Unmarshal([]byte(strings.Replace(tt.S_query, "'", "\"", -1)), &q)
 			}
@@ -727,6 +857,9 @@ func NewTaskRunAll(tt *TTask, budp bool, mapInfo map[string]interface{}) int {
 				if util.IntAll(tmp["infoformat"]) == 2 { //此处增加特例
 					res["toptype"] = "拟建"
 					res["subtype"] = "拟建"
+				} else if util.IntAll(tmp["infoformat"]) == 3 {
+					res["toptype"] = "产权"
+					res["subtype"] = "产权"
 				} else {
 					SMap := &tools.SortMap{}
 					if tt.I_tasktype == 2 { //标签任务
@@ -736,9 +869,20 @@ func NewTaskRunAll(tt *TTask, budp bool, mapInfo map[string]interface{}) int {
 						//res["projectinfo"] = tmp["projectinfo"]
 					} else { //常规任务
 						SMap = NewClassificationRun(tt, tmp)
+						//一级分类时,符合结果中成交规则时
+						if SMap.Map["toptype"] == "招标" && SMap.Map["subtype"] != "单一" {
+							if u.ChargeDetailResult(tmp["detail"].(string)) {
+								SMap.Map["toptype"] = "结果"
+								resa := ReSub(tt, tmp, "结果")
+								subtype := resa.Map["subtype"]
+								delete(SMap.Map, "subtype")
+								SMap.Map["subtype"] = subtype
+							}
+						}
 					}
-					//追加时处理
-					if tt.I_fieldUpdate == 1 && tt.I_multiclass == 1 { //更新字段 I_fieldUpdate  0:覆盖 1:追加
+
+					//追加时处理,//更新字段 I_fieldUpdate  0:覆盖 1:追加
+					if tt.I_fieldUpdate == 1 && tt.I_multiclass == 1 {
 						//封装追加信息
 						if len(SMap.Keys) > 0 {
 							for _, k := range SMap.Keys {
@@ -1050,6 +1194,9 @@ func UdpTaskRunAll(tt *TTask, budp bool, mapInfo map[string]interface{}, stype s
 					if util.IntAll(result["infoformat"]) == 2 { //此处增加特例
 						res["toptype"] = "拟建"
 						res["subtype"] = "拟建"
+					} else if util.IntAll(result["infoformat"]) == 3 {
+						res["toptype"] = "产权"
+						res["subtype"] = "产权"
 					} else {
 						SMap := &tools.SortMap{}
 						if tt.I_tasktype == 2 { //标签任务
@@ -1059,6 +1206,16 @@ func UdpTaskRunAll(tt *TTask, budp bool, mapInfo map[string]interface{}, stype s
 							//res["projectinfo"] = tmp["projectinfo"]
 						} else { //常规任务
 							SMap = NewClassificationRun(tt, result)
+							//一级分类时,符合结果中成交规则时
+							if SMap.Map["toptype"] == "招标" && SMap.Map["subtype"] != "单一" {
+								if u.ChargeDetailResult(tmp["detail"].(string)) {
+									SMap.Map["toptype"] = "结果"
+									resa := ReSub(tt, tmp, "结果")
+									subtype := resa.Map["subtype"]
+									delete(SMap.Map, "subtype")
+									SMap.Map["subtype"] = subtype
+								}
+							}
 						}
 
 						if tt.I_fieldUpdate == 1 && tt.I_multiclass == 1 { //更新字段 I_fieldUpdate  0:覆盖 1:追加
@@ -1517,6 +1674,16 @@ func StartTask(t *TTask) {
 				SMap := &tools.SortMap{}
 				SMap = NewClassificationRun(t, tmp)
 				if len(SMap.Map) > 0 {
+					//一级分类时,符合结果中成交规则时
+					if SMap.Map["toptype"] == "招标" && SMap.Map["subtype"] != "单一" {
+						if u.ChargeDetailResult(tmp["detail"].(string)) {
+							SMap.Map["toptype"] = "结果"
+							resa := ReSub(t, tmp, "结果")
+							subtype := resa.Map["subtype"]
+							delete(SMap.Map, "subtype")
+							SMap.Map["subtype"] = subtype
+						}
+					}
 					update = append(update, map[string]interface{}{"$set": SMap.Map})
 				}
 				//更新

+ 265 - 7
src/task/updatetask.go

@@ -261,6 +261,7 @@ func NewClassificationRun(tt *TTask, tmp map[string]interface{}) *tools.SortMap
 			// if tt.I_multiclass == 0 {  //单分类临时记录保存字段
 			// 	tmpSavefield[savefield] = ""
 			// }
+			//同一个字段,例如toptype有值了就跳过
 			if savefield != "" && tt.I_multiclass == 0 && SMap.Map[savefield] != nil {
 				//同一个字段,只要一个类识别...
 				continue
@@ -281,15 +282,18 @@ func NewClassificationRun(tt *TTask, tmp map[string]interface{}) *tools.SortMap
 					val = PreFilter(val, tt.Task_PreRule) //任务的前置过滤
 					rulval[f] = val                       //整个任务仅过滤一次,将其存储
 				}
+
 				if val != "" {
-					val = PreFilter(val, c.Class_PreRule) //分类的前置过滤
+					//1.分类的前置过滤,不符合直接跳过当前循环
+					val = PreFilter(val, c.Class_PreRule)
 					if val == "" {
 						continue
 					}
+					//2.循环当前类规则
 					for _, r := range rule {
-
-						if s_pid != "" { //只要此类的父类不为空
-							ru_s_pid := r.S_pid                      //当前规则的父规则id
+						//只要此类的父类不为空
+						if s_pid != "" {
+							ru_s_pid := r.S_pid                      //当前规则的父规则id,57a02d4fd368081fac39eaf2,57a18dd5d368081d70e185d0,5a4d99b7e138236b380378be
 							class_s_pid := strings.Split(s_pid, ",") //当前规则所属类的父类集合
 							bcontinue := true
 						L1:
@@ -316,12 +320,13 @@ func NewClassificationRun(tt *TTask, tmp map[string]interface{}) *tools.SortMap
 							continue
 						}
 						//开始识别
-						i_rule := r.Reg
+						i_rule := r.Reg //toptype=结果 的所有规则
 						//util.Debug("text---", ruval, "rule---", i_rule)
 						b, rulearr := DFAAnalyRules(ruval, i_rule)
 						//util.Debug("===============", b, f, rulearr, r.S_name)
 						if b {
-							if r.S_name == "中选" && len(r.DetailReg) > 0 { //title、channel二级分类中标处理
+							//title、channel二级分类中标处理
+							if r.S_name == "中选" && len(r.DetailReg) > 0 {
 								detail := util.ObjToString(tmp["detail"])
 								if len(r.NotReg) > 0 { //排除规则
 									dnrb, _ := DFAAnalyRules(detail, r.NotReg)
@@ -371,8 +376,10 @@ func NewClassificationRun(tt *TTask, tmp map[string]interface{}) *tools.SortMap
 							} else {
 								fflag[cid] = []string{r_id} //第一个规则中的_id
 							}
+							//57a02bc0d368081fac39eaef =[57a02cdcd368081fac39eaf1]
 							s_name := r.S_name
-							if tt.I_wordcount == 1 && len(rulearr) > 0 { //词频统计
+							//词频统计
+							if tt.I_wordcount == 1 && len(rulearr) > 0 {
 								tt.WcLock.Lock()
 								map1 := tt.WordCount[s_name]
 								if map1 == nil {
@@ -461,6 +468,244 @@ func NewClassificationRun(tt *TTask, tmp map[string]interface{}) *tools.SortMap
 	return SMap
 }
 
+func ReSub(tt *TTask, tmp map[string]interface{}, top string) *tools.SortMap {
+	SMap := tools.NewSortMap()
+	//tmpSavefield := map[string]interface{}{}
+	//fflag := map[string][]string{} //标志父类从属关系
+	rulval := map[string]string{} //存储过滤记录
+	class := tt.Class             //获取任务中的多个分类
+
+	var top_class_ids []string //一级分类的规则ID
+	if len(class) > 0 {
+		for _, cla := range class {
+			rules := cla.Rule
+			for _, rule := range rules {
+				if rule.S_name == top {
+					top_class_ids = append(top_class_ids, rule.Rid)
+				}
+			}
+
+		}
+	}
+
+	if class != nil && len(class) > 0 {
+		for _, c := range class {
+			//预处理lua
+			s_fields := c.S_fields                              //识别字段  o_jy.a_key.key  detail,title
+			for _, ftmp := range strings.Split(s_fields, ",") { //先将所有o_jy.a_key.key类型字段的值处理到tmp中
+				fieldarr := strings.Split(ftmp, ".")
+				if len(fieldarr) > 1 {
+					key := fieldarr[0] //字段名称
+					field := fieldarr[len(fieldarr)-1]
+					if tmp[key] == nil { //判断是否有该字段的值
+						continue
+					}
+					rMap := map[string]interface{}{}
+					if tMap, ok := tmp[key].(map[string]interface{}); ok {
+						rMap = tMap
+					} else if sMap, ok := tmp[key].(string); ok {
+						json.Unmarshal([]byte(sMap), &rMap)
+						logger.Warn("Projectinfo Type Is Wrong:", tmp["_id"])
+					}
+					if len(rMap) > 0 {
+						if s != nil && s[ftmp] != nil {
+							newscript := NewLuaScript(s[ftmp].Name, s[ftmp].File) //newlua
+							datamap := Dealdata(newscript, rMap, field)           //处理数据
+							dataarr := maptoarr(datamap)
+							if len(dataarr) == 0 {
+								continue
+							}
+							tmp[ftmp] = dataarr
+						}
+					}
+				}
+			}
+			//cid := c.Cid     //类id
+			s_pid := c.S_pid //父类id
+
+			if s_pid == "" {
+				continue
+			}
+			savefield := c.S_savefield //保存字段,toptype
+			// if tt.I_multiclass == 0 {  //单分类临时记录保存字段
+			// 	tmpSavefield[savefield] = ""
+			// }
+			//同一个字段,例如toptype有值了就跳过
+			if savefield != "" && tt.I_multiclass == 0 && SMap.Map[savefield] != nil {
+				//同一个字段,只要一个类识别...
+				continue
+			}
+			rule := c.Rule //取规则
+			fields := strings.Split(s_fields, ",")
+		L:
+			for _, f := range fields { //f:每一个识别字段
+				val := rulval[f]
+				if val == "" { //无过滤记录
+					strArr, ok := tmp[f].([]string)
+					if ok {
+						//strArr := tools.ObjArrToStringArr(strArr)
+						val = strings.Join(strArr, "")
+					} else {
+						val = util.ObjToString(tmp[f]) //取识别内容
+					}
+					val = PreFilter(val, tt.Task_PreRule) //任务的前置过滤
+					rulval[f] = val                       //整个任务仅过滤一次,将其存储
+				}
+
+				if val != "" {
+					//1.分类的前置过滤,不符合直接跳过当前循环
+					val = PreFilter(val, c.Class_PreRule)
+					if val == "" {
+						continue
+					}
+					//2.循环当前类规则
+					for _, r := range rule {
+						//只要此类的父类不为空
+						if s_pid != "" {
+							ru_s_pid := r.S_pid //当前规则的父规则id,57a02d4fd368081fac39eaf2,57a18dd5d368081d70e185d0,5a4d99b7e138236b380378be
+							//class_s_pid := strings.Split(s_pid, ",") //当前规则所属类的父类集合
+							bcontinue := true
+						L1:
+							for _, ids1 := range top_class_ids {
+								if strings.Index(ru_s_pid, ids1) > -1 {
+									bcontinue = false
+									break L1
+								}
+							}
+							if bcontinue { //没有找到父类的识别,跳过此规则
+								continue
+							}
+						}
+						ruval := val //避免不同class的rule串行过滤
+						if len(r.Rule_PreRule) > 0 {
+							ruval = PreFilter(ruval, r.Rule_PreRule) //rule的前置过滤
+						}
+						if ruval == "" {
+							continue
+						}
+						//开始识别
+						i_rule := r.Reg //toptype=结果 的所有规则
+						//util.Debug("text---", ruval, "rule---", i_rule)
+						b, rulearr := DFAAnalyRules(ruval, i_rule)
+						//util.Debug("===============", b, f, rulearr, r.S_name)
+						if b {
+							detail := util.ObjToString(tmp["detail"])
+							if len(r.NotReg) > 0 { //排除规则
+								dnrb, _ := DFAAnalyRules(detail, r.NotReg)
+								if dnrb { //排除规则匹配成功,匹配下一条
+									continue
+								}
+							}
+						}
+
+						if b {
+							//title、channel二级分类中标处理
+							if r.S_name == "中选" && len(r.DetailReg) > 0 {
+								detail := util.ObjToString(tmp["detail"])
+								if len(r.NotReg) > 0 { //排除规则
+									dnrb, _ := DFAAnalyRules(detail, r.NotReg)
+									if dnrb { //排除规则匹配成功,匹配下一条
+										continue
+									}
+								}
+								//util.Debug("detail---", detail)
+								drb, _ := DFAAnalyRules(detail, r.DetailReg)
+								//util.Debug("-----", drb)
+								if drb {
+									SMap.AddKey(savefield, "中标")
+								} else {
+									SMap.AddKey(savefield, "成交")
+								}
+								break L
+							}
+
+							//57a02bc0d368081fac39eaef =[57a02cdcd368081fac39eaf1]
+							s_name := r.S_name
+							//词频统计
+							if tt.I_wordcount == 1 && len(rulearr) > 0 {
+								tt.WcLock.Lock()
+								map1 := tt.WordCount[s_name]
+								if map1 == nil {
+									map1 = map[string]int{}
+								}
+								for _, rulekey := range rulearr {
+									map1[rulekey]++
+								}
+								tt.WordCount[s_name] = map1
+								tt.WcLock.Unlock()
+							}
+							if savefield != "" {
+								s_code := r.S_code
+								if tt.I_multiclass == 0 { //单分类
+									if tt.I_savetype == 1 { //存储属性
+										SMap.AddKey(savefield, s_name)
+									} else {
+										SMap.AddKey(savefield, s_code)
+									}
+									break L //停止其他字段识别
+								} else { //多分类
+									sf := []string{}
+									b := false
+									if SMap.Map[savefield] != nil {
+										b = true
+										sf = SMap.Map[savefield].([]string)
+									}
+									if tt.I_savetype == 1 { //
+										sf = append(sf, s_name)
+									} else {
+										sf = append(sf, s_code)
+									}
+									if b {
+										SMap.Map[savefield] = sf
+									} else {
+										SMap.AddKey(savefield, sf)
+									}
+								}
+							}
+						}
+					}
+				}
+			}
+			//设置默认值
+			//log.Println("smp", SMap.Map, SMap.Keys)
+			if savefield != "" && SMap.Map[savefield] == nil && c.S_default != "" {
+				if tt.I_multiclass == 0 { //单分类
+					//if savefield == "buyerclass" && util.ObjToString(tmp["buyer"]) == "" { //buyer不存在时,无buyerclass字段
+					//	return SMap
+					//}
+					SMap.AddKey(savefield, c.S_default)
+				} else { //多分类
+					SMap.AddKey(savefield, []string{c.S_default})
+				}
+			}
+		}
+	}
+
+	//多分类去重
+	if tt.I_multiclass != 0 {
+		for _, k := range SMap.Keys {
+			repeatMap := make(map[string]string)
+			tmpArr := make([]string, 0)
+			if arr, ok := SMap.Map[k].([]string); ok {
+				for _, v := range arr {
+					if repeatMap[v] != "has" {
+						tmpArr = append(tmpArr, v)
+						repeatMap[v] = "has"
+					}
+				}
+				SMap.Map[k] = tmpArr
+			}
+		}
+	} /* else { //单分类
+		for k, v := range tmpSavefield {
+			if k != "" && SMap.Map[k] == nil {
+				SMap.Map[k] = v
+			}
+		}
+	}*/
+	return SMap
+}
+
 //标签识别过程
 func TagClassificationRun(tt *TTask, tmp map[string]interface{}) *tools.SortMap {
 	SMap := tools.NewSortMap()
@@ -778,9 +1023,22 @@ func LoadUpdateTask(_id, s_mgourl, s_mgodb, s_coll, i_poolsize, s_esurl, s_esdb,
 				if util.IntAll(tmp["infoformat"]) == 2 { //此处增加特例
 					SMap.AddKey("toptype", "拟建")
 					SMap.AddKey("subtype", "拟建")
+				} else if util.IntAll(tmp["infoformat"]) == 3 {
+					SMap.AddKey("toptype", "产权")
+					SMap.AddKey("subtype", "产权")
 				} else {
 					//SMap = ClassificationRun(tt, tmp)
 					SMap = NewClassificationRun(tt, tmp)
+					//一级分类时,符合结果中成交规则时
+					if SMap.Map["toptype"] == "招标" && SMap.Map["subtype"] != "单一" {
+						if u.ChargeDetailResult(tmp["detail"].(string)) {
+							SMap.Map["toptype"] = "结果"
+							resa := ReSub(tt, tmp, "结果")
+							subtype := resa.Map["subtype"]
+							delete(SMap.Map, "subtype")
+							SMap.Map["subtype"] = subtype
+						}
+					}
 				}
 				bupdate := false
 				//对比,是否保存

+ 162 - 0
src/util/charge_rule.go

@@ -0,0 +1,162 @@
+package util
+
+import (
+	"go.mongodb.org/mongo-driver/bson/primitive"
+	"regexp"
+)
+
+// 审批数据判断字段,包含三个以上属于拟建
+//var spFields = []string{"approvecode", "owner", "projectaddr", "project_scale_info", "projectname"}
+
+//ChargeDetailResult 检测内容是否符合 toptype=结果
+func ChargeDetailResult(detail string) bool {
+	if ChargeDetailZB(detail) || ChargeDetailCJ(detail) {
+		return true
+	}
+	return false
+}
+
+//ChargeDetailCJ 检测内容是否符合 toptype=结果;subtype=成交
+func ChargeDetailCJ(detail string) bool {
+	var reg = regexp.MustCompile("(成交单位|成交人|成交供应商)[::][\\s ]*(.{2,25}(公司))")
+	//含有 投标截止时间 关键词,代表不是结果,需要排除
+	var reg2 = regexp.MustCompile("(投标截止时间)")
+	res1 := reg.MatchString(detail)
+	res2 := reg2.MatchString(detail)
+
+	if !res2 && res1 {
+		return true
+	}
+	return false
+}
+
+//ChargeDetailZB 检测内容是否符合 toptype=结果;subtype=中标
+func ChargeDetailZB(detail string) bool {
+	var reg = regexp.MustCompile("(中标人单位名称|中标供应商|中标供应商名称|中标人名称|中签单位名称|中签单位|中标商家)[::][\\s ]*(.{2,25}(公司|单位|局|厅))")
+	var reg2 = regexp.MustCompile("(投标截止时间)")
+	res1 := reg.MatchString(detail)
+	res2 := reg2.MatchString(detail)
+
+	if res1 && !res2 {
+		return true
+	}
+	return false
+}
+
+////ChargeSP 判断拟建数据是否 属于审批,三个以上属于 拟建,true 审批,false 拟建
+//func ChargeSP(tmp map[string]interface{}) bool {
+//	num := 0
+//	for _, v := range spFields {
+//		if val, ok := tmp[v]; ok && val != nil {
+//			num++
+//		}
+//	}
+//	if num >= 3 {
+//		return false
+//	}
+//	return true
+//}
+
+//GetJyKey 免费订阅:o_jy.a_key.key/appendkey
+func GetJyKey(data map[string]interface{}) (res []interface{}) {
+	// 获取o_jy.a_key.key的值
+	if ojy, ok := data["o_jy"].(map[string]interface{}); ok {
+		if akey, ok := ojy["a_key"].(primitive.A); ok {
+			for _, v := range akey {
+				if m, ok := v.(map[string]interface{}); ok {
+					if key, ok := m["key"].(primitive.A); ok {
+						res = append(res, key...)
+					}
+					if key, ok := m["appendkey"].(primitive.A); ok {
+						res = append(res, key...)
+					}
+				}
+			}
+		}
+	}
+
+	return
+}
+
+//GetVipKey 超级订阅:o_vipjy.a_items.a_key.key/appendkey
+func GetVipKey(data map[string]interface{}) (res []interface{}) {
+	//o_vipjy.a_items.a_key.key
+	if o_vipjy, ok := data["o_vipjy"].(map[string]interface{}); ok {
+		if aItems, ok := o_vipjy["a_items"].(primitive.A); ok {
+			for _, aItem := range aItems {
+				if a_k, ok := aItem.(map[string]interface{}); ok {
+					if aKey, ok := a_k["a_key"].(primitive.A); ok {
+						for _, ak := range aKey {
+							if va, ok := ak.(map[string]interface{}); ok {
+								if key, ok := va["key"].(primitive.A); ok {
+									res = append(res, key...)
+								}
+								if key, ok := va["appendkey"].(primitive.A); ok {
+									res = append(res, key...)
+								}
+							}
+						}
+					}
+				}
+			}
+		}
+	}
+
+	return
+}
+
+//GetMemberKey 大会员订阅:o_member_jy.a_items.a_key.key/appendkey
+func GetMemberKey(data map[string]interface{}) (res []interface{}) {
+	//o_member_jy.a_items.a_key.key
+	if o_member_jy, ok := data["o_member_jy"].(map[string]interface{}); ok {
+		if aItems, ok := o_member_jy["a_items"].(primitive.A); ok {
+			for _, aItem := range aItems {
+				if tem, ok := aItem.(map[string]interface{}); ok {
+					if aKey, ok := tem["a_key"].(primitive.A); ok {
+						for _, ak := range aKey {
+							if va, ok := ak.(map[string]interface{}); ok {
+								if key, ok := va["key"].(primitive.A); ok {
+									res = append(res, key...)
+								}
+								if key, ok := va["appendkey"].(primitive.A); ok {
+									res = append(res, key...)
+								}
+							}
+						}
+					}
+				}
+			}
+		}
+	}
+
+	return
+}
+
+//RemoveDuplicates 过滤重复数据
+func RemoveDuplicates(elements []interface{}) []interface{} {
+	encountered := map[interface{}]bool{}
+	result := []interface{}{}
+	for _, v := range elements {
+		if encountered[v] == true {
+			continue
+		} else {
+			encountered[v] = true
+			result = append(result, v)
+		}
+	}
+	return result
+}
+
+//GetUserKeys 获取用户订阅关键词
+func GetUserKeys(data map[string]interface{}) (res []interface{}) {
+	keys := GetJyKey(data)
+	vkeys := GetVipKey(data)
+	mkeys := GetMemberKey(data)
+
+	res = append(res, keys...)
+	res = append(res, vkeys...)
+	res = append(res, mkeys...)
+	res = RemoveDuplicates(res)
+
+	return
+}

+ 239 - 0
src/util/util_test.go

@@ -0,0 +1,239 @@
+package util
+
+import (
+	"encoding/json"
+	"fmt"
+	"testing"
+)
+
+func TestChargeDetailResult(t *testing.T) {
+	detail := `
+ 一、项目基本情况
+项目编号:QSZBCG2023-063
+项目名称:潜山市特困人员住院护理险(服务)采购项目
+资金来源:财政
+预算金额:1800000.00元/年
+最高限价:1800000.00元/年
+采购需求:详见附件“服务需求与技术要求”
+标段(包别)划分:一个包
+评标办法:综合评分      
+合同履行期限:贰年(自2023年1月1日至2024年12月31日)(服务期满,经潜山市民政局和中标方协商一致后,可续签一年)
+ 
+本项目不接受联合体投标。
+二、申请人的资格要求:
+1.满足《中华人民共和国政府采购法》第二十二条规定;
+2.落实政府采购政策需满足的资格要求:/
+3.本项目的特定资格要求:
+①供应商存在以下不良信用记录情形之一的,不得推荐为成交候选供应商,不得确定为成交供应商:
+a、供应商(包括供应商分公司或非法人性质分支机构等)或其法定代表人被人民法院列入失信被执行人的(核查网址:http://zxgk.court.gov.cn/);
+b、供应商(包括供应商分公司或非法人性质分支机构等)被税务部门列入重大税收违法案件当事人名单的(核查网址:http://www.chinatax.gov.cn);
+c、供应商(包括供应商分公司或非法人性质分支机构等)被政府采购监管部门列入政府采购严重违法失信行为记录名单的(信用中国、中国政府采购网 核查网址:http://www.creditchina.gov.cn/、http://www.ccgp.gov.cn/);
+②供应商(包括供应商分公司或非法人性质分支机构等)经营异常名录核查(核查网址:http://www.gsxt.gov.cn/index.html)(已被公示移出的除外):
+a、供应商(不包括供应商分公司或非法人性质分支机构等)被登记机关列入经营异常名录或严重违法失信企业名单(黑名单)信息的(已被公示移出的除外),不得推荐为成交候选供应商,不得确定为成交供应商;
+b、若供应商的分公司或非法人性质分支机构被登记机关列入经营异常名录的(已被公示移出的除外)不作为否决条件,但是将会在招标文件评标办法中予以考量[以投标截止时间为查询截止日。如供应商分公司或非法人性质分支机构有一条经营异常名录的,扣0.1分;如供应商分公司或非法人性质分支机构有一条严重违法失信企业名单(黑名单)信息的,扣0.2分;本项最高扣2分,若供应商分公司或非法人性质分支机构已办理注销的不予扣分],具体见招标文件。
+③供应商不良行为记录查询(核查网址:http://aqggzy.anqing.gov.cn/):
+供应商被安庆市(含五县、桐城市、潜山市)公共资源交易监督管理部门记不良行为记录的,不作为否决条件,但是将会在招标文件评标办法中予以考量[a、自2018年7月1日起至投标截止日(含),未被安庆市(含五县、桐城市、潜山市)公共资源交易监督管理部门记不良行为记录的,得2分;b、自2018年7月1日起至投标截止日(含),被安庆市(含五县、桐城市、潜山市)公共资源交易监督管理部门记不良行为记录,但投标截止日不在公示有效期内(公示有效期即限制行为开始时间至限制行为结束时间)的得1分�rc、自2018年7月1日起至投标截止日(含),被安庆市(含五县、桐城市、潜山市)公共资源交易监督管理部门记不良行为记录且投标截止日在公示有效期内(公示有效期即限制行为开始时间至限制行为结束时间)的得0分;d、2018年7月1日起至投标截止日(含),累计被安庆市(含五县、桐城市、潜山市)公共资源交易监督管理部门记2次及以上不良行为记录的得0分。] 具体见招标文件。
+4.具有合法有效的营业执照。
+三、获取招标文件
+时间:2023年 03 月 08 日至2023年 03 月 20 日, 每天上午8:00至12:00,下午14:30至17:30(北京时间,法定节假日除外)
+地点:安庆市公共资源交易中心平台(aqggzy.anqing.gov.cn)
+方式:(1)投标人须登录安庆市公共资源交易中心平台查询、获取招标文件。首次登录须在安徽省公共资源交易市场主体库(http://61.190.70.20/ahggfwpt-zhutiku/dengludenglu)办理入库手续,办理入库不收取任何费用。安徽省公共资源交易市场主体库使用相关问题(如系统登录、信息登记、录入及提交、数字证书关联等)请拨打服务电话:010-86483801 转 5-2(工作日)。  
+CA 数字证书有关问题请拨打服务电话:安徽 CA 客服400-880-4959(工作日)。
+市场主体招标环节和投标环节系统使用服务电话:400-998-0000(8:00-21:00)。
+(2)投标人登录安庆市公共资源交易中心平台获取招标文件及其它资料(含澄清和补充说明等)。如在招标文件获取过程中遇到系统问题,请拨打技术支持服务热线400-9980000,QQ:4008503300。
+售价:免费。
+四、提交投标文件截止时间、开标时间和地点
+2023年 03 月 29 日 09 点30分(北京时间)
+地点:安庆市公共资源交易平台     
+开评标方式:全流程电子化交易,在线开标
+五、公告期限
+自本公告发布之日起5个工作日。
+六、其他补充事宜
+1.投标申请人的联系人电话(手机)、电子邮箱等通讯方式在招投标过程中必须保持畅通,否则因上述原因造成的后果,责任自负。
+2.本项目采用电子招投标方式,请投标人在“安庆市公共资源交易服务网”下载专区下载“电子招投标系统平台操作手册”、在“安庆市公共资源交易中心网员系统”―登录页面―工具下载中下载电子投标文件制作工具等相关资料,仔细阅读招标文件要求和相关操作手册。
+3.供应商应合理安排招标文件获取时间。如果因计算机及网络故障等无法获取采购文件,责任自负。
+4.本项目开评标实行全流程电子化,开标活动在线完成。开标时投标人不得到达开标现场,不接受现场解密,实行远程解密和在线询标。各投标人认真学习《安庆新系统投标单位操作手册v1.0》,务必掌握远程解密方法和在线回复询标方法。
+七、对本次招标提出询问,请按以下方式联系。
+1.采购人信息
+名    称:潜山市民政局   
+地    址: 安庆市潜山市梅城镇梅苑路282号   
+联 系 人: 黄先生    
+联系方式: 0556-8958945   
+2.采购代理机构信息
+名    称: 安徽妙轩工程咨询有限公司     
+地    址: 安徽省潜山市开发区华瑞路128号 
+联 系 人:   汪先生        
+联系方式:  18255604225      
+3.项目联系方式
+项目联系人:黄先生
+电 话:  0556-8958945  
+1. 为贯彻落实中央、省、市“扫黑除恶”专项斗争有关文件和会议精神,净化我市公共资源交易环境,对在交易过程中如发现有涉嫌围标、串标、恶意竞标的违法违规人员,依据相关规定,将线索移送市扫黑办。
+2. 根据疫情防控要求,本项目供应商通过远程在线解密投标文件,不得到达招标文件中规定的开标现场参与开标活动。
+3. 评审中,评标委员会发现供应商的投标文件中对同类问题表述不一致、前后矛盾、有明显文字和计算错误的内容、有可能不符合招标文件规定等情况需要澄清时,评标委员会应当通过网上系统对供应商进行询标,要求供应商进行必要的澄清、说明或补正。
+4. 投标文件制作工具中投标文件格式与本招标文件“第六章投标文件格式”不一致时,以本招标文件“第六章投标文件格式”为准,招标文件另有规定的除外。`
+
+	detail2 := `浙江二建-电建-沈阳华润热电联产项目-铝芯电缆采购评标工作已经结束,中标人已经确定。现将中标结果公布如下:
+中标供应商:辽宁龙飞线缆有限公司(中标总金额:¥33349.5)
+中标理由:合理低价`
+	res := ChargeDetailResult(detail)
+	fmt.Println("TestChargeDetailResult =>", res)
+
+	res2 := ChargeDetailResult(detail2)
+	fmt.Println("TestChargeDetailResult2 =>", res2)
+}
+
+func TestGetJyKey(t *testing.T) {
+	data := `{
+    "_id" : "59c8f51bc9ebc230022bd75b",
+    "s_province" : "",
+    "i_ts_guide" : 1,
+    "s_unionid" : "oZQC_sx3IU9vsKys2rP4mh3KHP8I",
+    "o_jy" : {
+        "i_ratemode" : 2,
+        "l_modifydate" : 1525432476,
+        "a_key" : [
+            {
+                "key" : [
+                    "冷藏车"
+                ],
+                "appendkey" : null,
+                "matchway" : 0,
+                "notkey" : null,
+                "updatetime" : 1644287532
+            },
+            {
+                "key" : [
+                    "冷链"
+                ],
+                "appendkey" : null,
+                "matchway" : 0,
+                "notkey" : null,
+                "updatetime" : 1644287543
+            },
+            {
+                "key" : [
+                    "冷链"
+                ],
+                "appendkey" : null,
+                "matchway" : 0,
+                "notkey" : null,
+                "updatetime" : 1644287543
+            },
+            {
+                "appendkey" : null,
+                "key" : [
+                    "运输车"
+                ],
+                "matchway" : 1,
+                "notkey" : null,
+                "updatetime" : 1644287552
+            }
+        ],
+        "s_email" : "",
+        "s_surprise" : "A",
+        "o_area" : {
+            "江苏" : [
+
+            ]
+        },
+        "s_item" : "未分类",
+        "a_infotype" : [
+
+        ]
+    }
+}`
+
+	var obj map[string]interface{}
+	if err := json.Unmarshal([]byte(data), &obj); err != nil {
+		panic(err)
+	}
+	tags := GetUserKeys(obj)
+
+	fmt.Println(tags)
+	//[冷藏车 冷链 冷链 运输车]
+	fmt.Println("----------------------------------------")
+	tags = RemoveDuplicates(tags)
+	fmt.Println(tags)
+	fmt.Println("----------------------------------------")
+	fmt.Println("GetVipKey")
+	vtags := GetVipKey(obj)
+	fmt.Println(vtags)
+
+	fmt.Println("----------------------------------------")
+	fmt.Println("GetMemberKey")
+	mtags := GetMemberKey(obj)
+	fmt.Println(mtags)
+}
+
+func TestGetUserKeys(t *testing.T) {
+	data := `{
+		"o_member_jy" : {
+        "i_matchway" : 1,
+        "i_ratemode" :1,
+        "i_wxpush" : 1,
+        "a_items" : [
+            {
+                "a_key" : [
+                    {
+                        "notkey" : null,
+                        "matchway" : 1,
+                        "appendkey" : null,
+                        "key" : [
+                            "课程建设"
+                        ],
+                        "updatetime" : 1679386813
+                    },
+                    {
+                        "appendkey" : null,
+                        "key" : [
+                            "智慧教室"
+                        ],
+                        "updatetime" :1679386813,
+                        "notkey" : null,
+                        "matchway" : 1
+                    },
+                    {
+                        "appendkey" : null,
+                        "key" : [
+                            "全景教室"
+                        ],
+                        "updatetime" : 1679386813,
+                        "notkey" : null,
+                        "matchway" : 1
+                    },
+                    {
+                        "updatetime" : 1679386813,
+                        "notkey" : null,
+                        "matchway" : 1,
+                        "appendkey" : null,
+                        "key" : [
+                            "沉浸式教室"
+                        ]
+                    },
+                    {
+                        "notkey" : null,
+                        "matchway" : 1,
+                        "appendkey" : null,
+                        "key" : [
+                            "精品课程资源"
+                        ],
+                        "updatetime" : 1679386813
+                    }
+                ],
+                "s_item" : "未分类",
+                "updatetime" : 1679386813
+            }
+        ],
+        "i_apppush" : 1
+    },
+    "s_phone" : "19917630508"
+}
+`
+
+	var obj map[string]interface{}
+	if err := json.Unmarshal([]byte(data), &obj); err != nil {
+		panic(err)
+	}
+	userKeys := GetUserKeys(obj)
+	fmt.Println(userKeys)
+}