wanghuidong 5 سال پیش
والد
کامیت
5ef62fb402

+ 2 - 1
.gitignore

@@ -9,4 +9,5 @@ bin
 /src/util/utiltag_test.go
 .idea/
 customerdata/.DS_Store
-customerdata/src/.DS_Store
+customerdata/src/.DS_Store
+.idea

+ 52 - 27
src/config.json

@@ -1,32 +1,57 @@
 {
-  "port": "7000",
-  "udpport": ":11117",
-  "mgodb": "192.168.3.207:27092",
-  "dbsize": 5,
-  "dbname": "datatag_ent",
-  "mgodben": "192.168.3.207:27092",
-  "dbnameen": "enterprise",
-  "mongoenc": "winner_enterprise",
-  "subday": 15,
-  "customer": {
+	"port": "7000",
+	"udpport": ":11117",
+	"mgodb": "192.168.3.207:27092",
+	"dbsize": 5,
+	"dbname": "datatag_ent",
+	"mgodben": "192.168.3.207:27092",
+	"dbnameen": "enterprise",
+	"mongoenc": "winner_enterprise",
+	"subday": 15,
+	"customer": {
 	"api": "http://web-jytest.jianyu360.cn/api1/user/jy_newuser",
-  	"mgodb": "192.168.3.128:27080",
+	"mgodbs": "192.168.3.128:27080",
 	"dbname": "jyqyfw",
 	"dbsize": 5 
-  },
-  "preview_href": "http://192.168.20.127:5500/data-preview.html",
-  "elasticsearch": "http://127.0.0.1:9800",
-  "elasticsearch_index": "bidding",
-  "elasticsearch_type": "bidding",
-  "elasticPoolSize": 10,
-  "jkmail": {
-    "to": "wangjianghan@topnet.net.cn",
-    "api": "http://10.171.112.160:19281/_send/_mail"
-  },
-  "indexNode": {
-    "addr": "127.0.0.1",
-    "port": 1483,
-    "stype": "biddingall",
-    "memo": "生索引"
-  }
+	},
+	"preview_href": "http://192.168.20.127:5500/data-preview.html",
+	"elasticsearch": "http://192.168.3.128:9800",
+	"elasticsearch_index": "bidding",
+	"elasticsearch_type": "bidding",
+	"elasticPoolSize": 10,
+	"jkmail": {
+	  "to": "wangjianghan@topnet.net.cn",
+	  "api": "http://10.171.112.160:19281/_send/_mail"
+	},
+	"indexNode": {
+	  "addr": "127.0.0.1",
+	  "port": 1483,
+	  "stype": "biddingall",
+	  "memo": "生索引"
+	},
+	"buyerinfo": {
+		"addr": "192.168.3.166:27082",
+		"size": 15,
+		"db": "public",
+		"hospcoll": "buyer_rank",
+		"buyerentcoll": "buyer_ent"
+	},
+	"enterprise": { 
+		"addr": "192.168.3.207:27092",
+		"size": 15,
+		"db": "mxs",
+		"coll": "qyxy_ent"
+	},
+	"save": {
+		"addr": "192.168.3.207:27092",
+		"size": 15,
+		"db": "mxs",
+		"coll": "usermail"
+	},
+	"es": {
+		"addr": "http://192.168.3.128:9800",
+		"index": "bidding_v1",
+		"itype": "bidding",
+		"pool": 15
+	}
 }

+ 143 - 0
src/history/datamodel.go

@@ -0,0 +1,143 @@
+package history
+
+import (
+	"regexp"
+	"sync"
+
+	"gopkg.in/mgo.v2/bson"
+)
+
+var MgoSaveCache = make(chan map[string]interface{}, 1000)
+var SP = make(chan bool, 5)
+
+//客户模型
+type Customer struct {
+	ID           string                            //客户ID
+	Name         string                            //客户名称
+	AppId        string                            //appid
+	PushModel    int                               //推送模式 0:全局模式;1:部门模式;2:标签模式;3:部门+标签模式;4:规则模式
+	Departments  []*Department                     //所有部门
+	IsTagRule    bool                              //是否打标签
+	IsSearchHosp bool                              //是否查询第三方医院信息
+	IsSearchEnps bool                              //是否查询第三方企业信息
+	TagRules     []*TagRule                        //打标签规则集合
+	SaveDataMap  map[string]map[string]interface{} //最终入库数据(全局模式)
+	SaveDataArr  []map[string]interface{}          //最终入库数据(规则模式)
+	IdRange      bson.M                            //由当前时间生成是id查询区间
+}
+
+//部门模型
+type Department struct {
+	ID             string                              //部门ID
+	Name           string                              //部门名称
+	CustomerID     string                              //所属客户id
+	Rules          []*SearchRule                       //客户的多个规则
+	DepartmentData map[string][]map[string]interface{} //部门所有数据 key:ruleid value:[{k:dataid v:data},{k:dataid v:data}]
+	DataLock       *sync.Mutex                         //数据锁
+	SaveDataMap    map[string]map[string]interface{}   //最终入库数据(部门内部去重)
+}
+
+//查询规则模型
+type SearchRule struct { //一条查询规则
+	ID           string
+	Name         string
+	ExtFieldType int                    //规则1、标准字段包,2、高级字段包
+	CustomerID   string                 //所属客户id
+	DepartmentID string                 //所属部门id
+	EsQuery      string                 //客户所有标签的es语句
+	Fields       map[string]interface{} //存储所有字段(清理词、关键词、附加词匹配方式)
+	GCW          *ClearWord             //全局清理词
+	KW           []*KeyWord             //多组关键词(与附加词一一对应)
+	AW           []*AddWord             //多组附加词(与关键词一一对应)
+	//RuleData     *sync.Map              //key:规则id;val:某个规则查出的数据
+}
+
+//打标签规则模型
+type TagRule struct { //一条打标签规则
+	ID            string
+	Name          string
+	CustomerId    string
+	DepartRuleIds map[string]bool        //部门规则id组
+	Fields        map[string]interface{} //存储所有字段(排除词、关键词、附加词匹配方式)
+	//GNW           []*GlobalNotWord //全局排除词
+	//GAW           []*GlobalAddWord //全局附加词
+	NW       []*NotKeyWord //排除词
+	KW       []*KeyWord    //多组关键词(与附加词一一对应)
+	AW       []*AddWord    //多组附加词(与关键词一一对应)
+	TagNames []string      //一个关键词+一个附加词都有一个标签名称
+}
+
+//全局清理词类型
+type ClearWord struct {
+	KeyReg    []*regexp.Regexp
+	MatchType []string //清理词匹配方式
+}
+
+//关键词类型
+type KeyWord struct {
+	KeyReg    *Reg     //匹配规则(0:逗号分隔 1:&&分隔)      例如:[[智能,5G],[智慧交通,智慧城市]]
+	MatchType []string //关键词的匹配方式
+	//KeyWordMap []map[int]bool
+	//KeyWordMap []map[int]interface{} //记录KeyReg中字母规则(0:逗号分隔 1:&&分隔 interface{}实际为map[string]string k:大写 v:原值)
+	//KeyStr     [][]string            //记录关键词的原始值,与KeyReg一一对应 例如:[[智能,5G],[智慧交通&&智慧城市]]
+	//KeyWordMap []map[int]map[string]string //记录KeyReg中字母规则(0:逗号分隔 1:&&分隔)
+}
+
+//附加词类型
+type AddWord struct {
+	KeyReg    *Reg     //匹配规则(0:逗号分隔 1:&&分隔)
+	MatchType []string //附加词的匹配方式
+	//AddWordMap []map[int]bool
+	//AddWordMap []map[int]interface{} //记录KeyReg中字母规则(0:逗号分隔 1:&&分隔 interface{}实际为map[string]string k:大写 v:原值)
+	//KeyStr     [][]string
+	//AddWordMap []map[int]map[string]string //记录KeyReg中字母规则(0:逗号分隔 1:&&分隔)
+}
+
+//排除词
+type NotKeyWord struct {
+	KeyReg    *Reg     //例如:排除1&&排除2,排除3,KeyReg[0]存 排除3;KeyReg[1]存 排除1 排除2
+	MatchType []string //排除词的匹配方式
+	//NotWordMap []map[int]bool //记录KeyReg中字母规则
+}
+
+//匹配正则
+type Reg struct {
+	CReg []*CommonReg //逗号分隔
+	AReg []*AndRegPre //&&分隔
+	//KeyStr []string     //原始值(用于含字母时取原值)
+}
+
+//逗号分隔正则
+type CommonReg struct {
+	CrVal    *regexp.Regexp //正则类型
+	CsVal    string         //原始值
+	IsLetter bool           //是否含字母
+}
+
+//&&分隔正则
+type AndRegPre struct {
+	AndRegKid []*AndReg
+	CsVal     string //原始值
+}
+
+//&&分隔正则
+type AndReg struct {
+	CrVal    *regexp.Regexp
+	IsLetter bool //是否含字母
+}
+
+/*
+//全局排除词类型
+type GlobalNotWord struct {
+	KeyReg           [][]*regexp.Regexp //例如:排除1&&排除2,排除3,KeyReg[0]存 排除3;KeyReg[1]存 排除1 排除2
+	MatchType        []string           //排除词的匹配方式
+	GlobalNotWordMap []map[int]bool     //记录KeyReg中字母规则
+}
+
+//全局附加词类型
+type GlobalAddWord struct {
+	KeyReg           [][]*regexp.Regexp //例如:附加1&&附加2,附加3,KeyReg[0]存 附加3;KeyReg[1]存 附加1 附加2
+	MatchType        []string           //匹配方式
+	GlobalAddWordMap []map[int]bool     //记录KeyReg中字母规则
+}
+*/

+ 57 - 0
src/history/historytask.go

@@ -0,0 +1,57 @@
+package history
+
+import (
+	"log"
+	qu "qfw/util"
+	"strings"
+	. "util"
+
+	"github.com/go-xweb/xweb"
+)
+
+type HistoryData struct {
+	*xweb.Action
+	historyTask xweb.Mapper `xweb:"/service/history/(.*)"`
+}
+
+func (this *HistoryData) HistoryTask(history_id string) {
+	log.Println("开始历史任务...")
+	//加载一个客户
+	log.Println("history_id", history_id)
+	customer, _ := Mgo.Find("historylog", map[string]interface{}{"_id": qu.StringTOBsonId(history_id)}, nil, nil, false, -1, -1)
+	log.Println("data", customer)
+	if len(*customer) == 1 {
+		c := (*customer)[0]
+		customerId := qu.BsonIdToSId(c["_id"])
+		customer_name := qu.ObjToString(c["s_name"]) //客户名称
+		appId := qu.ObjToString(c["s_appid"])        //appid
+		extends := qu.ObjToString(c["s_extends"])    //扩展信息
+		pushModel := qu.IntAll(c["i_pushmodel"])     //推送模式
+
+		cus := &Customer{}
+		cus.SaveDataMap = map[string]map[string]interface{}{}
+		cus.SaveDataArr = []map[string]interface{}{}
+		cus.ID = customerId
+		cus.Name = customer_name
+		cus.PushModel = pushModel
+		cus.AppId = appId
+		for _, v := range strings.Split(extends, ",") {
+			if v == "hospitalgrade" {
+				cus.IsSearchHosp = true
+			} else if v == "enterpise" {
+				cus.IsSearchEnps = true
+			}
+		}
+
+		cus.GetTagRules(c["tag_rules"].([]map[string]interface{}))               //获取客户打标签规则
+		cus.GetDepartments("history", c["dep_rules"].([]map[string]interface{})) //获取客户信息
+		//PrintLog(cus)        //打印查看初始化的信息
+		qu.Debug("customer:", cus.ID, cus.Name, cus.PushModel, cus.AppId, cus.IsTagRule, cus.IsSearchHosp, cus.IsSearchEnps, len(cus.TagRules), len(cus.Departments))
+		cus.GetData("history") //获取数据
+		cus.RemoveRepeatData() //数据去重
+
+		cus.AssembelAndSaveData() //组装、保存数据
+	} else {
+		log.Println("初始化客户信息失败")
+	}
+}

+ 541 - 0
src/history/task.go

@@ -0,0 +1,541 @@
+package history
+
+import (
+	"encoding/json"
+	"log"
+	qu "qfw/util"
+	"regexp"
+	. "sqlmodel"
+	"strings"
+	"sync"
+
+	"gopkg.in/mgo.v2/bson"
+
+	. "util"
+
+	"go.mongodb.org/mongo-driver/bson/primitive"
+	es "gopkg.in/olivere/elastic.v1"
+)
+
+//获取客户打标签规则
+func (c *Customer) GetTagRules(tagRules []map[string]interface{}) {
+	log.Println("开始加载标签规则...")
+	defer qu.Catch()
+	if len(tagRules) > 0 {
+		c.IsTagRule = true //查到打标签规则,表示打标签
+		for _, tr := range tagRules {
+			TR := &TagRule{}
+			TR.Fields = make(map[string]interface{})
+			TR.DepartRuleIds = make(map[string]bool)
+			id := qu.BsonIdToSId(tr["_id"])
+			name := qu.ObjToString(tr["s_name"])
+			TR.ID = id
+			TR.Name = name
+			TR.CustomerId = c.ID
+			//部门规则id组
+			if departRuleIds := qu.ObjToString(tr["o_departruleids"]); departRuleIds != "" {
+				for _, drid := range strings.Split(departRuleIds, ",") {
+					TR.DepartRuleIds[drid] = true
+				}
+			}
+			//规则
+			if o_list, ok := tr["o_list"].(primitive.A); ok && len(o_list) > 0 {
+				TR.GetKeyAddNotKeyWord(o_list)
+			}
+			c.TagRules = append(c.TagRules, TR)
+		}
+	}
+}
+
+//获取部门信息
+func (c *Customer) GetDepartments(stype string, departments []map[string]interface{}) {
+	log.Println("开始获取部门信息...")
+	defer qu.Catch()
+	if len(departments) > 0 {
+		departMap := map[string]interface{}{}
+		for _, ds := range departments {
+			s_id := qu.BsonIdToSId(ds["s_departid"])
+			if departMap[s_id] == nil {
+				departMap[s_id] = ds["s_departid_name"]
+			}
+		}
+		for k, v := range departMap {
+			DM := &Department{}
+			DM.DataLock = &sync.Mutex{}
+			DM.DepartmentData = map[string][]map[string]interface{}{}
+			DM.SaveDataMap = map[string]map[string]interface{}{}
+			id := k
+			name := qu.ObjToString(v)
+			DM.ID = id
+			DM.Name = name
+			DM.CustomerID = c.ID
+			dsArr := []map[string]interface{}{}
+			for _, ds := range departments {
+				s_id := qu.BsonIdToSId(ds["s_departid"])
+				if s_id == k {
+					dsArr = append(dsArr, ds)
+				}
+			}
+			DM.GetSearchRules(c.ID, stype, c.IdRange, dsArr) //获取某个部门的所有规则
+			c.Departments = append(c.Departments, DM)
+			//qu.Debug("Departments---", DM.ID, DM.Name, DM.CustomerID, len(DM.Rules))
+		}
+	}
+}
+
+//获取数据
+func (c *Customer) GetData(stype string) {
+	log.Println("开始匹配数据...")
+	defer qu.Catch()
+	client := Es.GetEsConn()
+	defer Es.DestoryEsConn(client)
+	for _, dm := range c.Departments {
+		for _, sr := range dm.Rules {
+
+			//测试
+			// MgoDataTest(sr, dm, c)
+			// return
+
+			ch := make(chan bool, 10)
+			wg := &sync.WaitGroup{}
+			escount := Es.Count(Index, Itype, sr.EsQuery)
+			log.Println("查询总数:", escount, "规则ID:", sr.ID, "EsQuery:", sr.EsQuery)
+			if escount == 0 {
+				continue
+			}
+			//查询条件类型转换
+			var q es.Query
+			tmpQuery := es.BoolQuery{
+				QueryStrings: sr.EsQuery,
+			}
+			q = tmpQuery
+
+			//游标查询,index不支持别名,只能写索引库的名称
+			res, err := client.Scroll(Index).Query(q).Size(200).Do() //查询一条获取游标
+			if err == nil {
+				numDocs := 0
+				scrollId := res.ScrollId
+				for {
+					if scrollId == "" {
+						log.Println("ScrollId Is Error")
+						break
+					}
+					searchResult, err := client.Scroll(Index).Size(200).ScrollId(scrollId).Do() //查询
+					if err != nil {
+						if err.Error() == "EOS" { //迭代完毕
+							log.Println("Es Search Data Over:", err)
+						} else {
+							log.Println("Es Search Data Error:", err)
+						}
+						break
+					}
+					for _, hit := range searchResult.Hits.Hits {
+						//开始处理数据
+						wg.Add(1)
+						ch <- true
+						func(tmpHit *es.SearchHit) {
+							defer func() {
+								<-ch
+								wg.Done()
+							}()
+							tmp := make(map[string]interface{})
+							if json.Unmarshal(*tmpHit.Source, &tmp) == nil {
+								if stype != "history" {
+									if !SkipData(tmp) {
+										qu.Debug("跳过该条数据,发布时间在入库时间7天之前,", qu.ObjToString(tmp["_id"]))
+										return
+									}
+								}
+								id := qu.ObjToString(tmp["_id"])
+								tmp["id"] = id //记录数据原有id
+								delete(tmp, "_id")
+								if sr.ExtFieldType == 2 {
+									findwinner := strings.TrimSpace(qu.ObjToString(tmp["winner"]))
+									if findwinner != "" {
+										finddata := MgoEnps.FindOne(EnpsColl, bson.M{"company_name": findwinner})
+										if finddata != nil {
+											if legal_person := qu.ObjToString(finddata["legal_person"]); legal_person != "" {
+												tmp["legal_person"] = legal_person
+											}
+											//从最新年报中获取 中标单位联系电话、中标单位邮箱
+											if annual_reports, ok := finddata["annual_reports"].(primitive.A); ok && len(annual_reports) > 0 {
+												if anreport, ok := annual_reports[0].(map[string]interface{}); ok { //最新年报
+													if email := qu.ObjToString(anreport["company_email"]); email != "" {
+														tmp["company_email"] = email
+													}
+													if phone := qu.ObjToString(anreport["company_phone"]); phone != "" {
+														tmp["company_phone"] = phone
+													}
+												}
+											}
+										}
+									}
+								}
+								matchKey := map[string]bool{}     //记录所有匹配上的关键词
+								matchKeyType := map[string]bool{} //记录关键词对应的匹配方式
+								//先获取用到的所有字段值
+								fieldText := map[string]interface{}{}
+								for field, _ := range sr.Fields {
+									text := qu.ObjToString(tmp[field])
+									text = ProcessData(text) //处理文本(字母转大写,删除一些符号)
+									fieldText[field] = text
+								}
+								//清理词清理
+								for _, cwm := range sr.GCW.MatchType {
+									if text := qu.ObjToString(fieldText[cwm]); text != "" {
+										for _, gcw_reg := range sr.GCW.KeyReg {
+											text = gcw_reg.ReplaceAllString(text, "")
+										}
+										fieldText[cwm] = text
+									}
+								}
+								/*
+									因为要记录所有匹配上的关键词,所有优先匹配附加词,在匹配关键词
+								*/
+								//1.附加词匹配
+								IsMatch := false
+								//qu.Debug("sr.AW---", len(sr.AW))
+								for i, aw := range sr.AW {
+									//qu.Debug("-------------------------开始附加词匹配--------------------------")
+									IsMatchAddKey := RegMatch(fieldText, aw.MatchType, aw.KeyReg, nil, nil, false, true)
+									//qu.Debug(IsMatchAddKey, "------------------------------------------------------------")
+
+									//2.关键词匹配
+									if IsMatchAddKey {
+										kw := sr.KW[i]
+										//qu.Debug("-------------------------开始关键词匹配--------------------------")
+										IsMatchKey := RegMatch(fieldText, kw.MatchType, kw.KeyReg, matchKey, matchKeyType, true, false)
+										//qu.Debug(IsMatchKey, "------------------------------------------------------------")
+										if IsMatchKey {
+											IsMatch = true
+										}
+									}
+								}
+								/*
+									到此已经匹配完数据
+								*/
+								//qu.Debug("---------------------", id, IsMatch, matchKey)
+								if IsMatch { //匹配成功,数据上新增规则id,matchKey,item并临时保存数据
+									tmpMatchKey := MapDataToArr(matchKey)
+									tmpMatchKeyType := MapDataToArr(matchKeyType)
+									tmp["matchkey"] = strings.Join(tmpMatchKey, ",")
+									tmp["matchtype"] = strings.Join(tmpMatchKeyType, ",")
+									tmp["ruleid"] = sr.ID
+									tmp["rulename"] = sr.Name
+
+									//开始打标签
+									//qu.Debug("c.IsTagRule+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++")
+									if c.IsTagRule {
+										tagNameMap := map[string]bool{}
+										tagIdMap := map[string]bool{}
+										//qu.Debug("c.TagRules---", len(c.TagRules))
+										for _, tr := range c.TagRules {
+											if tr.DepartRuleIds[sr.ID] {
+												//先获取用到的所有字段值
+												for field, _ := range tr.Fields {
+													if fieldText[field] == nil { //补充fieldText
+														text := qu.ObjToString(tmp[field])
+														text = ProcessData(text) //处理文本(字母转大写,删除一些符号)
+														fieldText[field] = text
+													}
+												}
+												//qu.Debug("-------------------------开始排除词匹配--------------------------")
+												//qu.Debug("tr.NW---", len(tr.NW))
+												for j, tag_nw := range tr.NW { //排除词匹配
+													IsMatchNotKey := RegMatch(fieldText, tag_nw.MatchType, tag_nw.KeyReg, nil, nil, false, false)
+													//qu.Debug(IsMatchNotKey, "------------------------------------------------------------")
+													if !IsMatchNotKey { //排除词未匹配,匹配附加词关键词
+														if RegMatch(fieldText, tr.AW[j].MatchType, tr.AW[j].KeyReg, nil, nil, false, true) && RegMatch(fieldText, tr.KW[j].MatchType, tr.KW[j].KeyReg, nil, nil, false, false) {
+															tagname := tr.TagNames[j]
+															//qu.Debug("tagname-----", tagname)
+															tagNameMap[tagname] = true
+															tagIdMap[tr.ID] = true
+														}
+													}
+												}
+											}
+										}
+										//tagname
+										tagNameArr := MapDataToArr(tagNameMap)
+										tagIdArr := MapDataToArr(tagIdMap)
+										if len(tagNameArr) > 0 {
+											tmp["tagname"] = strings.Join(tagNameArr, ",")
+											if c.PushModel == 2 {
+												tmp["item"] = strings.Join(tagNameArr, ",")
+											}
+											tmp["tagid"] = strings.Join(tagIdArr, ",")
+										}
+									}
+									//item
+									switch c.PushModel {
+									case 0:
+										tmp["item"] = "数据"
+									case 1:
+										tmp["item"] = dm.Name
+									case 2:
+										//tmp["item"] = sr.Name
+									case 3:
+										tmp["item"] = dm.Name + "_" + sr.Name
+									case 4:
+										tmp["item"] = sr.Name
+									}
+									//appid
+									tmp["appid"] = c.AppId
+									//部门名称
+									tmp["departname"] = dm.Name
+									tmp["departid"] = dm.ID
+									//存储数据
+									dm.DataLock.Lock()
+									//qu.Debug("tmp---", tmp)
+									tmpMap := map[string]interface{}{id: tmp}
+									dm.DepartmentData[sr.ID] = append(dm.DepartmentData[sr.ID], tmpMap)
+									dm.DataLock.Unlock()
+								} else {
+									qu.Debug("------------", id, IsMatch)
+								}
+							}
+						}(hit)
+						numDocs += 1
+						if numDocs%500 == 0 {
+							log.Println("Current:", numDocs)
+						}
+					}
+					scrollId = searchResult.ScrollId
+				}
+				wg.Wait()
+				client.ClearScroll().ScrollId(scrollId).Do() //清理游标
+				log.Println("SearchRule ID", sr.ID, "Result Data Count:", numDocs)
+			} else {
+				log.Println("Customer:", c.Name, "Departmnet", dm.Name, "TagName", sr.Name, "Es Search Data Error,Tag ID:", sr.ID)
+			}
+		}
+	}
+}
+
+//数据去重
+func (c *Customer) RemoveRepeatData() {
+	log.Println("开始数据去重...")
+	defer qu.Catch()
+	for _, dm := range c.Departments {
+		for _, dataMapArr := range dm.DepartmentData { //一个部门的所有数据
+			for _, dataMap := range dataMapArr {
+				for dataId, data := range dataMap {
+					tmp := data.(map[string]interface{})
+					if c.PushModel == 0 { //全局模式所有数据去重
+						if c.SaveDataMap[dataId] == nil {
+							c.SaveDataMap[dataId] = tmp
+						} else { //数据重复
+							cus_history := c.SaveDataMap[dataId]
+							MergeData(cus_history, tmp, c.IsTagRule, true) //合并字段
+							//c.SaveDataMap[dataId] = cus_history
+						}
+					} else if c.PushModel == 1 || c.PushModel == 2 || c.PushModel == 3 { //部门内部去重
+						if dm.SaveDataMap[dataId] == nil {
+							dm.SaveDataMap[dataId] = tmp
+						} else { //数据重复
+							dm_history := dm.SaveDataMap[dataId]
+							MergeData(dm_history, tmp, c.IsTagRule, false) //合并字段
+							//dm.SaveDataMap[dataId] = dm_history
+						}
+					} else if c.PushModel == 4 { //规则模式不去重
+						c.SaveDataArr = append(c.SaveDataArr, tmp)
+					}
+				}
+			}
+		}
+		//将部门数据清空
+		dm.DepartmentData = map[string][]map[string]interface{}{}
+	}
+}
+
+//组装保存数据
+func (c *Customer) AssembelAndSaveData() {
+	log.Println("开始组装保存数据...")
+	defer qu.Catch()
+	ch := make(chan bool, 10)
+	wg := &sync.WaitGroup{}
+	n := 0
+	if c.PushModel == 0 && len(c.SaveDataMap) > 0 {
+		for _, tmp := range c.SaveDataMap {
+			wg.Add(1)
+			ch <- true
+			go func(data map[string]interface{}) {
+				defer func() {
+					<-ch
+					wg.Done()
+				}()
+				AssembelSave(data, c.IsSearchHosp, c.IsSearchEnps)
+			}(tmp)
+			n++
+			if n%500 == 0 {
+				log.Println("Current:", n)
+			}
+		}
+		wg.Wait()
+	} else if c.PushModel == 1 || c.PushModel == 2 || c.PushModel == 3 {
+		for _, dm := range c.Departments {
+			if len(dm.SaveDataMap) > 0 {
+				for _, tmp := range dm.SaveDataMap {
+					wg.Add(1)
+					ch <- true
+					go func(data map[string]interface{}) {
+						defer func() {
+							<-ch
+							wg.Done()
+						}()
+						AssembelSave(data, c.IsSearchHosp, c.IsSearchEnps)
+					}(tmp)
+					n++
+					if n%500 == 0 {
+						log.Println("Current:", n)
+					}
+				}
+			}
+		}
+		wg.Wait()
+	} else if c.PushModel == 4 && len(c.SaveDataArr) > 0 {
+		for _, tmp := range c.SaveDataArr {
+			wg.Add(1)
+			ch <- true
+			go func(data map[string]interface{}) {
+				defer func() {
+					<-ch
+					wg.Done()
+				}()
+				AssembelSave(data, c.IsSearchHosp, c.IsSearchEnps)
+			}(tmp)
+			n++
+			if n%500 == 0 {
+				log.Println("Current:", n)
+			}
+		}
+		wg.Wait()
+	}
+	log.Println("数据保存完毕...		Save Number:", n)
+}
+
+//获取用户所有规则
+func (d *Department) GetSearchRules(cid, stype string, idRange bson.M, searchRules []map[string]interface{}) {
+	defer qu.Catch()
+	if len(searchRules) > 0 {
+		for _, sr := range searchRules {
+			SR := &SearchRule{}
+			SR.Fields = make(map[string]interface{})
+
+			id := qu.BsonIdToSId(sr["_id"])
+			name := qu.ObjToString(sr["s_name"])
+			SR.ID = id
+			SR.Name = name
+			SR.CustomerID = cid
+			SR.DepartmentID = d.ID
+			SR.ExtFieldType = qu.IntAll(sr["i_extfieldstype"])
+			//SR.RuleData = &sync.Map{}
+			esquery := qu.ObjToString(sr["s_esquery"])
+			clearKey := qu.ObjToString(sr["s_globalclearkey"])
+			clearKeyMatch := qu.ObjToString(sr["s_globalclearkeymatch"])
+
+			//获取es
+			if stype == "history" {
+				SR.EsQuery = esquery
+			} else {
+				SR.GetEs(d.Name, esquery, idRange)
+			}
+			//获取关键词和附加词
+			if o_rules, ok := sr["o_rules"].(primitive.A); ok && len(o_rules) > 0 {
+				SR.GetKeyAddWord(o_rules)
+			}
+			//获取全局清理词
+			SR.GetClearWord(clearKey, clearKeyMatch)
+			d.Rules = append(d.Rules, SR)
+		}
+	}
+}
+
+//获取转换后的es语句
+func (sr *SearchRule) GetEs(department, esquery string, tmpRange bson.M) {
+	defer qu.Catch()
+	query := map[string]*QueryObjecct{}
+	if json.Unmarshal([]byte(esquery), &query) == nil {
+		qb := query["query"]
+		filter := qb.Filtered.Filter
+		if filter != nil && filter.Bool != nil { //有filter
+			index := -1 //记录range的位置
+			for i, m := range filter.Bool.Must {
+				mMap := m.(map[string]interface{})
+				if esRange, ok := mMap["range"].(map[string]interface{}); ok && esRange != nil { //有range
+					if esRange["publishtime"] != nil {
+						index = i
+						break
+					}
+				}
+			}
+			if index > -1 {
+				filter.Bool.Must[index] = tmpRange
+			} else {
+				filter.Bool.Must = append(filter.Bool.Must, tmpRange)
+			}
+		} else { //无filter则添加
+			bo := &BoolObject{}
+			bo.Must = append(bo.Must, tmpRange)
+			tmpFilter := &Filter{
+				Bool: bo,
+			}
+			qb.Filtered.Filter = tmpFilter
+		}
+		strquery, err := json.Marshal(query)
+		if err == nil {
+			sr.EsQuery = string(strquery)
+		} else {
+			log.Println("Department:", department, "Es Error,Tag ID:", sr.ID)
+		}
+	} else {
+		log.Println("Department:", department, "Es Error,Tag ID:", sr.ID)
+	}
+}
+
+//全局清理词处理
+func (sr *SearchRule) GetClearWord(key, match string) {
+	defer qu.Catch()
+	//匹配方式
+	cwmArr := []string{}
+	for _, mv := range strings.Split(match, ",") {
+		if field := qu.ObjToString(MatchType[mv]); field != "" {
+			cwmArr = append(cwmArr, field)
+			sr.Fields[field] = true
+		}
+	}
+	//清理词正则
+	cwkArr := []*regexp.Regexp{}
+	for _, kv := range strings.Split(key, ",") {
+		if LetterCase.MatchString(kv) { //字母转大写
+			kv = strings.ToUpper(kv)
+		}
+		reg := regexp.MustCompile(kv)
+		cwkArr = append(cwkArr, reg)
+	}
+	cw := &ClearWord{
+		KeyReg:    cwkArr,
+		MatchType: cwmArr,
+	}
+	sr.GCW = cw
+}
+
+//关键词、附加词处理
+func (sr *SearchRule) GetKeyAddWord(o_rules primitive.A) {
+	defer qu.Catch()
+	kw, aw, _, _ := GetNotkeyAndKeyAddWord(o_rules, sr.Fields, false)
+	sr.KW = kw
+	sr.AW = aw
+	//sr.Fields = fields
+}
+
+//排除词、关键词、附加词处理
+func (tr *TagRule) GetKeyAddNotKeyWord(o_list primitive.A) {
+	defer qu.Catch()
+	kw, aw, nkw, tagnames := GetNotkeyAndKeyAddWord(o_list, tr.Fields, true)
+	tr.NW = nkw
+	tr.KW = kw
+	tr.AW = aw
+	//tr.Fields = fields
+	tr.TagNames = tagnames
+}

+ 762 - 0
src/history/util_history.go

@@ -0,0 +1,762 @@
+package history
+
+import (
+	"log"
+	qu "qfw/util"
+	"regexp"
+	"strings"
+	"time"
+	. "util"
+
+	"github.com/donnie4w/go-logger/logger"
+	"go.mongodb.org/mongo-driver/bson/primitive"
+	"gopkg.in/mgo.v2/bson"
+)
+
+var LetterCase = regexp.MustCompile("[A-Za-z]")
+var LetterCase2 = regexp.MustCompile("[A-Za-z0-9]")
+var FilteReg = regexp.MustCompile("[()(){}]*")
+
+//匹配方式map
+var MatchType = map[string]interface{}{
+	"1": "title",
+	"2": "detail",
+	"3": "purchasing",
+	"4": "filetext",
+	"5": "projectname",
+	"6": "buyer",
+	"7": "s_winner",
+}
+
+//加载排除词、附加词、关键词
+func GetNotkeyAndKeyAddWord(list primitive.A, fieldMap map[string]interface{}, hasNotKey bool) (kws []*KeyWord, aws []*AddWord, nkws []*NotKeyWord, tagnames []string) {
+	defer qu.Catch()
+	for _, rules := range list {
+		ruleMap := rules.(map[string]interface{})
+		if hasNotKey { //是否处理排除词
+			//排除词匹配方式
+			nkm := qu.ObjToString(ruleMap["s_notkeymatch"])
+			nkmArr := []string{}
+			for _, nv := range strings.Split(nkm, ",") {
+				if field := qu.ObjToString(MatchType[nv]); field != "" {
+					nkmArr = append(nkmArr, field)
+					fieldMap[field] = true
+				}
+			}
+			//排除词
+			nkw := &NotKeyWord{}
+			nkw.MatchType = nkmArr
+			notkeyword := qu.ObjToString(ruleMap["s_notkey"])
+			nkw_commaArr := strings.Split(notkeyword, ",")
+			nkw_reg := &Reg{}
+			for _, comma := range nkw_commaArr {
+				nkw_addArr := strings.Split(comma, "&&")
+				if len(nkw_addArr) == 1 { //,
+					tmp_nkw := nkw_addArr[0]
+					if tmp_nkw != "" {
+						cr := &CommonReg{}
+						cr.CsVal = tmp_nkw                   //记录原值
+						if LetterCase.MatchString(tmp_nkw) { //判断附加词中是否有英文
+							tmp_nkw = strings.ToUpper(tmp_nkw) //附加词中有英文全部转为大写
+							cr.IsLetter = true                 //含字母
+						}
+						cr.CrVal = regexp.MustCompile(tmp_nkw) //记录reg值
+						nkw_reg.CReg = append(nkw_reg.CReg, cr)
+					}
+				} else { //&&
+					arp := &AndRegPre{}
+					arp.CsVal = comma //记录原值
+					for _, and := range nkw_addArr {
+						if and != "" {
+							ar := &AndReg{}
+							if LetterCase.MatchString(and) { //判断附加词中是否有英文
+								and = strings.ToUpper(and) //附加词中有英文全部转为大写
+								ar.IsLetter = true         //含字母
+							}
+							ar.CrVal = regexp.MustCompile(and)
+							arp.AndRegKid = append(arp.AndRegKid, ar)
+						}
+					}
+					nkw_reg.AReg = append(nkw_reg.AReg, arp)
+				}
+			}
+			nkw.KeyReg = nkw_reg
+			nkws = append(nkws, nkw)
+			//获取每组关键词的标签名称
+			tagname := qu.ObjToString(ruleMap["s_tagname"])
+			tagnames = append(tagnames, tagname)
+		}
+		//附加词匹配方式
+		awm := qu.ObjToString(ruleMap["s_addkeymatch"])
+		awmArr := []string{}
+		for _, av := range strings.Split(awm, ",") {
+			if field := qu.ObjToString(MatchType[av]); field != "" {
+				awmArr = append(awmArr, field)
+				fieldMap[field] = true
+			}
+		}
+		//附加词
+		aw := &AddWord{}
+		aw.MatchType = awmArr
+		aw_reg := &Reg{}
+		addword := qu.ObjToString(ruleMap["s_addkey"])
+		aw_commaArr := strings.Split(addword, ",")
+		for _, comma := range aw_commaArr {
+			aw_addArr := strings.Split(comma, "&&")
+			if len(aw_addArr) == 1 { //,
+				tmp_aw := aw_addArr[0]
+				if tmp_aw != "" {
+					cr := &CommonReg{}
+					cr.CsVal = tmp_aw                   //记录原值
+					if LetterCase.MatchString(tmp_aw) { //判断附加词中是否有英文
+						tmp_aw = strings.ToUpper(tmp_aw) //附加词中有英文全部转为大写
+						cr.IsLetter = true               //含字母
+					}
+					cr.CrVal = regexp.MustCompile(tmp_aw) //记录reg值
+					aw_reg.CReg = append(aw_reg.CReg, cr)
+				}
+			} else { //&&
+				arp := &AndRegPre{}
+				arp.CsVal = comma //记录原值
+				for _, and := range aw_addArr {
+					if and != "" {
+						ar := &AndReg{}
+						if LetterCase.MatchString(and) { //判断附加词中是否有英文
+							and = strings.ToUpper(and) //附加词中有英文全部转为大写
+							ar.IsLetter = true         //含字母
+						}
+						ar.CrVal = regexp.MustCompile(and)
+						arp.AndRegKid = append(arp.AndRegKid, ar)
+					}
+				}
+				aw_reg.AReg = append(aw_reg.AReg, arp)
+			}
+		}
+		aw.KeyReg = aw_reg
+		aws = append(aws, aw)
+		//关键词匹配方式
+		kwm := qu.ObjToString(ruleMap["s_keymatch"])
+		kwmArr := []string{}
+		for _, kv := range strings.Split(kwm, ",") {
+			if field := qu.ObjToString(MatchType[kv]); field != "" {
+				kwmArr = append(kwmArr, field)
+				fieldMap[field] = true
+			}
+		}
+		//关键词
+		kw := &KeyWord{}
+		kw.MatchType = kwmArr
+		kw_reg := &Reg{}
+		keyword := qu.ObjToString(ruleMap["s_matchkey"])
+		kw_commaArr := strings.Split(keyword, ",")
+		for _, comma := range kw_commaArr {
+			kw_addArr := strings.Split(comma, "&&")
+			if len(kw_addArr) == 1 { //,
+				tmp_kw := kw_addArr[0]
+				if tmp_kw != "" {
+					cr := &CommonReg{}
+					cr.CsVal = tmp_kw //记录原值
+					if LetterCase.MatchString(tmp_kw) {
+						tmp_kw = strings.ToUpper(tmp_kw)
+						cr.IsLetter = true //含字母
+					}
+					cr.CrVal = regexp.MustCompile(tmp_kw) //记录reg值
+					kw_reg.CReg = append(kw_reg.CReg, cr)
+				}
+			} else { //&&
+				arp := &AndRegPre{}
+				arp.CsVal = comma //记录原值
+				for _, and := range kw_addArr {
+					if and != "" {
+						ar := &AndReg{}
+						if LetterCase.MatchString(and) {
+							and = strings.ToUpper(and) //附加词中有英文全部转为大写
+							ar.IsLetter = true         //含字母
+						}
+						ar.CrVal = regexp.MustCompile(and)
+						arp.AndRegKid = append(arp.AndRegKid, ar)
+					}
+				}
+				kw_reg.AReg = append(kw_reg.AReg, arp)
+			}
+		}
+		kw.KeyReg = kw_reg
+		kws = append(kws, kw)
+	}
+	return
+}
+
+//根据时间获取起始和终止ID范围
+func GetIdRange() (bson.M, bool) {
+	defer qu.Catch()
+	now := time.Now().Unix()
+	for { //当前时间一直向前推半小时,直到取到数据
+		now = now - 600 //10分钟前
+		endTime := time.Unix(now, 0)
+		endId := bson.NewObjectIdWithTime(endTime).Hex()
+		if endId > LatestId {
+			esquery := `{"query": {"bool": {"must": [{"range": {"id": {"gt": "` + LatestId + `" , "lte": "` + endId + `"}}}]}}, "sort": [{"comeintime": "desc"}]}`
+			if Es.Count(Index, Itype, esquery) > 0 { //有数据返回id区间
+				list := Es.Get(Index, Itype, esquery)
+				tmpRange := bson.M{
+					"range": bson.M{
+						"id": bson.M{
+							"lte": endId,
+							"gt":  LatestId,
+						},
+					},
+				}
+				LatestId = qu.ObjToString((*list)[0]["_id"])
+				return tmpRange, true
+			}
+		} else { //结束id不大于起始id 退出
+			logger.Debug("Search End ID Range Error. Sid:", LatestId, "Eid:", endId)
+			break
+		}
+	}
+	return bson.M{}, false
+	// now := time.Now()
+	// end := now.Unix() - int64(60*now.Minute()) - int64(now.Second())
+	// start := end - TaskTime*3600
+	// endTime := time.Unix(end, 0)
+	// startTime := time.Unix(start, 0)
+	// eid := bson.NewObjectIdWithTime(endTime).Hex()
+	// sid := bson.NewObjectIdWithTime(startTime).Hex()
+
+	// query := bson.M{
+	// 	"_id": bson.M{
+	// 		"$gt": mongodb.StringTOBsonId(LatestId),
+	// 	},
+	// }
+	// sort := bson.M{
+	// 	"_id": -1,
+	// }
+	// fields := bson.M{
+	// 	"_id": 1,
+	// }
+	// //查抽取表最后一个id
+	// extData, err := MgoExt.FindByLimit(ExtColl, query, sort, fields, 0, 1)
+	// if len(extData) == 1 && err == nil {
+	// 	endId := mongodb.BsonTOStringId(extData[0]["_id"])
+	// 	if endId > LatestId {
+	// 		tmpRange := bson.M{
+	// 			"range": bson.M{
+	// 				"id": bson.M{
+	// 					"lte": endId,
+	// 					"gt":  LatestId,
+	// 				},
+	// 			},
+	// 		}
+	// 		LatestId = endId
+	// 		return tmpRange, true
+	// 	} else {
+	// 		logger.Debug("ID Range Error,Start ID:", LatestId, "End ID:", endId)
+	// 		return bson.M{}, false
+	// 	}
+	// }
+	// logger.Debug("Search End ID No Data", query, "Error:", err)
+
+}
+
+//处理文本
+func ProcessData(text string) string {
+	defer qu.Catch()
+	text = strings.ToUpper(text)               //文本中的英文全转为大写
+	text = FilteReg.ReplaceAllString(text, "") //去除一些特殊符号
+	return text
+}
+
+//校验字母
+func CheckLetter(text string, reg *regexp.Regexp, indexArr [][]int) (flag bool) {
+	defer qu.Catch()
+	for _, tmpArr := range indexArr {
+		sIndex := tmpArr[0]
+		eIndex := tmpArr[1]
+		sbyte := ""
+		ebyte := ""
+		//log.Println("---", sIndex, eIndex)
+		if sIndex != 0 {
+			sbyte = text[sIndex-1 : sIndex]
+			if eIndex != len(text) { //BAIB
+				ebyte = text[eIndex : eIndex+1]
+			} /*else { //BAI
+
+			}*/
+		} else {
+			if eIndex != len(text) { //AIB
+				ebyte = text[eIndex : eIndex+1]
+			} /*else { //AI
+
+			}*/
+		}
+		//log.Println("sssss", "s:", sbyte, "e:", ebyte, LetterCase2.Match([]byte(sbyte)), LetterCase2.Match([]byte(ebyte)))
+		if !LetterCase2.Match([]byte(sbyte)) && !LetterCase2.Match([]byte(ebyte)) {
+			flag = true
+			break
+		}
+	}
+	return
+}
+
+//匹配
+func RegMatch(fieldText map[string]interface{}, matchType []string, matchReg *Reg, matchKey map[string]bool, matchKeyType map[string]bool, goon, isAddWord bool) (match bool) {
+	defer qu.Catch()
+	if len(matchType) == 0 && isAddWord { //特殊处理附加词为空的情况
+		match = true
+		return
+	}
+	for _, mt := range matchType {
+		if text := qu.ObjToString(fieldText[mt]); text != "" {
+			for _, cr := range matchReg.CReg { //逗号分隔,任意一个匹配表示匹配成功
+				if goon && matchKey[cr.CsVal] { //matchkey已存在不在匹配
+					continue
+				}
+				if indexArr := cr.CrVal.FindAllStringIndex(text, -1); len(indexArr) > 0 { //匹配成功
+					if !cr.IsLetter { //reg无字母
+						if goon {
+							matchKey[cr.CsVal] = true
+							matchKeyType[mt] = true
+							match = true
+						} else {
+							match = true
+							return
+						}
+					} else if cr.IsLetter && CheckLetter(text, cr.CrVal, indexArr) { //reg有字母,判断是否是包含关系(AAAIBBB or AI){//
+						if goon {
+							matchKey[cr.CsVal] = true
+							matchKeyType[mt] = true
+							match = true
+						} else {
+							match = true
+							return
+						}
+					}
+				}
+			}
+			for _, ar := range matchReg.AReg { //&&分割,所有匹配表示匹配成功
+				if goon && matchKey[ar.CsVal] {
+					continue
+				}
+				IsAregMatch := false
+				for n, arc := range ar.AndRegKid { //ar.AndRegKid若有值必不小于2
+					if indexArr := arc.CrVal.FindAllStringIndex(text, -1); len(indexArr) < 1 { //匹配失败(ar.AndRegKid中任意一个未匹配则失败)
+						break
+					} else { //匹配成功,判断字母
+						if arc.IsLetter && !CheckLetter(text, arc.CrVal, indexArr) { //reg有字母,判断是否是包含关系(AAAIBBB or AI)
+							break
+						}
+					}
+					if n == len(ar.AndRegKid)-1 {
+						IsAregMatch = true
+					}
+				}
+				if IsAregMatch {
+					if goon {
+						matchKey[ar.CsVal] = true
+						matchKeyType[mt] = true
+						match = true
+					} else {
+						match = true
+						return
+					}
+				}
+			}
+		}
+	}
+	return
+}
+
+//map数据转数组
+func MapDataToArr(tmpMap map[string]bool) (tmpArr []string) {
+	for tm, _ := range tmpMap {
+		tmpArr = append(tmpArr, tm)
+	}
+	return
+}
+
+//合并数据
+func MergeData(history, tmp map[string]interface{}, isTagRule, isDepartRmvRep bool) {
+	//matchkey、matchtype、ruleid均非空
+	matchkey1 := qu.ObjToString(history["matchkey"])
+	matchkey2 := qu.ObjToString(tmp["matchkey"])
+	history["matchkey"] = MergeField(matchkey1, matchkey2)
+
+	matchkeytype1 := qu.ObjToString(history["matchtype"])
+	matchkeytype2 := qu.ObjToString(tmp["matchtype"])
+	history["matchtype"] = MergeField(matchkeytype1, matchkeytype2)
+
+	ruleid1 := qu.ObjToString(history["ruleid"])
+	ruleid2 := qu.ObjToString(tmp["ruleid"])
+	history["ruleid"] = MergeField(ruleid1, ruleid2)
+
+	rulename1 := qu.ObjToString(history["rulename"])
+	rulename2 := qu.ObjToString(tmp["rulename"])
+	history["rulename"] = MergeField(rulename1, rulename2)
+
+	if isTagRule { //标签模式 tagname、tagid合并
+		tagname1 := qu.ObjToString(history["tagname"])
+		tagname2 := qu.ObjToString(tmp["tagname"])
+		if tagNameResult := MergeField(tagname1, tagname2); tagNameResult != "" {
+			history["tagname"] = tagNameResult
+		}
+
+		tagid1 := qu.ObjToString(history["tagid"])
+		tagid2 := qu.ObjToString(tmp["tagid"])
+		if tagIdResult := MergeField(tagid1, tagid2); tagIdResult != "" {
+			history["tagid"] = tagIdResult
+		}
+	}
+	if isDepartRmvRep { //全局模式 部门合并
+		departname1 := qu.ObjToString(history["departname"])
+		departname2 := qu.ObjToString(tmp["departname"])
+		history["departname"] = MergeField(departname1, departname2)
+		departid1 := qu.ObjToString(history["departid"])
+		departid2 := qu.ObjToString(tmp["departid"])
+		history["departid"] = MergeField(departid1, departid2)
+	}
+}
+
+//合并字段
+func MergeField(str1, str2 string) string {
+	if str1 == "" {
+		return str2
+	} else if str2 == "" {
+		return str1
+	}
+	slice1 := strings.Split(str1, ",")
+	slice2 := strings.Split(str2, ",")
+	m := make(map[string]int)
+	for _, v := range slice1 {
+		m[v]++
+	}
+
+	for _, v := range slice2 {
+		times, _ := m[v]
+		if times == 0 {
+			slice1 = append(slice1, v)
+		}
+	}
+	return strings.Join(slice1, ",")
+}
+
+//补充信息并保存
+func AssembelSave(tmp map[string]interface{}, IsSearchHosp, IsSearchEnps bool) {
+	if IsSearchHosp { //医院信息
+		SearchHospInfo(tmp)
+	}
+	if IsSearchEnps { //企业信息
+		SearchEnterpriseInfo(tmp)
+	}
+	tmp["createtime"] = time.Now().Unix()
+	id := qu.ObjToString(tmp["id"])
+	tmp["jybxhref"] = `https://www.jianyu360.com/article/content/` + qu.CommonEncodeArticle("content", id) + `.html`
+	if publishtime, ok := tmp["publishtime"].(float64); ok && publishtime > 0 {
+		tmp["publishtime"] = qu.Int64All(publishtime)
+	}
+	if bidopentime, ok := tmp["bidopentime"].(float64); ok && bidopentime > 0 {
+		tmp["bidopentime"] = qu.Int64All(bidopentime)
+	}
+	MgoSaveCache <- tmp
+}
+
+//查询第三方医院等级信息和招标客户的社会征信代码
+func SearchHospInfo(tmp map[string]interface{}) {
+	if buyer := qu.ObjToString(tmp["buyer"]); buyer != "" { //buyer存在
+		//医院等级
+		hospData := MgoBuyer.FindOne(HospColl, bson.M{"name": buyer})
+		if hospData != nil && len(hospData) > 0 {
+			if rank := qu.ObjToString(hospData["rank"]); rank != "" {
+				tmp["rank"] = hospData["rank"]
+			}
+		}
+		//招标客户的社会征信代码
+		buyerEntData := MgoBuyer.FindOne(BuyerEntColl, bson.M{"company_name": buyer})
+		if buyerEntData != nil && len(buyerEntData) > 0 {
+			if credit_no := qu.ObjToString(buyerEntData["credit_no"]); credit_no != "" {
+				tmp["buyer_credit_no"] = credit_no
+			}
+		}
+	}
+}
+
+//查询第三方中标企业信息
+func SearchEnterpriseInfo(tmp map[string]interface{}) {
+	if s_winner := qu.ObjToString(tmp["s_winner"]); s_winner != "" { //buyer存在
+		winner := strings.Split(s_winner, ",")[0] //取第一个中标单位
+		data := MgoEnps.FindOne(EnpsColl, bson.M{"company_name": winner})
+		if data != nil && len(data) > 0 {
+			//中标单位联系人
+			if legal_person := qu.ObjToString(data["legal_person"]); legal_person != "" {
+				tmp["legal_person"] = legal_person
+			}
+			//中标单位的注册地址
+			if company_address := qu.ObjToString(data["company_address"]); company_address != "" {
+				tmp["company_address"] = company_address
+			}
+			//注册资金"capital" : "324 万","capital" : 124.8,
+			if capital := qu.ObjToString(data["capital"]); capital != "" {
+				tmp["capital"] = capital
+				//if capitalStr, ok := data["capital"].(string); ok && capitalStr != "" {
+				//	if capital := ObjToMoney(capitalStr); capital != 0 { //金额转换
+				//		tmp["capital"] = capital
+				//	}
+				//} else if capitalFloat, ok := data["capital"].(float64); ok && capitalFloat != 0 {
+				//	tmp["capital"] = capitalFloat
+				//}
+			}
+			//注册时间"establish_date" : ISODate("1949-10-01T00:00:00.000+0000")
+			if data["establish_date"] != nil {
+				if establish_date, ok := data["establish_date"].(primitive.DateTime); ok {
+					t := establish_date.Time()
+					tmp["establish_date"] = qu.FormatDate(&t, qu.Date_Short_Layout)
+				}
+			}
+			//经营范围
+			if business_scope := qu.ObjToString(data["business_scope"]); business_scope != "" {
+				tmp["business_scope"] = business_scope
+			}
+			//中标单位的社会征信代码
+			if credit_no := qu.ObjToString(data["credit_no"]); credit_no != "" {
+				tmp["winner_credit_no"] = credit_no
+			}
+			//股东名单
+			if partners, ok := data["partners"].(primitive.A); ok && len(partners) > 0 {
+				stock_name_arr := []string{}
+				for _, partner := range partners {
+					p := partner.(map[string]interface{})
+					if stock_name := qu.ObjToString(p["stock_name"]); stock_name != "" {
+						stock_name_arr = append(stock_name_arr, stock_name)
+					}
+				}
+				if len(stock_name_arr) > 0 {
+					tmp["stock_name"] = strings.Join(stock_name_arr, ",")
+				}
+			}
+			//从最新年报中获取 中标单位联系电话、中标单位邮箱
+			if annual_reports, ok := data["annual_reports"].(primitive.A); ok && len(annual_reports) > 0 {
+				if anreport, ok := annual_reports[0].(map[string]interface{}); ok { //最新年报
+					if email := qu.ObjToString(anreport["company_email"]); email != "" {
+						tmp["company_email"] = email
+					}
+					if phone := qu.ObjToString(anreport["company_phone"]); phone != "" {
+						tmp["company_phone"] = phone
+					}
+				}
+			}
+		}
+	}
+}
+
+//数据存库
+func SaveMgo() {
+	log.Println("Mgo Save...")
+	arru := make([]map[string]interface{}, 500)
+	indexu := 0
+	for {
+		select {
+		case v := <-MgoSaveCache:
+			arru[indexu] = v
+			indexu++
+			if indexu == 500 {
+				SP <- true
+				go func(arru []map[string]interface{}) {
+					defer func() {
+						<-SP
+					}()
+					MgoSave.SaveBulk(SaveColl, arru...)
+				}(arru)
+				arru = make([]map[string]interface{}, 500)
+				indexu = 0
+			}
+		case <-time.After(1000 * time.Millisecond):
+			if indexu > 0 {
+				SP <- true
+				go func(arru []map[string]interface{}) {
+					defer func() {
+						<-SP
+					}()
+					MgoSave.SaveBulk(SaveColl, arru...)
+				}(arru[:indexu])
+				arru = make([]map[string]interface{}, 500)
+				indexu = 0
+			}
+		}
+	}
+}
+
+//打印初始化信息
+func PrintLog(cus *Customer) {
+	qu.Debug("----------------------打标签规则----------------------------")
+	for i, tr := range cus.TagRules {
+		qu.Debug("tagrules:", i, tr.ID, tr.Name, tr.CustomerId, tr.DepartRuleIds, tr.Fields)
+		for j, kw := range tr.KW {
+			qu.Debug("kw_matchkey---", kw.MatchType, len(kw.MatchType))
+			qu.Debug("------------CReg--------------")
+			for ck1, cr := range kw.KeyReg.CReg {
+				qu.Debug("CR---", ck1, cr.CrVal, cr.CsVal, cr.IsLetter)
+			}
+			qu.Debug("------------AReg--------------")
+			for ck1, ar := range kw.KeyReg.AReg {
+				qu.Debug("AR---", ck1, ar.CsVal)
+				for _, arc := range ar.AndRegKid {
+					qu.Debug("ARC---", arc.CrVal, arc.IsLetter)
+				}
+			}
+			qu.Debug("+++++++++++++++++++++AW++++++++++++++++++++++")
+			qu.Debug("aw_matchkey---", tr.AW[j].MatchType, len(tr.AW[j].MatchType))
+			qu.Debug("------------CReg--------------")
+			for ck2, cr := range tr.AW[j].KeyReg.CReg {
+				qu.Debug("CR---", ck2, cr.CrVal, cr.CsVal, cr.IsLetter)
+			}
+			qu.Debug("------------AReg--------------")
+			for ck2, ar := range tr.AW[j].KeyReg.AReg {
+				qu.Debug("AR---", ck2, ar.CsVal)
+				for _, arc := range ar.AndRegKid {
+					qu.Debug("ARC---", arc.CrVal, arc.IsLetter)
+				}
+			}
+			qu.Debug("++++++++++++++++++++NW+++++++++++++++++++++++")
+			qu.Debug("nw_matchkey---", tr.NW[j].MatchType, len(tr.NW[j].MatchType))
+			qu.Debug("------------CReg--------------")
+			for ck1, cr := range tr.NW[j].KeyReg.CReg {
+				qu.Debug("CR---", ck1, cr.CrVal, cr.CsVal, cr.IsLetter)
+			}
+			qu.Debug("------------AReg--------------")
+			for ck1, ar := range tr.NW[j].KeyReg.AReg {
+				qu.Debug("AR---", ck1, ar.CsVal)
+				for _, arc := range ar.AndRegKid {
+					qu.Debug("ARC---", arc.CrVal, arc.IsLetter)
+				}
+			}
+			qu.Debug("tagname-------------", tr.TagNames[j])
+			qu.Debug("-------------------------------------------------")
+		}
+	}
+	qu.Debug("----------------------查询规则----------------------------")
+	for i, dm := range cus.Departments {
+		qu.Debug("deparment:", i, dm.ID, dm.Name, dm.CustomerID)
+		for j, sr := range dm.Rules {
+			qu.Debug("ck---", j, sr.ID, sr.Name, sr.CustomerID, sr.DepartmentID, sr.GCW.KeyReg, len(sr.GCW.KeyReg), sr.GCW.MatchType, len(sr.GCW.MatchType), sr.Fields, len(sr.Fields), sr.EsQuery)
+			for j, kw := range sr.KW {
+				qu.Debug("kw_matchkey---", kw.MatchType, len(kw.MatchType))
+				qu.Debug("------------CReg--------------")
+				for ck1, cr := range kw.KeyReg.CReg {
+					qu.Debug("CR---", ck1, cr.CrVal, cr.CsVal, cr.IsLetter)
+				}
+				qu.Debug("------------AReg--------------")
+				for ck1, ar := range kw.KeyReg.AReg {
+					qu.Debug("AR---", ck1, ar.CsVal)
+					for _, arc := range ar.AndRegKid {
+						qu.Debug("ARC---", arc.CrVal, arc.IsLetter)
+					}
+				}
+				qu.Debug("+++++++++++++++++++++AW++++++++++++++++++++++")
+				qu.Debug("aw_matchkey---", sr.AW[j].MatchType, len(sr.AW[j].MatchType))
+				qu.Debug("------------CReg--------------")
+				for ck2, cr := range sr.AW[j].KeyReg.CReg {
+					qu.Debug("CR---", ck2, cr.CrVal, cr.CsVal, cr.IsLetter)
+				}
+				qu.Debug("------------AReg--------------")
+				for ck2, ar := range sr.AW[j].KeyReg.AReg {
+					qu.Debug("AR---", ck2, ar.CsVal)
+					for _, arc := range ar.AndRegKid {
+						qu.Debug("ARC---", arc.CrVal, arc.IsLetter)
+					}
+				}
+				qu.Debug("--------------------------------------------------------------------------------------")
+			}
+		}
+	}
+}
+
+//匹配
+func RegMatchTest(fieldText map[string]interface{}, matchType []string, matchReg *Reg, matchKey map[string]bool, matchKeyType map[string]bool, goon, isAddWord bool) (match bool) {
+	defer qu.Catch()
+	qu.Debug("matchType---", matchType)
+	if len(matchType) == 0 && isAddWord { //特殊处理附加词为空的情况
+		match = true
+		return
+	}
+	for _, mt := range matchType {
+		if text := qu.ObjToString(fieldText[mt]); text != "" {
+			qu.Debug("匹配方式---", mt, "text---", text)
+			qu.Debug("--------------开始查找逗号分隔----------------", len(matchReg.CReg))
+			for i, cr := range matchReg.CReg { //逗号分隔,任意一个匹配表示匹配成功
+				qu.Debug("iiii---", i, cr.CrVal, goon, matchKey)
+				if goon && matchKey[cr.CsVal] { //matchkey已存在不在匹配
+					continue
+				}
+				if indexArr := cr.CrVal.FindAllStringIndex(text, -1); len(indexArr) > 0 { //匹配成功
+					if !cr.IsLetter { //reg无字母
+						qu.Debug("goon---", goon)
+						if goon {
+							qu.Debug("key++++++++++ ++++++++++", cr.CsVal)
+							matchKey[cr.CsVal] = true
+							matchKeyType[mt] = true
+							match = true
+						} else {
+							match = true
+							return
+						}
+					} else if cr.IsLetter && CheckLetter(text, cr.CrVal, indexArr) { //reg有字母,判断是否是包含关系(AAAIBBB or AI){//
+						qu.Debug("goon---", goon)
+						if goon {
+							qu.Debug("key++++++++++++++++++++", cr.CsVal)
+							matchKey[cr.CsVal] = true
+							matchKeyType[mt] = true
+							match = true
+						} else {
+							match = true
+							return
+						}
+					}
+				}
+			}
+			qu.Debug("--------------开始查找&&分隔----------------", len(matchReg.AReg))
+			for j, ar := range matchReg.AReg { //&&分割,所有匹配表示匹配成功
+				qu.Debug("jjjj---", j, ar.CsVal, goon, matchKey)
+				if goon && matchKey[ar.CsVal] {
+					continue
+				}
+				IsAregMatch := false
+				qu.Debug("ar.AndRegKid---", j, ar.AndRegKid, len(ar.AndRegKid))
+				for n, arc := range ar.AndRegKid { //ar.AndRegKid若有值必不小于2
+					qu.Debug("nnnn---", n, arc.CrVal, arc.IsLetter)
+					if indexArr := arc.CrVal.FindAllStringIndex(text, -1); len(indexArr) < 1 { //匹配失败(ar.AndRegKid中任意一个未匹配则失败)
+						break
+					} else { //匹配成功,判断字母
+						if arc.IsLetter && !CheckLetter(text, arc.CrVal, indexArr) { //reg有字母,判断是否是包含关系(AAAIBBB or AI)
+							break
+						}
+					}
+					if n == len(ar.AndRegKid)-1 {
+						IsAregMatch = true
+					}
+				}
+				qu.Debug("IsAregMatch---", IsAregMatch)
+				if IsAregMatch {
+					qu.Debug("goon---", goon)
+					if goon {
+						qu.Debug("key++++++++++++++++++++", ar.CsVal)
+						matchKey[ar.CsVal] = true
+						matchKeyType[mt] = true
+						match = true
+					} else {
+						match = true
+						return
+					}
+				}
+			}
+		}
+	}
+	return
+}
+
+//发布时间不在范围内(7天)不要这条数据
+func SkipData(tmp map[string]interface{}) bool {
+	comeIn := qu.Int64All(tmp["comeintime"])
+	if qu.Int64All(tmp["publishtime"]) > (comeIn - 7*24*60*60) {
+		return true
+	}
+	return false
+}

+ 3 - 0
src/main.go

@@ -3,6 +3,7 @@ package main
 import (
 	_ "filter"
 	"front"
+	"history"
 	"log"
 	qu "qfw/util"
 	"service"
@@ -32,8 +33,10 @@ func init() {
 	xweb.AddAction(&service.Feedback{})
 	xweb.AddAction(&service.Private{})
 	xweb.AddAction(&service.Customer{})
+	xweb.AddAction(&history.HistoryData{})
 	xweb.RootApp().AppConfig.SessionTimeout = 1 * time.Hour
 	xweb.RootApp().Logger.SetOutputLevel(1)
+	go history.SaveMgo()
 }
 
 func main() {

+ 66 - 0
src/service/customer_service.go

@@ -7,6 +7,7 @@ import (
 	"github.com/lauyoume/gopinyin"
 	"gopkg.in/mgo.v2/bson"
 	"io/ioutil"
+	"log"
 	"net/http"
 	qu "qfw/util"
 	"strings"
@@ -22,6 +23,7 @@ type Customer struct {
 	customerEuser   xweb.Mapper `xweb:"/service/customer/euser"`
 	customerSave   	xweb.Mapper `xweb:"/service/customer/save"`
 	customerDel    	xweb.Mapper `xweb:"/service/customer/del"`
+	customerHistory	xweb.Mapper `xweb:"/service/customer/history"`
 	setupDepart    	xweb.Mapper `xweb:"/service/customer/setup"`
 
 	departDel 		xweb.Mapper `xweb:"/service/depart/del"`
@@ -50,6 +52,8 @@ type Customer struct {
 	tagDel			xweb.Mapper `xweb:"/service/euser/tag/del"`
 	cuserRuleList	xweb.Mapper `xweb:"/service/tag/ruleList"`			//规则列表	客户
 	tagAssociated	xweb.Mapper `xweb:"/service/euser/tag/associated"`   //私有标签关联规则
+	customerAllRule	xweb.Mapper `xweb:"/service/customer/history/allrule"`   //用户历史规则
+	saveHistoryRule	xweb.Mapper `xweb:"/service/customer/history/saverule"`   //保存历史规则
 }
 
 func (c *Customer) CustomerList() {
@@ -843,3 +847,65 @@ func (c *Customer) TagAssociated() {
 	})
 }
 
+
+func (c *Customer)CustomerHistory(){
+	defer qu.Catch()
+	log.Println("&&&")
+	id := c.GetString("id")
+	query := bson.M{}
+	query["_id"] = qu.StringTOBsonId(id)
+	data, _ := Mgo.FindOneByField("cuser", query, `{}`)
+	depart, _ := Mgo.Find("cuserdepart", bson.M{"s_userid": id, "b_delete": false}, nil, nil, false, -1, -1)
+	(*data)["_id"] = qu.BsonIdToSId((*data)["_id"])
+	if (*data)["i_state"]  == 1 {
+		tagRule, _ := Mgo.Find("eusertagrule", bson.M{"s_userid": id, "b_delete": false}, nil, nil, false, -1, -1)
+		c.T["tagrule"] = *tagRule
+	}
+	c.T["data"] = *data
+	c.T["depart"] = *depart
+	c.Render("private/customer_history.html", &c.T)
+}
+
+//选择历史规则
+func (c *Customer)CustomerAllRule(){
+	customerId := c.GetString("c_id")
+	query := bson.M{
+		"s_userid":   customerId,
+		"b_delete":   false,
+	}
+	data,_ := Mgo.Find("cuserdepart",query, `{"i_createtime":-1}`, nil, false, 0, 0)
+	if data!=nil&&len(*data)!=0{
+		res := []map[string]interface{}{}
+		for _,m := range *data{
+			history_rule_data := make(map[string]interface{})
+			q := bson.M{
+				"s_departid":   m["_id"].(bson.ObjectId).Hex(),
+				"b_delete":   false,
+			}
+			history_rule_data["department_isuse"] = m["i_isuse"]
+			history_rule_data["depart_name"] = m["s_name"]
+			depart_rule_data,_:=Mgo.Find("cuserdepartrule",q, `{"i_createtime":-1}`, `{"s_tagname":1,"i_updatetime":1,"i_isuse":1,"_id":1}`, false, 0, 0)
+			if depart_rule_data!=nil&&len(*depart_rule_data)!=0{
+				for _,j:= range *depart_rule_data{
+					j["_id"] = j["_id"].(bson.ObjectId).Hex()
+				}
+				history_rule_data["department_rule"] = depart_rule_data
+			}
+			res = append(res,history_rule_data)
+		}
+		c.ServeJson(map[string]interface{}{
+			"status":"success",
+			"data":res,
+		})
+	}else{
+		c.ServeJson(map[string]interface{}{
+			"status":"fail",
+			"data":"",
+		})
+	}
+}
+
+//保存历史规则
+func (c *Customer)SaveHistoryRule(){
+
+}

+ 60 - 0
src/util/config.go

@@ -4,6 +4,7 @@ import (
 	"qfw/mongodb"
 	qu "qfw/util"
 	"qfw/util/elastic"
+	mgo "util/mgodb"
 )
 
 var (
@@ -16,6 +17,23 @@ var (
 	Subday          float64
 	PreviewHref     string //数据预览地址
 	CustomerApi     string
+	LatestId        = ""            //起始id
+	MgoTag          *mgo.MongodbSim //标签库连接
+	MgoSave         *mgo.MongodbSim //数据保存库连接
+	MgoBuyer        *mgo.MongodbSim //医院等级信息
+	MgoEnps         *mgo.MongodbSim //企业信息
+	SaveColl        string
+	HospColl        string
+	BuyerEntColl    string
+	EnpsColl        string
+	Es              *elastic.Elastic //es
+	Index           string
+	Itype           string
+	TaskTime        int64 //定时任务时间
+	//历史数据
+	SId          string
+	EId          string
+	CustomerName string
 )
 
 var (
@@ -75,6 +93,7 @@ func InitOther() {
 	Subday = qu.Float64All(Sysconfig["subday"])
 	PreviewHref = qu.ObjToString(Sysconfig["preview_href"])
 	initTagField()
+	initdb()
 }
 
 func initCitys() {
@@ -161,3 +180,44 @@ func initEs() {
 func initTagField() {
 	TagFieldMap, _ = Mgo.Find("code_tagfield", `{}`, `{"_id": 1}`, `{}`, false, -1, -1)
 }
+
+func initdb() {
+	//mgo save
+	save := Sysconfig["save"].(map[string]interface{})
+	SaveColl = qu.ObjToString(save["coll"])
+	MgoSave = &mgo.MongodbSim{
+		MongodbAddr: qu.ObjToString(save["addr"]),
+		DbName:      qu.ObjToString(save["db"]),
+		Size:        qu.IntAllDef(save["size"], 15),
+	}
+	MgoSave.InitPool()
+	//mgo buyerinfo
+	buyerinfo := Sysconfig["buyerinfo"].(map[string]interface{})
+	HospColl = qu.ObjToString(buyerinfo["hospcoll"])
+	BuyerEntColl = qu.ObjToString(buyerinfo["buyerentcoll"])
+	MgoBuyer = &mgo.MongodbSim{
+		MongodbAddr: qu.ObjToString(buyerinfo["addr"]),
+		DbName:      qu.ObjToString(buyerinfo["db"]),
+		Size:        qu.IntAllDef(buyerinfo["size"], 15),
+	}
+	MgoBuyer.InitPool()
+	//mgo enterprise
+	enterprise := Sysconfig["enterprise"].(map[string]interface{})
+	EnpsColl = qu.ObjToString(enterprise["coll"])
+	MgoEnps = &mgo.MongodbSim{
+		MongodbAddr: qu.ObjToString(enterprise["addr"]),
+		DbName:      qu.ObjToString(enterprise["db"]),
+		Size:        qu.IntAllDef(enterprise["size"], 15),
+	}
+	MgoEnps.InitPool()
+
+	//es
+	es := Sysconfig["es"].(map[string]interface{})
+	Es = &elastic.Elastic{
+		S_esurl: qu.ObjToString(es["addr"]),
+		I_size:  qu.IntAllDef(es["pool"], 15),
+	}
+	Es.InitElasticSize()
+	Index = qu.ObjToString(es["index"])
+	Itype = qu.ObjToString(es["itype"])
+}

+ 357 - 0
src/util/mgodb/mgo.go

@@ -0,0 +1,357 @@
+package mongodb
+
+import (
+	"context"
+	"log"
+	"time"
+
+	"go.mongodb.org/mongo-driver/bson"
+	"go.mongodb.org/mongo-driver/bson/primitive"
+	"go.mongodb.org/mongo-driver/mongo"
+	"go.mongodb.org/mongo-driver/mongo/options"
+)
+
+var Mgo *MongodbSim
+
+type MgoSess struct {
+	Db     string
+	Coll   string
+	Query  interface{}
+	Sorts  []string
+	fields interface{}
+	limit  int64
+	skip   int64
+	M      *MongodbSim
+}
+
+type MgoIter struct {
+	Cursor *mongo.Cursor
+}
+
+func (mt *MgoIter) Next(result interface{}) bool {
+	if mt.Cursor != nil {
+		if mt.Cursor.Next(nil) {
+			err := mt.Cursor.Decode(result)
+			if err != nil {
+				log.Println("mgo cur err", err.Error())
+				mt.Cursor.Close(nil)
+				return false
+			}
+			return true
+		} else {
+			mt.Cursor.Close(nil)
+			return false
+		}
+	} else {
+		return false
+	}
+
+}
+
+func (ms *MgoSess) DB(name string) *MgoSess {
+	ms.Db = name
+	return ms
+}
+
+func (ms *MgoSess) C(name string) *MgoSess {
+	ms.Coll = name
+	return ms
+}
+
+func (ms *MgoSess) Find(q interface{}) *MgoSess {
+	ms.Query = q
+	return ms
+}
+
+func (ms *MgoSess) Select(fields interface{}) *MgoSess {
+	ms.fields = fields
+	return ms
+}
+
+func (ms *MgoSess) Limit(limit int64) *MgoSess {
+	ms.limit = limit
+	return ms
+}
+func (ms *MgoSess) Skip(skip int64) *MgoSess {
+	ms.skip = skip
+	return ms
+}
+
+func (ms *MgoSess) Sort(sorts ...string) *MgoSess {
+	ms.Sorts = sorts
+	return ms
+}
+
+func (ms *MgoSess) Iter() *MgoIter {
+	it := &MgoIter{}
+	find := options.Find()
+	if ms.skip > 0 {
+		find.SetSkip(ms.skip)
+	}
+	if ms.limit > 0 {
+		find.SetLimit(ms.limit)
+	}
+	find.SetBatchSize(100)
+	if len(ms.Sorts) > 0 {
+		sort := bson.M{}
+		for _, k := range ms.Sorts {
+			switch k[:1] {
+			case "-":
+				sort[k[1:]] = -1
+			case "+":
+				sort[k[1:]] = 1
+			default:
+				sort[k] = 1
+			}
+		}
+		find.SetSort(sort)
+	}
+	if ms.fields != nil {
+		find.SetProjection(ms.fields)
+	}
+	cur, err := ms.M.C.Database(ms.Db).Collection(ms.Coll).Find(ms.M.Ctx, ms.Query, find)
+	if err != nil {
+		log.Println("mgo find err", err.Error())
+	} else {
+		it.Cursor = cur
+	}
+	return it
+}
+
+type MongodbSim struct {
+	MongodbAddr string
+	Size        int
+	//	MinSize     int
+	DbName   string
+	C        *mongo.Client
+	Ctx      context.Context
+	ShortCtx context.Context
+	pool     chan bool
+}
+
+func (m *MongodbSim) GetMgoConn() *MgoSess {
+	//m.Open()
+	ms := &MgoSess{}
+	ms.M = m
+	return ms
+}
+
+func (m *MongodbSim) DestoryMongoConn(ms *MgoSess) {
+	//m.Close()
+	ms.M = nil
+	ms = nil
+}
+
+func (m *MongodbSim) InitPool() {
+	opts := options.Client()
+	opts.SetConnectTimeout(3 * time.Second)
+	opts.ApplyURI("mongodb://" + m.MongodbAddr)
+	opts.SetMaxPoolSize(uint64(m.Size))
+	m.pool = make(chan bool, m.Size)
+	opts.SetMaxConnIdleTime(2 * time.Hour)
+	m.Ctx, _ = context.WithTimeout(context.Background(), 99999*time.Hour)
+	m.ShortCtx, _ = context.WithTimeout(context.Background(), 1*time.Minute)
+	client, err := mongo.Connect(m.ShortCtx, opts)
+	if err != nil {
+		log.Println("mgo init error:", err.Error())
+	} else {
+		m.C = client
+		log.Println("init success")
+	}
+}
+
+func (m *MongodbSim) Open() {
+	m.pool <- true
+}
+func (m *MongodbSim) Close() {
+	<-m.pool
+}
+
+//批量插入
+func (m *MongodbSim) UpSertBulk(c string, doc ...[]map[string]interface{}) (map[int64]interface{}, bool) {
+	m.Open()
+	defer m.Close()
+	coll := m.C.Database(m.DbName).Collection(c)
+	var writes []mongo.WriteModel
+	for _, d := range doc {
+		write := mongo.NewUpdateOneModel()
+		write.SetFilter(d[0])
+		write.SetUpdate(d[1])
+		write.SetUpsert(true)
+		writes = append(writes, write)
+	}
+	r, e := coll.BulkWrite(m.Ctx, writes)
+	if e != nil {
+		log.Println("mgo upsert error:", e.Error())
+		return nil, false
+	}
+	//	else {
+	//		if r.UpsertedCount != int64(len(doc)) {
+	//			log.Println("mgo upsert uncomplete:uc/dc", r.UpsertedCount, len(doc))
+	//		}
+	//		return true
+	//	}
+	return r.UpsertedIDs, true
+}
+
+//批量插入
+func (m *MongodbSim) SaveBulk(c string, doc ...map[string]interface{}) bool {
+	m.Open()
+	defer m.Close()
+	coll := m.C.Database(m.DbName).Collection(c)
+	var writes []mongo.WriteModel
+	for _, d := range doc {
+		write := mongo.NewInsertOneModel()
+		write.SetDocument(d)
+		writes = append(writes, write)
+	}
+	_, e := coll.BulkWrite(m.Ctx, writes)
+	if e != nil {
+		log.Println("mgo savebulk error:", e.Error())
+		return false
+	}
+	return true
+}
+
+//保存
+func (m *MongodbSim) Save(c string, doc map[string]interface{}) interface{} {
+	m.Open()
+	defer m.Close()
+	coll := m.C.Database(m.DbName).Collection(c)
+	r, err := coll.InsertOne(m.Ctx, doc)
+	if err != nil {
+		return nil
+	}
+	return r.InsertedID
+}
+
+//更新by Id
+func (m *MongodbSim) UpdateById(c, id string, doc map[string]interface{}) bool {
+	m.Open()
+	defer m.Close()
+	coll := m.C.Database(m.DbName).Collection(c)
+	_, err := coll.UpdateOne(m.Ctx, map[string]interface{}{"_id": StringTOBsonId(id)}, doc)
+	if err != nil {
+		return false
+	}
+	return true
+}
+
+//更新Update
+func (m *MongodbSim) Update(c string, query map[string]interface{}, up map[string]interface{}) (map[int64]interface{}, bool) {
+	m.Open()
+	defer m.Close()
+	coll := m.C.Database(m.DbName).Collection(c)
+	var writes []mongo.WriteModel
+	write := mongo.NewUpdateOneModel()
+	write.SetFilter(query)
+	write.SetUpdate(up)
+	write.SetUpsert(true)
+	writes = append(writes, write)
+	r, err := coll.BulkWrite(m.Ctx, writes)
+	if err != nil {
+		return nil, false
+	}
+	return r.UpsertedIDs, true
+}
+
+//删除by id
+func (m *MongodbSim) DeleteById(c, id string) int64 {
+	m.Open()
+	defer m.Close()
+	coll := m.C.Database(m.DbName).Collection(c)
+	r, err := coll.DeleteOne(m.Ctx, map[string]interface{}{"_id": StringTOBsonId(id)})
+	if err != nil {
+		return 0
+	}
+	return r.DeletedCount
+}
+
+//通过条件删除
+func (m *MongodbSim) Delete(c string, query map[string]interface{}) int64 {
+	m.Open()
+	defer m.Close()
+	coll := m.C.Database(m.DbName).Collection(c)
+	r, err := coll.DeleteMany(m.Ctx, query)
+	if err != nil {
+		return 0
+	}
+	return r.DeletedCount
+}
+
+//findbyid
+func (m *MongodbSim) FindById(c, id string, fields interface{}) map[string]interface{} {
+	m.Open()
+	defer m.Close()
+	coll := m.C.Database(m.DbName).Collection(c)
+	op := options.FindOne()
+	r := coll.FindOne(m.Ctx, map[string]interface{}{"_id": StringTOBsonId(id)}, op.SetProjection(fields))
+	v := map[string]interface{}{}
+	r.Decode(&v)
+	return v
+}
+
+//findone
+func (m *MongodbSim) FindOne(c string, query map[string]interface{}) map[string]interface{} {
+	m.Open()
+	defer m.Close()
+	coll := m.C.Database(m.DbName).Collection(c)
+	//op := options.FindOne()
+	//r := coll.FindOne(m.Ctx, query, op.SetProjection(fields))
+	r := coll.FindOne(m.Ctx, query)
+	v := map[string]interface{}{}
+	r.Decode(&v)
+	return v
+}
+
+//find
+func (m *MongodbSim) Find(c string, query map[string]interface{}, sort, fields interface{}) ([]map[string]interface{}, error) {
+	m.Open()
+	defer m.Close()
+	coll := m.C.Database(m.DbName).Collection(c)
+	op := options.Find()
+	r, err := coll.Find(m.Ctx, query, op.SetSort(sort), op.SetProjection(fields))
+	if err != nil {
+		log.Fatal(err)
+		return nil, err
+	}
+	var results []map[string]interface{}
+	if err = r.All(m.Ctx, &results); err != nil {
+		log.Fatal(err)
+		return nil, err
+	}
+	return results, nil
+}
+
+//findbylimit
+func (m *MongodbSim) FindByLimit(c string, query map[string]interface{}, sort, fields interface{}, start int64, limit int64) ([]map[string]interface{}, error) {
+	m.Open()
+	defer m.Close()
+	coll := m.C.Database(m.DbName).Collection(c)
+	op := options.Find()
+	r, err := coll.Find(m.Ctx, query, op.SetSort(sort), op.SetProjection(fields), op.SetSkip(start), op.SetLimit(limit))
+	if err != nil {
+		log.Fatal(err)
+		return nil, err
+	}
+	var results []map[string]interface{}
+	if err = r.All(m.Ctx, &results); err != nil {
+		log.Fatal(err)
+		return nil, err
+	}
+	return results, nil
+}
+
+//创建_id
+func NewObjectId() primitive.ObjectID {
+	return primitive.NewObjectID()
+}
+
+func StringTOBsonId(id string) primitive.ObjectID {
+	objectId, _ := primitive.ObjectIDFromHex(id)
+	return objectId
+}
+
+func BsonTOStringId(id interface{}) string {
+	return id.(primitive.ObjectID).Hex()
+}

+ 18 - 17
src/util/utiltag_test.go

@@ -1,15 +1,16 @@
 package util
 
 import (
-	"gopkg.in/mgo.v2/bson"
 	"log"
 	qu "qfw/util"
 	"testing"
 	"time"
+
+	"gopkg.in/mgo.v2/bson"
 )
 
 func TestUtiltags(t *testing.T) {
-	qu.ReadConfig("../config.json",&Sysconfig) //初始化配置
+	qu.ReadConfig("../config.json", &Sysconfig) //初始化配置
 	log.Println(Sysconfig)
 	InitMgoPool()
 	_id := "5e9e5a2bc2e1e0873f9923e2"
@@ -18,38 +19,38 @@ func TestUtiltags(t *testing.T) {
 		esquery := Utiltags(*data)
 		log.Println(esquery)
 		//return
-		log.Println("update:",Mgo.Update("tags", `{"_id":"`+_id+`"}`,bson.M{"$set":bson.M{
-			"s_esquery":esquery,
-		}},false,false))
+		log.Println("update:", Mgo.Update("tags", `{"_id":"`+_id+`"}`, bson.M{"$set": bson.M{
+			"s_esquery": esquery,
+		}}, false, false))
 		return
-		InitOther()               //
-		data, _ := Mgo.FindOne("tags",  `{"_id":"`+_id+`"}`)
-		log.Println("保存:",UtilEsFind(*data))
-		data, _ = Mgo.FindOne("tags",  `{"_id":"`+_id+`"}`)
+		InitOther() //
+		data, _ := Mgo.FindOne("tags", `{"_id":"`+_id+`"}`)
+		log.Println("保存:", UtilEsFind(*data))
+		data, _ = Mgo.FindOne("tags", `{"_id":"`+_id+`"}`)
 		log.Println((*data)["i_estotal"])
-	}else {
+	} else {
 		log.Println("no found")
 	}
 }
 
 func TestED(t *testing.T) {
-	qu.ReadConfig("../config.json",&Sysconfig) //初始化配置
+	qu.ReadConfig("../config.json", &Sysconfig) //初始化配置
 	enstr := qu.SE.EncodeString("1585796166xxx1xxx2")
 	log.Println(enstr)
 
 	destr := qu.SE.DecodeString(enstr)
 	log.Println(destr)
 
-	if len(destr)>10{
-		ctime:= destr[:10]
+	if len(destr) > 10 {
+		ctime := destr[:10]
 		log.Println(ctime)
-		ctimeint:= qu.Int64All(ctime)
+		ctimeint := qu.Int64All(ctime)
 		log.Println(ctimeint)
 		log.Println(time.Now().Unix())
-		log.Println(time.Now().Sub(time.Unix(ctimeint,0)).Hours()/24)
-		if time.Now().Sub(time.Unix(ctimeint,0)).Hours()/24 < qu.Float64All(Sysconfig["subday"])*24{
+		log.Println(time.Now().Sub(time.Unix(ctimeint, 0)).Hours() / 24)
+		if time.Now().Sub(time.Unix(ctimeint, 0)).Hours()/24 < qu.Float64All(Sysconfig["subday"])*24 {
 
 		}
 	}
 
-}
+}

+ 1544 - 0
src/web/templates/private/cuser_rule_history_edit.html

@@ -0,0 +1,1544 @@
+{{include "com/inc.html"}}
+<!-- Main Header -->
+{{include "com/header.html"}}
+<!-- Left side column. 权限菜单 -->
+{{include "com/menu.html"}}
+{{include "com/modal.html"}}
+<script src="/time/js/angular.min.js"></script>
+<script src="/time/js/wui-date.js"></script>
+<div class="content-wrapper">
+    <section class="content-header">
+        <h1> 新建客户规则
+            <small>
+                <button class="btn btn-warning btn-sm" onclick="importcomrule()"><i class="fa fa-fw fa-upload fa-lg"></i>导入通用标签</button>
+                <button class="btn btn-primary btn-sm" onclick="importRule()"><i class="fa fa-fw fa-cloud-upload fa-lg"></i>导入关键词</button>
+                <form style="display:none" id='uploadform' method='post'>
+                    <input type='file' name='xlsx' id='file'/>
+                </form>
+                <button class="btn btn-info btn-sm" onclick="importEs()"><i class="fa fa-fw fa-cloud-upload fa-lg"></i>导入es</button>
+                <a class="btn btn-sm btn-success" id="download" onclick="downloades()"><i class="fa fa-fw fa-cloud-download fa-lg"></i>下载es</a>
+                <a class="btn btn-sm btn-instagram" onclick="produceData()"><i class="fa fa-fw fa-database fa-lg"></i>生成数据</a>
+                <button class="btn btn-warning btn-sm" onclick="previewData()"><i class="fa fa-fw fa-eye fa-lg"></i>预览</button>
+                <button class="btn btn-danger btn-sm" onclick="archive()"><i class="fa fa-fw fa-save fa-lg"></i>存档</button>
+            </small>
+        </h1>
+        <ol class="breadcrumb">
+            <li><a href="#"><i class="fa fa-dashboard"></i> 首页</a></li>
+            <li><a href="/service/customer/list"> 客户列表</a></li>
+            <li><a href="/service/customer/edit?id={{.T.cid}}"> 编辑客户</a></li>
+            <li><a href="/service/cuser/rule/list?ids={{.T.ids}}"> 客户规则</a></li>
+            <li><a href="#"> 新增客户规则</a></li>
+        </ol>
+    </section>
+    <!-- Main content -->
+    <section class="content">
+        <div class="tab-content">
+            <div class="nav-tabs-custom">
+                <ul class="nav nav-tabs edit-step">
+                    <li class="active" data-mode="guide"><a href="#tab_1" data-toggle="tab" aria-expanded="true">通用规则</a></li>
+                    <li data-mode="guide" class=""><a href="#tab_2" data-toggle="tab" aria-expanded="false">关键词规则</a></li>
+                    <button class="btn btn-primary btn-sm" style="float: right;margin-top: 7px;margin-right: 10px" onclick="saveTag()"><i class="fa fa-fw fa-file-text fa-lg"></i>保存标签</button>
+                </ul>
+                <div class="tab-content">
+                    <div class="tab-pane active" id="tab_1">
+                        <form class="form-horizontal">
+                            <div class="box box-primary">
+                                <div class="box-header with-border">
+                                    <i class="fa fa-tag"></i>
+                                    <h3 class="box-title">通用规则</h3>
+                                </div>
+                                <div class="box-body">
+                                    <div class="form-group">
+                                        <label class="col-sm-2 control-label"><span style="color:red;">* </span>标签名称</label>
+                                        <div class="col-sm-3">
+                                            <input type="text" class="form-control" id="tagname" placeholder="标签名称"
+                                                   value="" required>
+                                        </div>
+                                        <label class="col-sm-2 control-label"><span style="color:red;">* </span>部门名称</label>
+                                        <div class="col-sm-3">
+                                            <input type="text" class="form-control" id="departmentName" placeholder="部门名称"
+                                                   value="" required>
+                                        </div>
+                                    </div>
+                                    <hr>
+                                    <div class="form-group">
+                                        <label class="col-sm-2 control-label">选择省</label>
+                                        <div class="col-sm-3">
+                                            <select class="form-control selectpicker" multiple data-live-search="true"
+                                                    id="province"></select>
+                                            <script>
+                                                $('#province').on('hide.bs.select', function () {
+                                                    var arr = $('#province').val();
+                                                    var arrStr = "";
+                                                    for (var i in arr) {
+                                                        if (arrStr === "") {
+                                                            arrStr = arr[i]
+                                                        } else {
+                                                            arrStr = arrStr + "," + arr[i]
+                                                        }
+                                                    }
+                                                    dataMap["s_area"] = arrStr
+                                                })
+                                            </script>
+                                        </div>
+                                        <label class="col-sm-2 control-label">选择市</label>
+                                        <div class="col-sm-3">
+                                            <select class="form-control selectpicker" multiple data-live-search="true"
+                                                    id="city"></select>
+                                            <script>
+                                                $('#city').on('hide.bs.select', function () {
+                                                    var arr = $('#city').val();
+                                                    var arrStr = "";
+                                                    for (var i in arr) {
+                                                        if (arrStr === "") {
+                                                            arrStr = arr[i]
+                                                        } else {
+                                                            arrStr = arrStr + "," + arr[i]
+                                                        }
+                                                    }
+                                                    dataMap["s_city"] = arrStr
+                                                })
+                                            </script>
+                                        </div>
+                                    </div>
+                                    <div class="form-group">
+                                        <label class="col-sm-2 control-label">选择区/县</label>
+                                        <div class="col-sm-3">
+                                            <select class="form-control selectpicker" multiple data-live-search="true"
+                                                    id="district"></select>
+                                            <script>
+                                                $('#district').on('hide.bs.select', function () {
+                                                    var arrStr = "";
+                                                    $('#district option:selected').each(function () {
+                                                        if (arrStr === "") {
+                                                            arrStr = $(this).val()
+                                                        } else {
+                                                            arrStr = arrStr + "," + $(this).val()
+                                                        }
+                                                    });
+                                                    dataMap["s_district"] = arrStr
+                                                })
+
+                                            </script>
+                                        </div>
+                                    </div>
+                                    <div class="form-group">
+                                        <label class="col-sm-2 control-label">公告类别</label>
+                                        <div class="col-sm-3">
+                                            <select class="form-control selectpicker" id="typeSelect">
+                                                <option selected:disabled style="diaplay:none" value=0>请选择</option>
+                                                <option value=1>一级公告类别</option>
+                                                <option value=2>二级公告类别</option>
+                                            </select>
+                                            <script>
+                                                $('#typeSelect').on('changed.bs.select', function () {
+                                                    $("#typeSelectVal").selectpicker("deselectAll");
+                                                    $("#typeSelectVal").empty();
+                                                    if ($(this).val() == 1) {
+                                                        for (var i in topTypeArr) {
+                                                            var opt = document.createElement('option');
+                                                            opt.innerText = topTypeArr[i];
+                                                            opt.value = topTypeArr[i];
+                                                            $('#typeSelectVal')[0].appendChild(opt)
+                                                        }
+                                                    }else if ($(this).val() == 2) {
+                                                        for (var i in subTypeArr) {
+                                                            var opt = document.createElement('option');
+                                                            opt.innerText = subTypeArr[i];
+                                                            opt.value = subTypeArr[i];
+                                                            $('#typeSelectVal')[0].appendChild(opt)
+                                                        }
+                                                    }
+                                                    $("#typeSelectVal").selectpicker("refresh");
+                                                })
+                                            </script>
+                                        </div>
+                                        <label class="col-sm-2 control-label">公告类别选择</label>
+                                        <div class="col-sm-3">
+                                            <select class="form-control selectpicker" multiple id="typeSelectVal"></select>
+                                            <script>
+                                                $('#typeSelectVal').on('show.bs.select', function () {
+                                                    if ($('#typeSelect').val() == 0) {
+                                                        showTip("请选择公告类别", 500)
+                                                    }
+                                                })
+                                                $('#typeSelectVal').on('hide.bs.select', function () {
+                                                    var arr = $(this).val();
+                                                    var arrStr = "";
+                                                    for (var i in arr) {
+                                                        if (arrStr === "") {
+                                                            arrStr = arr[i]
+                                                        } else {
+                                                            arrStr = arrStr + "," + arr[i]
+                                                        }
+                                                    }
+                                                    if ($('#typeSelect').val() == 1) {
+                                                        dataMap["s_toptype"] = arrStr;
+                                                        dataMap["s_subtype"] = "";
+                                                    }else {
+                                                        dataMap["s_toptype"] = "";
+                                                        dataMap["s_subtype"] = arrStr;
+                                                    }
+                                                })
+                                            </script>
+                                        </div>
+                                    </div>
+                                    <div class="form-group">
+                                        <label class="col-sm-2 control-label ">开始时间</label>
+                                        <div class="col-sm-3">
+                                            <div class="wui-content">
+                                                <div class="wui-area" id="starttime">
+                                                    <wui-date
+                                                            format="yyyy-mm-dd hh:mm:ss"
+                                                            placeholder="请选择或输入开始日期"
+                                                            id="date4"
+                                                            btns="{'ok':'确定','now':'此刻'}"
+                                                            ng-model="date4">
+                                                    </wui-date>
+                                                </div>
+                                            </div>
+                                        </div>
+                                        <label class="col-sm-2 control-label ">结束时间</label>
+                                        <div class="col-sm-3">
+                                            <div class="wui-content">
+                                                <div class="wui-area" id="endtime">
+                                                    <wui-date
+                                                            format="yyyy-mm-dd hh:mm:ss"
+                                                            placeholder="请选择或输入结束日期"
+                                                            id="date4"
+                                                            btns="{'ok':'确定','now':'此刻'}"
+                                                            ng-model="date5">
+                                                    </wui-date>
+                                                </div>
+                                            </div>
+                                        </div>
+                                    </div>
+                                    <div class="form-group">
+                                        <label class="col-sm-2 control-label">选择字段</label>
+                                        <div class="col-sm-3">
+                                            <select class="form-control selectpicker" multiple id="existFieldSelect"
+                                                    data-actions-box="true"></select>
+                                            <script>
+                                                $('#existFieldSelect').on('hide.bs.select', function () {
+                                                    var arr = $('#existFieldSelect').val();
+                                                    var arrStr = "";
+                                                    for (var i in arr) {
+                                                        if (arrStr === "") {
+                                                            arrStr = arr[i]
+                                                        } else {
+                                                            arrStr = arrStr + "," + arr[i]
+                                                        }
+                                                    }
+                                                    dataMap["s_existfields"] = arrStr
+                                                })
+                                            </script>
+                                        </div>
+                                    </div>
+                                    <hr>
+                                    <div class="form-group">
+                                        <label class="col-sm-2 control-label">预算限制</label>
+                                        <div class="col-sm-3">
+                                            <input class="form-control" type="text" id="s_budget_min" placeholder="单位:元/大于">
+                                        </div>
+                                        <label class="col-sm-2" style="text-align: center">___ ___</label>
+                                        <div class="col-sm-3">
+                                            <input class="form-control" type="text" id="s_budget_max" placeholder="单位:元/小于">
+                                        </div>
+                                    </div>
+                                    <div class="form-group">
+                                        <label class="col-sm-2 control-label">中标金额限制</label>
+                                        <div class="col-sm-3" id="bidamountDiv">
+                                            <input class="form-control" type="text" id="s_bidamount_min" placeholder="单位:元/大于">
+                                        </div>
+                                        <label class="col-sm-2" style="text-align: center">___ ___</label>
+                                        <div class="col-sm-3">
+                                            <input class="form-control" type="text" id="s_bidamount_max" placeholder="单位:元/小于">
+                                        </div>
+                                    </div>
+                                    <div class="form-group">
+                                        <label class="col-sm-2 control-label">全局附加词</label>
+                                        <div class="col-sm-3">
+                                            <input type="text" class="form-control" id="g_addkey" placeholder="***"
+                                                   value="">
+                                        </div>
+                                        <label class="col-sm-2 control-label">匹配方式</label>
+                                        <div class="col-sm-3">
+                                            <input class="form-control" style="text-align: left;overflow: hidden;white-space: nowrap;text-overflow: ellipsis"
+                                                   type="button" id="s_addkeymatch" onclick="selectKeyMatch(0)">
+                                        </div>
+                                    </div>
+                                    <div class="form-group">
+                                        <label for="spiderChannel" class="col-sm-2 control-label">全局排除词</label>
+                                        <div class="col-sm-3">
+                                            <input type="text" class="form-control" id="g_notkey" placeholder="***"
+                                                   value="">
+                                        </div>
+                                        <label class="col-sm-2 control-label">匹配方式</label>
+                                        <div class="col-sm-3">
+                                            <input class="form-control" style="text-align: left;overflow: hidden;white-space: nowrap;text-overflow: ellipsis"
+                                                   type="button" id="s_notkeymatch" onclick="selectKeyMatch(1)">
+                                        </div>
+                                    </div>
+                                     <div class="form-group">
+                                        <label class="col-sm-2 control-label">全局清理词</label>
+                                        <div class="col-sm-3">
+                                            <input type="text" class="form-control" id="s_globalclearkey" placeholder="***"
+                                                   value="">
+                                        </div>
+                                         <label class="col-sm-2 control-label">匹配方式</label>
+                                        <div class="col-sm-3">
+                                            <input class="form-control" style="text-align: left;overflow: hidden;white-space: nowrap;text-overflow: ellipsis"
+                                                   type="button" id="s_globalclearkeymatch" onclick="selectKeyMatch(13)">
+                                        </div>
+                                    </div>
+                                    <div class="form-group">
+                                        <label class="col-sm-2 control-label">采购单位行业</label>
+                                        <div class="col-sm-3">
+                                            <input class="form-control" style="text-align: left;overflow: hidden;white-space: nowrap;text-overflow: ellipsis"
+                                                   type="button" id="s_buyerclass" onclick="selectBuyer(2)">
+                                        </div>
+                                        <label class="col-sm-2 control-label">公告行业</label>
+                                        <div class="col-sm-3">
+                                            <input class="form-control" style="text-align: left;overflow: hidden;white-space: nowrap;text-overflow: ellipsis"
+                                                   type="button" id="s_scopeclass" onclick="selectScope(3)">
+                                        </div>
+                                    </div>
+                                    <hr>
+                                    <div class="form-group">
+                                        <label class="col-sm-2 control-label">预览数据量</label>
+                                        <div class="col-sm-3">
+                                            <input type="text" class="form-control" id="maxnum" placeholder="默认100条" value="">
+                                        </div>
+                                        <label class="col-sm-2 control-label ">高级字段包</label>
+                                        <div class="col-sm-3">
+                                            <select class="form-control" id="extfiledselect" style="border-radius:3px">
+                                                <option value="2">是</option>
+                                                <option value="1">否</option>
+                                            </select>
+                                            <script>
+                                                $('#extfiledselect').on("change", function () {
+                                                    dataMap["i_extfieldstype"] = $('#extfiledselect option:selected').val();
+                                                })
+                                            </script>
+                                        </div>
+                                    </div>
+                                    <hr>
+                                    <div id="es_div" class="form-group" style="display: none">
+                                        <label class="col-sm-2 control-label">ES语句</label>
+                                        <div class="col-sm-8">
+                                            <textarea class="form-control" id="estxt" readonly></textarea>
+                                        </div>
+                                    </div>
+                                </div>
+                            </div>
+                        </form>
+                    </div>
+                    <div class="tab-pane" id="tab_2">
+                        <div class="box box-primary">
+                            <div class="box-header with-border">
+                                <i class="fa fa-bookmark"></i>
+                                <h3 class="box-title">关键词规则</h3>
+                                <div style="float: right">
+                                    <a class="btn btn-default btn-sm" onclick="selectKeyMatch(4)">关键词匹配方式</a>
+                                    <a class="btn btn-default btn-sm" onclick="selectKeyMatch(6)">附加词匹配方式</a>
+                                    <a class="btn btn-default btn-sm" onclick="selectKeyMatch(8)">排除词匹配方式</a>
+                                    <a class="btn btn-default btn-sm" onclick="selectBuyer(9)">采购单位行业</a>
+                                    <a class="btn btn-default btn-sm" onclick="selectScope(10)">公告行业</a>
+                                </div>
+                            </div>
+                            <table id="keywords" class="table table-bordered">
+                                <thead>
+                                <div style="margin: 10px 10px 0px;">
+                                    <a class="btn btn-twitter" onclick="add_Rule()">新增</a>
+                                    <a class="btn btn-twitter" onclick="modify_Rule()">修改</a>
+                                    <a class="btn btn-twitter" onclick="del_Rule()">删除</a>
+                                </div>
+                                <tr>
+                                    <th>编号</th>
+                                    <th><input type="checkbox" onclick="allSelect(this)" id="allCheckbox"></th>
+                                    <th>关键词</th>
+                                    <th>匹配方式</th>
+                                    <th>附加词</th>
+                                    <th>匹配方式</th>
+                                    <th>排除词</th>
+                                    <th>匹配方式</th>
+                                    <th>采购单位行业</th>
+                                    <th>公告行业</th>
+                                </tr>
+                                </thead>
+                            </table>
+                        </div>
+                    </div>
+                </div>
+            </div>
+        </div>
+    </section>
+</div>
+
+{{include "com/footer.html"}}
+<script>
+    menuActive("/customer/list");
+    var state = 1;
+    var clearkeyMatchArr = ["detail","title"];
+    var app = angular.module('app', ["wui.date"]);
+    var province = {{.T.province}};
+    var cityMap = {{.T.city}};
+    var districtMap = {{.T.district}};
+    var topTypeArr = {{.T.topTypeArr}};
+    var subTypeArr = {{.T.subTypeArr}};
+    var matchTypeMap = {{.T.matchTypeMap}};
+    var matchTypeMap2 = {{.T.matchTypeMap2}};
+    var existField = {{.T.existField}};
+    var buyerClass = {{.T.buyerClass}};
+    var scopeClass = {{.T.scopeClass}};
+
+    var ids = {{.T.ids}}
+
+    var matchCode = [];     //匹配方式code
+    var matchName = [];     //匹配名字
+    var buyerArr = [];      //采购单位
+    var scopeTopArr = [];       //公告行业一级
+    var scopeSubArr = [];       //公告行业二级
+
+    var keyTableChecked = false;        //关键词表格数据是否有选中
+    var keyTableAllChecked = true;      //表单数据是否被全选中
+    var selectIndex = [];               //选中的编号
+    var setValue = 0;                   // 4: 关键词匹配方式,6:附加词匹配方式,8:排除词匹配方式,9:采购单位,10:公告行业, 2:全局采购单位行业, 3:全局公告行业, 13:全局清理词
+    var o_rules = [];                    //关键词列表
+
+    var s_dataid = "";                  //预览链接加密串
+
+    //表单数据
+    var dataMap = {
+        "id": "",
+        "s_tagname": "",
+        "s_area": "",
+        "s_city": "",
+        "s_district": "",
+        "s_toptype": "",
+        "s_subtype": "",
+        "s_esquery": "",
+        "s_globaladdkey": "",
+        "s_globaladdkeymatch": "",
+        "s_globalnotkey": "",
+        "s_globalnotkeymatch": "",
+        "i_maxnum": 100,
+        "i_starttime": 0,
+        "i_endtime": 0,
+        "i_extfieldstype": 2,
+        "i_esquerytype": 1,
+        "i_isuse": 1,
+    };
+
+    $(document).ready(function () {
+        //省
+        for (var i in province) {
+            var provOpt = document.createElement('option');
+            provOpt.innerText = province[i];
+            provOpt.value = province[i];
+            $("#province")[0].appendChild(provOpt);
+        }
+        var optHtml = "";
+        var optGroup = "";
+        for (var i in districtMap) {
+            var opt = document.createElement('option');
+            opt.innerText = i;
+            opt.value = i;
+            $("#city")[0].appendChild(opt);
+            optGroup += '<optgroup label="' + i + '" value="' + i + '">';
+            for (var j in districtMap[i]) {
+                optGroup += '<option value="' + i + "-" + districtMap[i][j] + '">' + districtMap[i][j] + '</option>';
+            }
+            optGroup += `</optgroup>`;
+        }
+        optHtml += optGroup;
+        $("#district").append(optHtml);
+
+        $("#province").selectpicker("refresh");
+        $("#city").selectpicker("refresh");
+        $("#district").selectpicker("refresh");
+
+        for (var i in existField) {
+            var opt = document.createElement('option');
+            opt.innerText = existField[i]["name"];
+            opt.value = existField[i]["code"];
+            $('#existFieldSelect')[0].appendChild(opt)
+        }
+        $("#existFieldSelect").selectpicker("refresh");
+        var btnMatchHtml1 = "";
+        var btnMatchHtml2 = "";
+        for (var i in matchTypeMap) {
+            if (i < 5) {
+                btnMatchHtml1 += `<input type="button" class="btn btn-default col-md-pull-2" style="margin-left: 10px;margin-top: 10px" onclick="clickMatchBtn(this)"
+                            code="${matchTypeMap[i]["code"]}" value="${matchTypeMap[i]["name"]}">`;
+            }else {
+                btnMatchHtml2 += `<input type="button" class="btn btn-default col-md-pull-2" style="margin-left: 10px;margin-top: 10px" onclick="clickMatchBtn2(this)"
+                            code="${matchTypeMap[i]["code"]}" value="${matchTypeMap[i]["name"]}">`;
+            }
+        }
+        $("#btnGroupMatch").append(btnMatchHtml1);
+        $("#btnGroupMatch2").append(btnMatchHtml2);
+        var btnBuyerHtml = "";
+        for (var i in buyerClass) {
+            btnBuyerHtml += `<input type="button" class="btn btn-default col-md-2" style="margin-left: 10px;margin-top: 10px" onclick="clickBuyerBtn(this)"
+                             value="${buyerClass[i]}">`;
+        }
+        $("#btnGroupBuyer").append(btnBuyerHtml);
+
+        var btnTopHtml = "";
+        var btnSubHtml = "";
+        for (var i in scopeClass) {
+            var topId = "top_" + i;
+            btnTopHtml += `<input type="button" id="${topId}" class="btn btn-default btn_top" onclick="btnTopScopeClick(this)" value="${scopeClass[i]["name"]}">`;
+            btnSubHtml += `<div class="btn-group-sub">`;
+            for (var j in scopeClass[i]["child"]) {
+                var sub = scopeClass[i]["child"][j].split("_");
+                btnSubHtml += `<input type="button" class="btn btn-default btn-sub" onclick="btnSubScopeClick(this)" code="${scopeClass[i]["child"][j]}"
+                                index="${i}" value="${sub[1]}">`
+            }
+            btnSubHtml += '</div>'
+        }
+        $("#btnGroupTop").append(btnTopHtml);
+        $("#btnGroupSub").append(btnSubHtml);
+
+        $('#keywords').DataTable({
+            "paging": false,
+            "lengthChange": false,
+            "searching": false,
+            "ordering": false,
+            "info": true,
+            "autoWidth": false,
+            "language": {
+                "url": "/dist/js/dataTables.chinese.lang"
+            },
+            "fnDrawCallback": function () {
+                this.api().column(0).nodes().each(function (cell, i) {
+                    cell.innerHTML = i + 1;
+                });
+            },
+            "columns": [
+                {"data": null, width: "2%"},
+                {"data": "", width: "1%", render: function() {
+                        return `<input type="checkbox" name="ckb-keyid" onclick="singleSelect(this)" style="text-align: center">`
+                    }},
+                {"data": "s_matchkey", width: "10%"},
+                {"data": function (row) {
+                        var str = row.s_keymatch;
+                        if (str && str != "") {
+                            var s = "";
+                            for (var i in matchTypeMap) {
+                                if (str.indexOf(matchTypeMap[i]["code"]) != -1) {
+                                    if (s == "") {
+                                        s = matchTypeMap[i]["name"]
+                                    } else {
+                                        s = s + "," + matchTypeMap[i]["name"]
+                                    }
+                                }
+                            }
+                            return s
+                        }
+                        return ""
+                    }, width: "5%"},
+                {"data": "s_addkey", width: "10%"},
+                {"data": function (row) {
+                        var str = row.s_addkeymatch;
+                        if (str && str != "") {
+                            var s = "";
+                            for (var i in matchTypeMap) {
+                                if (str.indexOf(matchTypeMap[i]["code"]) != -1) {
+                                    if (s == "") {
+                                        s = matchTypeMap[i]["name"]
+                                    } else {
+                                        s = s + "," + matchTypeMap[i]["name"]
+                                    }
+                                }
+                            }
+                            return s
+                        }
+                        return ""
+                    }, width: "5%"},
+                {"data": "s_notkey", width: "10%"},
+                {"data": function (row) {
+                        var str = row.s_notkeymatch;
+                        if (str && str != "") {
+                            var s = "";
+                            for (var i in matchTypeMap) {
+                                if (str.indexOf(matchTypeMap[i]["code"]) != -1) {
+                                    if (s == "") {
+                                        s = matchTypeMap[i]["name"]
+                                    } else {
+                                        s = s + "," + matchTypeMap[i]["name"]
+                                    }
+                                }
+                            }
+                            return s
+                        }
+                        return ""
+                    }, width: "5%"},
+                {"data": "s_buyerclass", width: "7%", render: function (val) {
+                        if (val == undefined) {
+                            val = ""
+                        }
+                        return val
+                    }},
+                {"data": "s_topscopeclass", width: "5%", render: function (val) {
+                        if (val == undefined) {
+                            val = ""
+                        }
+                        return val
+                    }},
+            ],
+            "columnDefs": [{
+                "targets": [2],
+                "render": function (data, type, full, meta) {
+                    if (data && data.length > 24) {
+                        return "<a title='" + data + "' href='#' style='text-decoration: none;'>" + data.trim().substr(0, 24) + "..." + "</a>";
+                    }else if (data == undefined) {
+                        return ""
+                    }else {
+                        return data
+                    }
+                }
+            }, {
+                "targets": [4],
+                "render": function (data, type, full, meta) {
+                    if (data && data.length > 24) {
+                        return "<a title='" + data + "' href='#' style='text-decoration: none;'>" + data.trim().substr(0, 24) + "..." + "</a>";
+                    }else if (data == undefined) {
+                        return ""
+                    }else {
+                        return data
+                    }
+                }
+            }, {
+                "targets": [6],
+                "render": function (data, type, full, meta) {
+                    if (data && data.length > 24) {
+                        return "<a title='" + data + "' href='#' style='text-decoration: none;'>" + data.trim().substr(0, 24) + "..." + "</a>";
+                    }else if (data == undefined) {
+                        return ""
+                    }else {
+                        return data
+                    }
+                }
+            }]
+        });
+    });
+
+    $('#province').selectpicker({
+        liveSearch: true,
+    });
+    $('#city').selectpicker({
+        liveSearch: true,
+    });
+    $('#district').selectpicker({
+        liveSearch: true,
+    });
+    //全局modal取消按钮
+    function cancelModel() {
+        $('#modal-imp-es').modal("hide");
+        $('#modal-select-match').modal("hide");
+        $('#modal-select-buyer').modal("hide");
+        $('#modal-select-scope').modal("hide");
+        $('#btnGroupMatch').find("input").each(function () {
+            $(this).removeClass("active-btn-match");
+        });
+        $('#btnGroupMatch2').find("input").each(function () {
+            $(this).removeClass("active-btn-match");
+        });
+        $('#btnGroupBuyer').find("input").each(function () {
+            $(this).removeClass("active-btn-match");
+        });
+        $('#btnGroupTop').find("input").each(function () {
+            $(this).removeClass("active-btn-match");
+        });
+        $('#btnGroupSub').find("input").each(function () {
+            $(this).removeClass("active-btn-match");
+        });
+    }
+    //导入es语句modal
+    function importEs() {
+        $("#modal-imp-es").modal("show");
+    }
+    //保存es语句
+    function saveEs() {
+        var esStr = $("#es_json").val();
+        try {
+            if (typeof JSON.parse(esStr) == "object") {
+                $('#modal-imp-es').modal("hide");
+                $('#es_div').show();
+                dataMap["s_esquery"] = esStr;
+                $('#estxt').val(esStr);
+                dataMap["i_esquerytype"] = 2;
+            }
+        } catch (e) {
+            showMsg("json格式有误")
+        }
+    }
+    //关键词/附加词/排除词匹配方式modal
+    function selectKeyMatch(val) {
+        setValue = val;
+        if (setValue == 0 || setValue == 1 || setValue == 13) {
+            matchCode.length = 0;
+            matchName.length = 0;
+            $('#modal-select-match').modal("show");
+        }else {
+            if (selectIndex.length > 0 && keyTableChecked) {
+                matchCode.length = 0;
+                matchName.length = 0;
+                $('#modal-select-match').modal("show");
+            }else {
+                alert("请选中关键词")
+            }
+        }
+    }
+    //匹配方式按钮点击事件
+    function clickMatchBtn(obj) {
+        if ($(obj).hasClass("active-btn-match")) {
+            $(obj).removeClass("active-btn-match")
+            matchCode.splice($.inArray($(obj).attr("code"), matchCode), 1);
+            matchName.splice($.inArray($(obj).val(), matchName), 1)
+        } else {
+            $(obj).addClass("active-btn-match");
+            matchCode.push($(obj).attr("code"));
+            matchName.push($(obj).val())
+        }
+
+        $('#btnGroupMatch2 .btn-default').each(function () {
+            $(this).removeClass("active-btn-match");
+        });
+    }
+    function clickMatchBtn2(obj) {
+        matchCode.length = 0;
+        matchName.length = 0;
+        if ($(obj).hasClass("active-btn-match")) {
+            $(obj).removeClass("active-btn-match")
+            matchCode.splice($.inArray($(obj).attr("code"), matchCode), 1);
+            matchName.splice($.inArray($(obj).val(), matchName), 1)
+        } else {
+            $(obj).addClass("active-btn-match");
+            matchCode.push($(obj).attr("code"));
+            matchName.push($(obj).val())
+            $('#btnGroupMatch2 .btn-default').each(function () {
+                if (this != obj) {
+                    $(this).removeClass("active-btn-match");
+                }
+            });
+        }
+
+        $('#btnGroupMatch .btn-default').each(function () {
+            $(this).removeClass("active-btn-match");
+        });
+    }
+    //匹配方式保存
+    function saveMatch() {
+        $('#btnGroupMatch').find("input").each(function () {
+            $(this).removeClass("active-btn-match");
+        });
+        $('#btnGroupMatch2').find("input").each(function () {
+            $(this).removeClass("active-btn-match");
+        });
+        var matchNameStr = "";
+        var codeStr = "";
+        for (var i in matchName) {
+            if (matchNameStr == "") {
+                matchNameStr = matchName[i];
+            }else {
+                matchNameStr += "," + matchName[i]
+            }
+            if (codeStr == "") {
+                codeStr = matchCode[i]
+            }else {
+                codeStr = codeStr + "," + matchCode[i]
+            }
+        }
+        if (setValue == 0) {
+            $('#s_addkeymatch').val(matchNameStr);
+            dataMap["s_globaladdkeymatch"] = codeStr
+        }else if (setValue == 1) {
+            $('#s_notkeymatch').val(matchNameStr);
+            dataMap["s_globalnotkeymatch"] = codeStr
+        }else if (setValue == 13){
+            $('#s_globalclearkeymatch').val(matchNameStr);
+            dataMap["s_globalclearkeymatch"] = codeStr
+        }else {
+            for (var i in selectIndex) {
+                var j = selectIndex[i] - 1;
+                if (setValue == 4) {
+                    if ($("#keywords tbody tr:nth-child("+selectIndex[i]+") td:nth-child(3)").text() != "") {
+                        $("#keywords tbody tr:nth-child("+selectIndex[i]+") td:nth-child("+setValue+")").html(matchNameStr);
+                        o_rules[j]["s_keymatch"] = codeStr;
+                    }
+                }else if (setValue == 6) {
+                    if ($("#keywords tbody tr:nth-child("+selectIndex[i]+") td:nth-child(5)").text() != "") {
+                        $("#keywords tbody tr:nth-child("+selectIndex[i]+") td:nth-child("+setValue+")").html(matchNameStr);
+                        o_rules[j]["s_addkeymatch"] = codeStr;
+                    }
+                } else if (setValue == 8) {
+                    if ($("#keywords tbody tr:nth-child("+selectIndex[i]+") td:nth-child(7)").text() != "") {
+                        $("#keywords tbody tr:nth-child("+selectIndex[i]+") td:nth-child("+setValue+")").html(matchNameStr);
+                        o_rules[j]["s_notkeymatch"] = codeStr;
+                    }
+                }
+            }
+        }
+        $('#modal-select-match').modal("hide");
+        matchName.length = 0;
+        matchCode.length = 0;
+    }
+    //采购单位modal
+    function selectBuyer(val) {
+        setValue = val;
+        if (val == 2) {
+            $('#modal-select-buyer').modal("show");
+        }else {
+            if (keyTableChecked) {
+                $('#modal-select-buyer').modal("show");
+            }else {
+                alert("请选中关键词")
+            }
+        }
+    }
+    //采购单位点击
+    function clickBuyerBtn(obj) {
+        if ($(obj).hasClass("active-btn-match")) {
+            $(obj).removeClass("active-btn-match")
+            buyerArr.splice($.inArray($(obj).val(), buyerArr), 1)
+        } else {
+            $(obj).addClass("active-btn-match");
+            buyerArr.push($(obj).val())
+        }
+    }
+    function saveBuyer() {
+        $('#btnGroupBuyer').find("input").each(function () {
+            $(this).removeClass("active-btn-match");
+        });
+        var buyerStr = "";
+        for (var i in buyerArr) {
+            if ("" === buyerStr) {
+                buyerStr = buyerArr[i];
+            }else {
+                buyerStr += "," + buyerArr[i]
+            }
+        }
+        if (setValue == 2) {
+            $('#s_buyerclass').val(buyerStr);
+            $('#s_buyerclass').attr('title', buyerStr);
+            dataMap["s_globalbuyerclass"] = buyerStr;
+        }else {
+            for (var i in selectIndex) {
+                var j = selectIndex[i] - 1;
+                //采购单位
+                $("#keywords tbody tr:nth-child("+selectIndex[i]+") td:nth-child(9)").html(buyerStr);
+                o_rules[j]["s_buyerclass"] = buyerStr;
+            }
+        }
+        $('#modal-select-buyer').modal("hide");
+        buyerArr.length = 0;
+    }
+    //公告行业modal
+    function selectScope(val) {
+        setValue = val;
+        if (val == 3) {
+            $('#modal-select-scope').modal("show");
+        }else {
+            if (keyTableChecked) {
+                $('#modal-select-scope').modal("show");
+            }else {
+                alert("请选中关键词")
+            }
+        }
+    }
+    //公告行业一级点击
+    function btnTopScopeClick(obj) {
+        if ($(obj).hasClass("active-btn-match")) {
+            $(obj).removeClass("active-btn-match");
+            scopeTopArr.splice($.inArray($(obj).val(), scopeTopArr), 1)
+        } else {
+            $(obj).addClass("active-btn-match");
+            scopeTopArr.push($(obj).val())
+        }
+
+        //选择一级行业,取消掉所选择的所有二级行业
+        if (scopeSubArr.length > 0) {
+            $('#btnGroupSub .btn-default').each(function () {
+                $(this).removeClass("active-btn-match");
+            });
+            scopeSubArr.length = 0
+        }
+    }
+    //公告行业二级点击
+    function btnSubScopeClick(obj) {
+        if ($(obj).hasClass("active-btn-match")) {
+            $(obj).removeClass("active-btn-match")
+            scopeSubArr.splice($.inArray($(obj).val(), scopeSubArr), 1)
+        } else {
+            $(obj).addClass("active-btn-match");
+            var topid = "#top_" + $(obj).attr("index");
+            $(topid).removeClass("active-btn-match");
+            scopeSubArr.push($(obj).attr("code"))
+        }
+
+        //选择二级行业,取消掉所选择的所有一级行业
+        if (scopeTopArr.length > 0) {
+            $('#btnGroupTop .btn-default').each(function () {
+                $(this).removeClass("active-btn-match");
+            });
+            scopeTopArr.length = 0
+        }
+    }
+    function saveScope() {
+        $('#btnGroupTop').find("input").each(function () {
+            $(this).removeClass("active-btn-match");
+        });
+        $('#btnGroupSub').find("input").each(function () {
+            $(this).removeClass("active-btn-match");
+        });
+        var scopeStr = "";
+        var topStr = "";
+        var subStr = "";
+        for (var i in scopeTopArr) {
+            if ("" == scopeStr) {
+                scopeStr = scopeTopArr[i];
+            }else {
+                scopeStr += "," + scopeTopArr[i]
+            }
+            if (topStr == "") {
+                topStr = scopeTopArr[i]
+            } else {
+                topStr = topStr + "," + scopeTopArr[i]
+            }
+        }
+        for (var i in scopeSubArr) {
+            if ("" == scopeStr) {
+                scopeStr = scopeSubArr[i];
+            }else {
+                scopeStr += "," + scopeSubArr[i]
+            }
+            if (subStr == "") {
+                subStr = scopeSubArr[i]
+            } else {
+                subStr = subStr + "," + scopeSubArr[i]
+            }
+        }
+        if (setValue == 3) {
+            $('#s_scopeclass').val(scopeStr);
+            $('#s_scopeclass').attr('title', scopeStr);
+            dataMap["s_globaltopscopeclass"] = topStr;
+            dataMap["s_globalsubscopeclass"] = subStr;
+        }else {
+            for (var i in selectIndex) {
+                var j = selectIndex[i] - 1;
+                $("#keywords tbody tr:nth-child("+selectIndex[i]+") td:nth-child(10)").html(scopeStr);
+                o_rules[j]["s_topscopeclass"] = topStr;
+                o_rules[j]["s_subscopeclass"] = subStr;
+            }
+        }
+        $('#modal-select-scope').modal("hide");
+        scopeTopArr.length = 0;
+        scopeSubArr.length = 0;
+    }
+    //页面添加附加词、排除词
+    function addkeyWord(rdata) {
+        o_rules = rdata["o_rules"];
+        $('#g_addkey').attr("value", rdata["s_globaladdkey"]);
+        $('#g_notkey').attr("value", rdata["s_globalnotkey"]);
+        $('#s_globalclearkey').attr("value", rdata["s_globalclearkey"]);
+        dataMap["s_globalclearkey"] = rdata["s_globalclearkey"];
+        dataMap["s_globaladdkey"] = rdata["s_globaladdkey"];
+        dataMap["s_globalnotkey"] = rdata["s_globalnotkey"];
+
+        $('#keywords').dataTable().fnClearTable();
+        $('#keywords').dataTable().fnAddData(rdata["o_rules"]);
+
+    }
+    //表格全选
+    function allSelect(obj) {
+        if ($(obj).is(':checked')) {
+            keyTableChecked = true;
+            keyTableAllChecked = true;
+            $('input[name="ckb-keyid"]').each(function () {
+                this.checked = true;
+                selectIndex.push($(this).parents("tr").index() + 1);
+            })
+        }else {
+            selectIndex.length = 0;
+            keyTableChecked = false;
+            keyTableAllChecked = false;
+            $('input[name="ckb-keyid"]').each(function () {
+                this.checked = false;
+            })
+        }
+    }
+    //表格单个选中
+    function singleSelect(obj) {
+        var tag = true;
+        selectIndex.length = 0;
+        $('input[name="ckb-keyid"]').each(function () {
+            if (this.checked) {
+                keyTableChecked = true;
+                selectIndex.push($(this).parents("tr").index() + 1);
+            }else {
+                tag = false
+            }
+        });
+        if (tag) {
+            keyTableAllChecked = true;
+            $('#allCheckbox').prop("checked", true);
+        }else {
+            keyTableAllChecked = false;
+            $('#allCheckbox').prop("checked", false);
+        }
+    }
+    //保存标签
+    function saveTag() {
+        let obj = checkArea(dataMap.s_area, dataMap.s_city, dataMap.s_district);
+        if (!obj.flag) {
+            let msg = "选择的" + obj.v1 + "和" + obj.v2 + "生成es语句时会产生错误,请重新选择其中一个";
+            showMsg(msg);
+            return
+        }
+        //通用规则采购单位行业和关键词中的采购单位行业只能存在一个
+        if (o_rules.length > 0) {
+            let tag = false;
+            for (var i in o_rules) {
+                if (o_rules[i].s_buyerclass) {
+                    tag = true
+                }
+            }
+            if (tag && dataMap.s_globalbuyerclass) {
+                showMsg("保存失败,采购单位行业在通用规则和关键词规则中不能同时存在!")
+                return;
+            }
+        }
+        //关键词、附加词、排除词存在,则必须有相应的匹配方式
+        if (dataMap.s_globaladdkey && !dataMap.s_globaladdkeymatch) {
+            showMsg("保存失败,全局附加词必须有对应的匹配方式!")
+            return;
+        }
+        if (dataMap.s_globalnotkey && !dataMap.s_globalnotkeymatch) {
+            showMsg("保存失败,全局附加词必须有对应的匹配方式!")
+            return;
+        }
+        if (dataMap.s_globalclearkey && !dataMap.s_globalclearkeymatch) {
+            showMsg("保存失败,全局清理词说必须有对应的匹配方式!")
+            return;
+        }
+        if (o_rules.length > 0) {
+            for (const i in o_rules) {
+                if (o_rules[i].s_matchkey && !o_rules[i].s_keymatch) {
+                    showMsg("保存失败,关键词规则中有关键词未对应匹配方式!")
+                    return;
+                }
+                if (o_rules[i].s_addkey && !o_rules[i].s_addkeymatch) {
+                    showMsg("保存失败,关键词规则中有附加词未对应匹配方式!")
+                    return;
+                }
+                if (o_rules[i].s_notkey && !o_rules[i].s_notkeymatch) {
+                    showMsg("保存失败,关键词规则中有排除词未对应匹配方式!")
+                    return;
+                }
+            }
+        }
+        dataMap["s_tagname"] = $('#tagname').val();
+        dataMap["s_globaladdkey"] = $('#g_addkey').val();
+        dataMap["s_globalnotkey"] = $('#g_notkey').val();
+        dataMap["s_globalclearkey"] = $('#s_globalclearkey').val();
+        if (dataMap["s_tagname"] == "") {
+            alert("请填写必须字段!")
+            return
+        }
+        if ($('#starttime').find("input").val() != "") {
+            var start = $('#starttime').find("input").val();
+            start = new Date(start).getTime() / 1000;
+            dataMap["i_starttime"] = start;
+        }
+        if ($('#endtime').find("input").val() != "") {
+            var end = $('#endtime').find("input").val();
+            end = new Date(end).getTime() / 1000;
+            dataMap["i_endtime"] = end;
+        }
+        var budget = "";
+        if ($('#s_budget_min').val() != "") {
+            budget = "大于" + $('#s_budget_min').val()
+        }
+        if ($('#s_budget_max').val() != "") {
+            if (budget == "") {
+                budget = "小于" + $('#s_budget_max').val()
+            }else {
+                budget = budget + ",小于" + $('#s_budget_max').val()
+            }
+        }
+        dataMap["s_budgetlimit"] = budget
+        var bidamount = "";
+        if ($('#s_bidamount_min').val() != "") {
+            bidamount = "大于" + $('#s_bidamount_min').val()
+        }
+        if ($('#s_bidamount_max').val() != "") {
+            if (bidamount == "") {
+                bidamount = "小于" + $('#s_bidamount_max').val()
+            }else {
+                bidamount = bidamount + ",小于" + $('#s_bidamount_max').val()
+            }
+        }
+        dataMap["s_bidamountlimit"] = bidamount
+        if ($('#maxnum').val() !== "") {
+            dataMap["i_maxnum"] = $('#maxnum').val();
+        }
+        if (dataMap.s_globaladdkeymatch.indexOf("1") > -1 || dataMap.s_globaladdkeymatch.indexOf("2") > -1 || dataMap.s_globaladdkeymatch.indexOf("3") > -1 ||
+            dataMap.s_globaladdkeymatch.indexOf("4") > -1 || dataMap.s_globaladdkeymatch.indexOf("5") > -1) {
+            dataMap["i_globaladdkeytype"] = 1
+        }else if (dataMap.s_globaladdkeymatch.indexOf("6") > -1 || dataMap.s_globaladdkeymatch.indexOf("7") > -1 ||
+            dataMap.s_globaladdkeymatch.indexOf("8") > -1 || dataMap.s_globaladdkeymatch.indexOf("9") > -1) {
+            dataMap["i_globaladdkeytype"] = 2
+        }else {
+            dataMap["i_globaladdkeytype"] = 0
+        }
+        if (dataMap.s_globalnotkeymatch.indexOf("1") > -1 || dataMap.s_globalnotkeymatch.indexOf("2") > -1 || dataMap.s_globalnotkeymatch.indexOf("3") > -1 ||
+            dataMap.s_globalnotkeymatch.indexOf("4") > -1 || dataMap.s_globalnotkeymatch.indexOf("5") > -1) {
+            dataMap["i_globalnotkeytype"] = 1
+        }else if (dataMap.s_globalnotkeymatch.indexOf("6") > -1 || dataMap.s_globalnotkeymatch.indexOf("7") > -1 ||
+            dataMap.s_globalnotkeymatch.indexOf("8") > -1 || dataMap.s_globalnotkeymatch.indexOf("9") > -1) {
+            dataMap["i_globalnotkeytype"] = 2
+        }else {
+            dataMap["i_globalnotkeytype"] = 0
+        }
+        dataMap["o_rules"] = JSON.stringify(o_rules);
+        dataMap["ids"] = ids
+        dataMap["departmentName"] = $("#departmentName").val();
+        console.log(dataMap)
+        $.ajax({
+            url: "/service/cuser/rule/history/Edit",
+             type: 'POST',
+             data: dataMap,
+             success: function (r) {
+                 if (r.rep && r.id) {
+                    dataMap["id"]= r.id;
+                    dataMap["s_dataid"] = r.s_dataid;
+                     $('#es_div').show();
+                     $('#estxt').val(r.s_esquery);
+                    state = 2;
+                    showTip("保存成功",1000);
+                 } else {
+                    showTip("保存失败",1000);
+                 }
+             }
+        })
+    }
+
+    //导入标签
+    function importRule() {
+        $("#file").click();
+    }
+    $(function() {
+        $("#uploadform").find("input").change(function () {
+            var val = $(this).val() ? $(this).val() : "";
+            if (val.indexOf(".xlsx") < 0) {
+                showMsg("文件格式非法", function () {
+                });
+            } else {
+                var myform = new FormData();
+                myform.append('xlsx', $("#file")[0].files[0]);
+                $.ajax({
+                    url: "/service/rule/import",
+                    type: 'POST',
+                    data: myform,
+                    async: false,
+                    contentType: false,//不要设置Content-Type请求头
+                    processData: false,// 使数据不做处理
+                    success: function (r) {
+                        if (r.rep) {
+                           // dataMap["id"] = r.id;
+                            addkeyWord(r.rdata);
+                            s_dataid = r.s_dataid;
+                            showTip("导入成功",1000);
+                        } else {
+                            showTip("导入失败",1000);
+                        }
+                    }
+                })
+            }
+        })
+    });
+    //生成数据
+    function produceData() {
+        if (dataMap.id == undefined || dataMap.id == "") {
+            alert("请先保存标签");
+            return
+        }
+        com.maskShow("正在生成数据...");
+        $.ajax({
+            url:"/service/cuser/produce",
+            type:"post",
+            data: {"id": dataMap.id},
+            success:function(r){
+                com.maskHide();
+                if(r.rep){
+                    dataMap.i_estotal = r.count
+                    showTip("数据生成成功", 1000);
+                }else{
+                    showTip("数据生成失败",1000);
+                }
+            }
+        })
+    }
+    //预览数据
+    function previewData() {
+        if (state == 2) {
+           myDataTable = $('#previewData').DataTable({
+                "destroy": true,
+                "paging": true,
+                "lengthChange": false,
+                "searching": false,
+                "ordering": false,
+                "info": true,
+                "autoWidth": true,
+                "serverSide": true,
+                "ajax": {
+                    url: "/service/rule/preview",
+                    type: "post",
+                    data: {"s_dataid": dataMap.s_dataid},
+                },
+                "language": {
+                    "url": "/dist/js/dataTables.chinese.lang"
+                },
+                "fnDrawCallback": function () {
+                    $("ul.pagination").prepend("&nbsp;&nbsp;&nbsp;转到第 <input type='text' id='changePage'   style='width:20px;'> 页    <a type='text' href='javascript:void(0);' id='dataTable-btn' style='text-align:center'>GO</a>");
+                    $('#dataTable-btn').click(function (e) {
+                        var redirectpage = 0;
+                        if ($("#changePage").val() && $("#changePage").val() > 0) {
+                            var redirectpage = $("#changePage").val() - 1;
+                        }
+                        myDataTable.page(redirectpage).draw(false);
+                    });
+                    this.api().column(0).nodes().each(function (cell, i) {
+                        cell.innerHTML = i + 1;
+                    });
+                },
+                "columns": [
+                    {"data": null, width: "2%"},
+                    {"data": function(row) {
+                            if (row.s_matchkey) {
+                                return row.s_matchkey
+                            }else {
+                                return ""
+                            }
+                        }, width: "8%"},
+                    {"data": function(row) {
+                            if (row.area) {
+                                return row.area
+                            }else {
+                                return ""
+                            }
+                        }, width: "4%"},
+                    {"data": function(row) {
+                            if (row.city) {
+                                return row.city
+                            }else {
+                                return ""
+                            }
+                        }, width: "4%"},
+                    {"data": function (row) {
+                            if (row.s_jyhref) {
+                                tmp = '<a class="" target="_blank" href='+row.s_jyhref+'>'+row.title+'</a>';
+                                return tmp
+                            }else {
+                                return row.title
+                            }
+                        }, width: "5%"},
+                    {"data": function(row) {
+                            if (row.subtype) {
+                                return row.subtype
+                            }else {
+                                return ""
+                            }
+                        }, width: "4%"},
+                    {"data": function (row) {
+                            var str = com.trimStr(row.detail);
+                            if (str.length > 14) {
+                                return str.substring(0, 14) + "..."
+                            }else {
+                                return str
+                            }
+                        }, width: "5%"},
+                    {"data": function(row) {
+                            var dt = new Date();
+                            dt.setTime(parseInt(row.publishtime)*1000);
+                            return dt.format("yyyy-MM-dd")
+                        }, width: "4%"},
+                    {"data": function (row) {
+                            if (row.href) {
+                                var shref = row.href;
+                                tmp = '<a class="" target="_blank" href='+shref+'>公告地址</a>';
+                                return tmp
+                            }else {
+                                return ""
+                            }
+                        }, width: "5%"},
+                    {"data": function (row) {
+                            if (row.projectscope) {
+                                if (row.projectscope.length > 14) {
+                                    return row.projectscope.substring(0, 14) + "..."
+                                } else {
+                                    return row.projectscope
+                                }
+                            }
+                            return ""
+                        }, width: "5%"},
+                    {"data": "projectname", width: "5%"},
+                    {"data": function (row) {
+                            if (row.projectscope) {
+                                return row.projectscope
+                            }
+                            return ""
+                        }, width: "3%"},
+                    {"data": function (row) {
+                            if (row.budget == undefined) {
+                                return ""
+                            }else {
+                                return row.budget
+                            }
+                        }, width: "3%"},
+                    {"data": function (row) {
+                            if (row.bidamount == undefined) {
+                                return ""
+                            }else {
+                                return row.bidamount
+                            }
+                        }, width: "3%"},
+                    {"data": "bidopentime", width: "3%", render: function (val) {
+                            if (val) {
+                                var dt = new Date();
+                                dt.setTime(parseInt(val)*1000);
+                                return dt.format("yyyy-MM-dd")
+                            }else {
+                                return ""
+                            }
+                        }},
+                    {"data": "buyer", width: "3%", render: function (val) {
+                            if (val == undefined) {
+                                val = ""
+                            }
+                            return val
+                        }},
+                    {"data": function (row) {
+                            if (row.buyerperson) {
+                                return row.buyerperson
+                            }else {
+                                return ""
+                            }
+                        }, width: "4%"},
+                    {"data": function (row) {
+                            if (row.buyertel) {
+                                return row.buyertel
+                            }else {
+                                return ""
+                            }
+                        }, width: "4%"},
+                    {"data": "agency", width: "5%", render: function (val) {
+                            if (val == undefined) {
+                                val = ""
+                            }
+                            return val
+                        }},
+                    {"data": "winner", width: "3%", render: function (val) {
+                            if (val == undefined) {
+                                val = ""
+                            }
+                            return val
+                        }},
+                    {"data": function (row) {
+                            if (row.winnerperson) {
+                                return row.winnerperson
+                            }else {
+                                return ""
+                            }
+                        }, width: "4%"},
+                    {"data": function (row) {
+                            if (row.winnertel) {
+                                return row.winnertel
+                            }else {
+                                return ""
+                            }
+                        }, width: "4%"},
+                    {"data": function (row) {
+                            if (row.legal_person) {
+                                return row.legal_person
+                            }else {
+                                return ""
+                            }
+                        }, width: "4%"},
+                    {"data": function (row) {
+                            if (row.company_phone) {
+                                return row.company_phone
+                            }else {
+                                return ""
+                            }
+                        }, width: "4%"},
+                    {"data": function (row) {
+                            if (row.company_email) {
+                                return row.company_email
+                            }else {
+                                return ""
+                            }
+                        }, width: "4%"},
+                ]
+            });
+            $('#modal-preview').modal("show");
+            if (dataMap.i_extfieldstype == 1) {
+                var str = "标准字段包 " + dataMap.i_maxnum + "/" + dataMap.i_estotal;
+                $('#dataCount').html(str)
+            } else {
+                var str = "高级字段包 " + dataMap.i_maxnum + "/" + dataMap.i_estotal;
+                $('#dataCount').html(str)
+            }
+        }else {
+            alert("请先保存标签");
+        }
+    }
+    
+    //下载es
+    function downloades(){
+      if(state==1){
+        alert("请先保存标签")
+      }else{
+        $("#download").attr("href","/service/rule/exportes?id="+dataMap["id"])
+      }
+    }
+    
+    //存档
+    function archive(){
+      if(state == 1){
+        alert("请先保存标签");
+        return
+      }else{
+        showConfirm("是否存档?", function() {
+          $.ajax({
+              url:"/service/rule/archive",
+              type:"post",
+              data:{"_id":dataMap["id"]},
+              success:function(r){
+                if(r.rep){
+                  showTip("存档成功",1000);
+                  //ttable.ajax.reload();
+                }else{
+                  showTip("存档失败",1000);
+                }
+              }
+          })
+        });
+      }
+    }
+
+    var key_flag = -1;      // 0:新增,1:修改
+    //关键词表格新增
+    function add_Rule() {
+        key_flag = 0;
+        $('#keyword_txt').val("");
+        $('#addword_txt').val("");
+        $('#notword_txt').val("");
+        $('#modal-add-keyword').modal("show");
+    }
+    //关键词修改
+    function modify_Rule(){
+        if (selectIndex.length == 1) {
+            key_flag = 1;
+            var index = selectIndex[0] - 1;
+            $('#modal-add-keyword').modal("show");
+            $('#keyword_txt').val(o_rules[index].s_matchkey);
+            $('#addword_txt').val(o_rules[index].s_addkey);
+            $('#notword_txt').val(o_rules[index].s_notkey);
+        }else {
+            showTip("请选中需要修改的关键词,且只能选中一条", 1000)
+        }
+
+    }
+    function saveKeyword() {
+        var key = $('#keyword_txt').val();
+        var addkey = $('#addword_txt').val();
+        var notkey = $('#notword_txt').val();
+        if (key != "") {
+            var rule = {};
+            rule["s_matchkey"] = key.replace(",", ",");
+            if (addkey != "") rule["s_addkey"] = addkey.replace(",", ",");
+            if (notkey != "") rule["s_notkey"] = notkey.replace(",", ",");
+            $('#modal-add-keyword').modal('hide');
+            if (key_flag == 1) {
+                var index = selectIndex[0] - 1;
+                o_rules.splice(index, 1, rule)
+            }else {
+                o_rules.push(rule)
+            }
+            $('#keywords').dataTable().fnClearTable();
+            $('#keywords').dataTable().fnAddData(o_rules);
+        }else {
+            showTip("关键词不能为空", 500)
+        }
+    }
+    //关键词删除
+    function del_Rule() {
+        if (keyTableChecked && selectIndex.length > 0) {
+            showConfirm("确定删除?", function() {
+                var tmp = []
+                for (var i in selectIndex) {
+                    var j = selectIndex[i] - 1;
+                    tmp.push(j)
+                }
+                o_rules = deleteArrayVal(o_rules, tmp)
+                $('#keywords').dataTable().fnClearTable();
+                $('#keywords').dataTable().fnAddData(o_rules);
+
+                keyTableChecked = false;
+                if (o_rules.length == 0) {
+                    $('#allCheckbox').prop("checked", false);
+                    $('#keywords').dataTable().fnClearTable();
+                }
+            })
+        }else {
+            showTip("请选中需要删除的关键词", 500)
+        }
+    }
+
+    function checkArea(area, city, district) {
+        let flag = true;
+        let v1 = "";
+        let v2 = ""
+        if (area == "" && city == "" && district == "") {
+            flag = true
+        }
+        if (city != "" && area != "") {
+            var areaArr = area.split(",");
+            var cityArr = city.split(",");
+            for (var i in areaArr) {
+                if (cityMap[areaArr[i]] != undefined) {
+                    for (var j in cityArr) {
+                        if ($.inArray(cityArr[j], cityMap[areaArr[i]]) > -1) {
+                            flag = false;
+                            v1 = areaArr[i];
+                            v2 = cityArr[j]
+                        }
+                    }
+                }
+            }
+            return {flag: flag, v1: v1, v2: v2}
+        }
+        if (city != "" && district != "") {
+            var cityArr = city.split(",");
+            var districtArr = district.split(",");
+            for (var i in cityArr) {
+                if (districtMap[cityArr[i]] != undefined) {
+                    for (var j in districtArr) {
+                        let dis = districtArr[j].split("-")[1];
+                        if ($.inArray(dis, districtMap[cityArr[i]]) > -1) {
+                            flag = false;
+                            v1 = cityArr[i];
+                            v2 = districtArr[j]
+                        }
+                    }
+                }
+            }
+            return {flag: flag, v1: v1, v2: v2}
+        }
+        return {flag: flag, v1: v1, v2: v2}
+    }
+</script>

+ 817 - 0
src/web/templates/private/customer_history.html

@@ -0,0 +1,817 @@
+{{include "com/inc.html"}}
+<!-- Main Header -->
+{{include "com/header.html"}}
+<!-- Left side column. 权限菜单 -->
+{{include "com/menu.html"}}
+<style>
+</style>
+<div class="content-wrapper">
+    <section class="content-header">
+        <h1>编辑客户
+        </h1>
+        <ol class="breadcrumb">
+            <li><a href="#"><i class="fa fa-dashboard"></i> 首页</a></li>
+            <li><a href="/service/customer/list"> 客户列表</a></li>
+            <li><a href="#"> 编辑客户</a></li>
+        </ol>
+    </section>
+    <!-- Main content -->
+    <section class="content">
+        <div class="tab-content">
+            <div class="nav-tabs-custom">
+                <ul class="nav nav-tabs edit-step">
+                    <li class="active" data-mode="guide"><a href="#tab_1" data-toggle="tab" aria-expanded="true">客户信息</a></li>
+                    <li data-mode="guide" class="tab_2"><a href="#tab_2" data-toggle="tab" aria-expanded="false">私有标签</a></li>
+                </ul>
+                <div class="tab-content">
+
+                    <div class="tab-pane active" id="tab_1">
+                        <form class="form-horizontal">
+                            <div class="box box-primary">
+                                <div class="box-header with-border">
+                                    <i class="fa fa-tag"></i>
+                                    <h3 class="box-title">基本信息</h3>
+                                    <a class="btn btn-primary btn-sm" style="float: right;margin-top: 7px;margin-right: 10px" onclick="saveCuser()"><i class="fa fa-fw fa-file-text fa-lg"></i>保存</a>
+                                </div>
+                                <div class="box-body">
+                                    <div class="form-group">
+                                        <label class="col-sm-2 control-label"><span style="color:red;">* </span>客户名称</label>
+                                        <div class="col-sm-2">
+                                            <input type="text" class="form-control" id="customername" placeholder="公司名称"
+                                                   value="{{.T.data.s_name}}" required>
+                                        </div>
+                                        <label class="col-sm-2 control-label"><span style="color:red;">* </span>销售人员</label>
+                                        <div class="col-sm-2">
+                                            <input type="text" class="form-control" id="seller" placeholder="销售人员名称"
+                                                   value="{{.T.data.s_salesperson}}" required>
+                                        </div>
+                                        <label class="col-sm-2 control-label">客户联系人</label>
+                                        <div class="col-sm-2">
+                                            <input type="text" class="form-control" id="contract" placeholder="客户联系人名称"
+                                                   value="{{.T.data.s_customername}}">
+                                        </div>
+                                    </div>
+                                    <div id="pushDiv">
+                                        <div class="box-header with-border">
+                                            <i class="fa fa-tag"></i>
+                                            <h3 class="box-title">企业客户信息</h3>
+                                        </div>
+                                        <div class="box-body">
+                                            <div class="form-group">
+                                                <label class="col-sm-2 control-label"><span style="color:red;">* </span>推送模式</label>
+                                                <div class="col-sm-2">
+                                                    <select class="form-control selectpicker" id="pushModelSelect">
+                                                        <option value=0>全局模式</option>
+                                                        <option value=1>部门模式</option>
+                                                        <option value=3>部门+标签模式</option>
+                                                        <option value=4>规则模式(不去重)</option>
+                                                    </select>
+                                                </div>
+                                                <label class="col-sm-2 control-label"><span style="color:red;">* </span>推送方式</label>
+                                                <div class="col-sm-2">
+                                                    <select class="form-control selectpicker" id="pushTypeSelect">
+                                                        <option value=0 code="email">邮箱</option>
+                                                        <option value=1 code="interface">接口</option>
+                                                    </select>
+                                                </div>
+                                                <label class="col-sm-2 control-label">数据扩展</label>
+                                                <div class="col-sm-2">
+                                                    <select class="form-control selectpicker" multiple id="expandSelect">
+                                                        <option value="hospitalgrade" >医院等级</option>
+                                                        <option value="enterpise" >企业信息</option>
+                                                    </select>
+                                                    <script>
+                                                        $('#expandSelect').on('hide.bs.select', function () {
+                                                            var arr = $('#expandSelect').val();
+                                                            var arrStr = "";
+                                                            for (var i in arr) {
+                                                                if (arrStr === "") {
+                                                                    arrStr = arr[i]
+                                                                } else {
+                                                                    arrStr = arrStr + "," + arr[i]
+                                                                }
+                                                            }
+                                                            dataMap["s_extends"] = arrStr
+                                                        })
+                                                    </script>
+                                                </div>
+                                            </div>
+                                        </div>
+                                    </div>
+                                    <div class="box-header with-border">
+                                        <i class="fa fa-tag"></i>
+                                        <h3 class="box-title">部门信息</h3>
+                                    </div>
+                                    <table id="departTable" class="table table-bordered">
+                                        <thead>
+                                        <div style="margin: 10px 10px 0px;">
+                                            <a class="btn btn-default" onclick="showDepart()">新增</a>
+                                            <a class="btn btn-default" onclick="modify_Depart()">修改</a>
+                                            <a class="btn btn-default" onclick="del_Depart()">删除</a>
+                                            <a class="btn btn-default" onclick="select_Depart()">选择</a>
+                                        </div>
+                                        <tr>
+                                            <th></th>
+                                            <th>编号</th>
+                                            <th>部门名称</th>
+                                            <th>部门联系人</th>
+                                            <th>修改时间</th>
+                                            <th>是否启用</th>
+                                            <th>功能</th>
+                                        </tr>
+                                        </thead>
+                                    </table>
+                                </div>
+                            </div>
+                        </form>
+                    </div>
+                    <div class="tab-pane" id="tab_2">
+                        <div class="box box-primary">
+                            <div class="box-header with-border">
+                                <i class="fa fa-bookmark"></i>
+                                <h3 class="box-title">私有标签</h3>
+                                <a class="btn btn-primary btn-sm" style="float: right;margin-top: 7px;margin-right: 10px" href="/service/euser/tag/edit?cid={{.T.data._id}}"><i class="fa fa-fw fa-file-code-o fa-lg"></i>新建标签</a>
+                            </div>
+                            <table id="tagrule" class="table table-bordered">
+                                <thead>
+                                <tr>
+                                    <th>编号</th>
+                                    <th>标签名称</th>
+                                    <th>修改时间</th>
+                                    <th>启用状态</th>
+                                    <th>操作</th>
+                                    <th>功能</th>
+                                </tr>
+                                </thead>
+                            </table>
+                        </div>
+                    </div>
+                </div>
+            </div>
+        </div>
+    </section>
+</div>
+
+<!-- 新增部门 -->
+<div class="modal fade" id="modal-add-depart" tabindex="-1" role="dialog" aria-hidden="true">
+    <div class="modal-dialog" style="width: 50%">
+        <div class="modal-content">
+            <div class="modal-header">
+                <div class="modal-header">
+                    <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
+                    <div class="edit-form">
+                        <div class="edit-info">
+                            <span class="glyphicon glyphicon-ok-sign" aria-hidden="true"></span>
+                            <span>新增部门</span>
+                        </div>
+                        <form class="form-horizontal">
+                            <div class="form-group">
+                                <label class="col-sm-3 control-label"><span style="color:red;">* </span>部门名称</label>
+                                <div class="col-sm-6">
+                                    <input class="form-control" placeholder="销售部、销售部-xx子部门" id="depart_name"/></div>
+                            </div>
+                            <div class="form-group">
+                                <label class="col-sm-3 control-label">联系人</label>
+                                <div class="col-sm-6">
+                                    <input class="form-control" placeholder="输入联系人名称" id="depart_contract"/></div>
+                            </div>
+                            <div class="form-group">
+                                <label class="col-sm-3 control-label">是否启用</label>
+                                <div class="col-sm-6">
+                                    <select class="form-control selectpicker" id="isuseSelect">
+                                        <option value=1 selected>是</option>
+                                        <option value=0>否</option>
+                                    </select>
+                                </div>
+                            </div>
+                        </form>
+                    </div>
+                </div>
+                <div class="modal-footer">
+                    <input type="button" onclick="saveDepart()" class="btn btn-primary saveBtn" value="保存">
+                    <input type="button" onclick="cancelModel()" class="btn btn-default" style="margin-left: 24px"
+                           value="取消">
+                </div>
+            </div>
+        </div>
+    </div><!-- /.modal -->
+</div>
+<!-- 关联客户规则 -->
+<div class="modal fade" id="modal-associated" tabindex="-1" role="dialog" aria-hidden="true">
+    <div class="modal-dialog" style="width: 60%">
+        <div class="modal-content">
+            <div class="modal-header">
+                <div class="modal-header">
+                    <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
+                    <div class="edit-form">
+                        <div class="edit-info">
+                            <span class="glyphicon glyphicon-ok-sign" aria-hidden="true"></span>
+                            <span>关联客户规则</span>
+                        </div>
+                        <div class="content">
+                            <table id="cuserRule" class="table table-bordered">
+                                <thead>
+                                <tr>
+                                    <th>编号</th>
+                                    <th><input type="checkbox" id="selrow" onclick="allSelect(this)"/></th>
+                                    <th>规则名称</th>
+                                    <th>部门名称</th>
+                                    <th>创建时间</th>
+                                </tr>
+                                </thead>
+                            </table>
+                        </div>
+                    </div>
+                </div>
+                <div class="modal-footer">
+                    <input type="button" onclick="saveTagrule()" class="btn btn-primary saveBtn" value="保存">
+                    <input type="button" onclick="cancelModel()" class="btn btn-default" style="margin-left: 24px"
+                           value="取消">
+                </div>
+            </div>
+        </div>
+    </div><!-- /.modal -->
+</div>
+
+<!--历史规则-->
+<div class="modal fade" id="modal-history" tabindex="-1" role="dialog" aria-hidden="true">
+    <div class="modal-dialog" style="width: 80%">
+        <div class="modal-content">
+            <div class="modal-header">
+                <div class="modal-header">
+                    <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
+                    <div class="edit-form">
+                        <div class="edit-info">
+                            <span class="glyphicon glyphicon-ok-sign" aria-hidden="true"></span>
+                            <span>选择历史规则</span>
+                        </div>
+                        <table id="historyRule" class="table table-bordered">
+                            <thead>
+                            <tr>
+                                <th><input type="checkbox" id="selrow" onclick="allSelect(this)"/></th>
+                                <th>编号</th>
+                                <th>部门名称</th>
+                                <th>规则名称</th>
+                                <th>修改时间</th>
+                                <th>部门状态</th>
+                                <th>规则状态</th>
+                                <th class="hidden">规则id  </th>
+
+                            </tr>
+                            </thead>
+                            <tbody class="history_rules"></tbody>
+                        </table>
+                    </div>
+                </div>
+                <div class="modal-footer">
+                    <input type="button" onclick="saveHistoryRule(this)" class="btn btn-primary saveBtn" value="确定">
+                    <input type="button" onclick="cancelModel()" class="btn btn-default" style="margin-left: 24px"
+                           value="取消">
+                </div>
+            </div>
+        </div>
+    </div><!-- /.modal -->
+</div>
+{{include "com/footer.html"}}
+<script>
+    menuActive("/customer/list");
+    var dataMap = {{.T.data}}
+    var departArr = {{.T.depart}}
+    var tagruleArr = []
+    var ruleArr = []
+
+    var selectIndex = [];               //选中的编号
+
+    $(document).ready(function () {
+        if (dataMap.i_state == 0) {
+            $('.tab_2').hide()
+            $('#pushDiv').hide()
+        }else {
+            tagruleArr = {{.T.tagrule}}
+            $('#pushSwitch').bootstrapSwitch({
+                onText: "开启",
+                offText: "关闭",
+            })
+            if (dataMap.i_push && dataMap.i_push == 1) {
+                $('#pushSwitch').bootstrapSwitch('state', true);
+            }else {
+                $('#pushSwitch').bootstrapSwitch('state', false);
+            }
+            $('#pushModelSelect').val(dataMap.i_pushmodel)
+            if (dataMap.i_pushtype == 0) {
+                $('#pushTypeSelect').val(0)
+            }else {
+                $('#pushTypeSelect').val(1)
+            }
+            if (dataMap.s_extends && dataMap.s_extends.indexOf("hospitalgrade") >= 0 && dataMap.s_extends.indexOf("enterpise") >= 0) {
+                $('#expandSelect').find("option[value = hospitalgrade]").attr("selected","selected");
+                $('#expandSelect').find("option[value = enterpise]").attr("selected","selected");
+            }else if (dataMap.s_extends && dataMap.s_extends.indexOf("hospitalgrade") >= 0){
+                $('#expandSelect').find("option[value = hospitalgrade]").attr("selected","selected");
+            }else if (dataMap.s_extends && dataMap.s_extends.indexOf("enterpise") >= 0) {
+                $('#expandSelect').find("option[value = enterpise]").attr("selected","selected");
+            }
+        }
+        $('#departTable').DataTable({
+            "paging": false,
+            "lengthChange": true,
+            "searching": false,
+            "ordering": false,
+            "info": false,
+            "autoWidth": true,
+            "language": {
+                "url": "/dist/js/dataTables.chinese.lang"
+            },
+            "data": departArr,
+            "fnDrawCallback": function () {
+                this.api().column(1).nodes().each(function (cell, i) {
+                    cell.innerHTML = i + 1;
+                });
+            },
+            "columns": [
+                {"data":function() {
+                        return `<input type="checkbox" name="ckb-depart" onclick="singleSelect1(this)" style="text-align: center">`
+                    }, width: "3%"},
+                {"data": null, width: "3%"},
+                {"data": "s_name", width: "10%"},
+                {"data": function (row) {
+                        if (row.s_contract == "") {
+                            return ""
+                        }else {
+                            return row.s_contract
+                        }
+                    }, width: "10%"},
+                {"data": "i_updatetime", render: function (val) {
+                        var dt = new Date();
+                        dt.setTime(parseInt(val) * 1000);
+                        return dt.format("yyyy-MM-dd hh:mm:ss")
+                    }, width: "20%"},
+                {"data": "i_isuse", render: function (val, a, row) {
+                        tmp = ""
+                        if(val == 1){
+                            tmp="<a href='#' title='停用' onclick='setup(\""+row._id+"\",false)'><i class='fa fa-fw fa-circle text-green'></i></a>已启用"
+                        }else{
+                            tmp="<a href='#' title='启用' onclick='setup(\""+row._id+"\",true)'><i class='fa fa-fw fa-circle text-red'></i></a>未启用"
+                        }
+                        return tmp
+                    }, width: "10%" },
+                {"data": "_id", width: "20%", render: function (val, a, row, pos) {
+                        var s = [val, row.s_userid];
+                        tmp = '<div>' +
+                            '<a class="btn btn-sm btn-primary" href="/service/cuser/rule/list?ids=' + s + '">规则列表</a>&nbsp&nbsp'
+                            '</div>';
+                        return tmp
+                    }
+                }
+            ],
+        });
+        $('#tagrule').DataTable({
+            "paging": false,
+            "lengthChange": false,
+            "searching": false,
+            "ordering": false,
+            "info": true,
+            "autoWidth": false,
+            "language": {
+                "url": "/dist/js/dataTables.chinese.lang"
+            },
+            "fnDrawCallback": function () {
+                this.api().column(0).nodes().each(function (cell, i) {
+                    cell.innerHTML = i + 1;
+                });
+            },
+            "data": tagruleArr,
+            "columns": [
+                {"data": null, width: "1%"},
+                {"data": "s_name", width: "10%"},
+                {"data": "i_updatetime", width:"9%", render: function (val) {
+                        var dt = new Date()
+                        dt.setTime(parseInt(val) * 1000);
+                        return dt.format("yyyy-MM-dd hh:mm:ss")
+                    }},
+                {"data": "i_isuse", render: function (val, a, row) {
+                        tmp = ""
+                        if(val == 1){
+                            tmp="<a href='#' title='停用' onclick='setupTag(\""+row._id+"\",false)'><i class='fa fa-fw fa-circle text-green'></i></a>已启用"
+                        }else{
+                            tmp="<a href='#' title='启用' onclick='setupTag(\""+row._id+"\",true)'><i class='fa fa-fw fa-circle text-red'></i></a>未启用"
+                        }
+                        return tmp
+                    }, width: "10%" },
+                {"data": "_id", width:"11%",render: function (val, a, row, pos) {
+                        tmp = '<div>' +
+                            '<a class="btn btn-sm btn-primary" href="/service/euser/tag/edit?id='+val+'&cid='+row.s_userid+'">编辑</a>&nbsp&nbsp'+
+                            '<a class="btn btn-sm btn-danger" onclick="delTag(\''+val+'\')">删除</a>'+
+                            '</div>';
+                        return tmp
+                    }},
+                {"data": "_id", width:"11%",render: function (val, a, row, pos) {
+                        var s = [val, row.s_userid]
+                        tmp = '<div>' +
+                            '<a class="btn btn-sm btn-primary" onclick="associated(\''+s+'\')">关联数据范围</a>&nbsp&nbsp'+
+                            '</div>';
+                        return tmp
+                    }},
+            ],
+        });
+    })
+    function cancelModel() {
+        $('#modal-add-depart').modal("hide")
+        $('#modal-associated').modal("hide")
+    }
+    //表格全选
+    function allSelect(obj) {
+        if ($(obj).is(':checked')) {
+            keyTableChecked = true;
+            keyTableAllChecked = true;
+            $('input[name="ckb-rule"]').each(function () {
+                this.checked = true;
+                selectIndex.push($(this).parents("tr").index());
+            })
+        } else {
+            selectIndex.length = 0;
+            keyTableChecked = false;
+            keyTableAllChecked = false;
+            $('input[name="ckb-keyid"]').each(function () {
+                this.checked = false;
+            })
+        }
+    }
+    function singleSelect(obj) {
+        var tag = true;
+        selectIndex.length = 0;
+        $('input[name="ckb-rule"]').each(function () {
+            if (this.checked) {
+                keyTableChecked = true;
+                selectIndex.push($(this).parents("tr").index());
+            }else {
+                tag = false
+            }
+        });
+        if (tag) {
+            keyTableAllChecked = true;
+            $('#allCheckbox').prop("checked", true);
+        }else {
+            keyTableAllChecked = false;
+            $('#allCheckbox').prop("checked", false);
+        }
+    }
+    function singleSelect1(obj) {
+        var tag = true;
+        selectIndex.length = 0;
+        $('input[name="ckb-depart"]').each(function () {
+            if (this.checked) {
+                keyTableChecked = true;
+                selectIndex.push($(this).parents("tr").index() + 1);
+            }else {
+                tag = false
+            }
+        });
+        if (tag) {
+            keyTableAllChecked = true;
+            $('#allCheckbox').prop("checked", true);
+        }else {
+            keyTableAllChecked = false;
+            $('#allCheckbox').prop("checked", false);
+        }
+    }
+
+    function saveCuser() {
+        var name = $('#customername').val();
+        var contract = $('#contract').val();
+        var saleperson = $('#seller').val();
+        if (name == "" || saleperson == "") {
+            showTip("请填写必填字段!", 1000)
+            return
+        }
+        dataMap.s_name = name
+        dataMap.s_customername = contract
+        dataMap.s_salesperson = saleperson
+        if (dataMap.i_state == 1) {
+            dataMap.i_pushmodel = $('#pushModelSelect option:selected').val()
+            dataMap.i_pushtype = $('#pushTypeSelect option:selected').val()
+        }
+        dataMap.id = dataMap._id
+        $.ajax({
+            url: "/service/customer/save",
+            type: 'POST',
+            data: dataMap,
+            success: function (r) {
+                if (r.rep) {
+                    showTip("保存成功", 500);
+                } else {
+                    showTip("保存失败", 500);
+                }
+            }
+        })
+    }
+
+    var depart_flag = -1;      // 0:新增,1:修改
+    function showDepart() {
+        depart_flag = 0
+        $('#depart_name').val("");
+        $('#depart_contract').val("");
+        $('#s_depart_name').val("");
+        $('#modal-add-depart').modal("show")
+    }
+    //修改部门
+    function modify_Depart() {
+        depart_flag = 1
+        var index = selectIndex[0] - 1;
+        $('#depart_name').val(departArr[index].s_name);
+        $('#depart_contract').val(departArr[index].s_contract);
+        $('#s_depart_name').val(departArr[index].s_subname)
+        $("#isuseSelect").val(departArr[index].i_isuse)
+        $('#modal-add-depart').modal("show");
+    }
+    function del_Depart() {
+        if (keyTableChecked && selectIndex.length > 0) {
+            showConfirm("确定删除?", function() {
+                var tmp = []
+                for (var i in selectIndex) {
+                    var j = selectIndex[i] - 1;
+                    tmp.push(j)
+                }
+                var did = departArr[tmp[0]]._id
+                var bol = false
+                $.ajax({
+                    url: "/service/depart/del",
+                    type: "post",
+                    data: {"id": did},
+                    success: function (r) {
+                        bol = r.rep
+                        if(r.rep){
+                            // showTip("删除成功", 1000)
+                            window.location.reload()
+                        }else{
+                            showTip("删除失败", 1000);
+                        }
+                    }
+                })
+                if (!bol) {
+                    return
+                }
+                departArr = deleteArrayVal(departArr, tmp)
+
+                keyTableChecked = false;
+                if (departArr.length == 0) {
+                    $('#allCheckbox').prop("checked", false);
+                    $('#departTable').dataTable().fnClearTable();
+                }else {
+                    $('#departTable').dataTable().fnClearTable();
+                    $('#departTable').dataTable().fnAddData(departArr);
+                }
+            })
+        }else {
+            showTip("请选中需要删除的关键词", 1000)
+        }
+    }
+    function saveDepart() {
+        var name = $('#depart_name').val();
+        if (name != "") {
+            if (depart_flag == 1) {
+                var index = selectIndex[0] - 1;
+                var depart = departArr[index]
+                depart["id"] = depart["_id"]
+            }else {
+                var depart = {}
+                depart["s_userid"] = dataMap._id
+            }
+            depart["s_name"] = name;
+            depart["s_contract"] = $('#depart_contract').val();
+            depart["s_subname"] = $('#s_depart_name').val()
+            depart["i_isuse"] =  $('#isuseSelect').val()
+            if (depart_flag == 1) {
+                var index = selectIndex[0] - 1;
+                departArr.splice(index, 1, depart)
+            }else {
+                departArr.push(depart)
+            }
+            $('#departTable').dataTable().fnClearTable();
+            $('#departTable').dataTable().fnAddData(departArr);
+            $('#modal-add-depart').modal('hide');
+
+            $.ajax({
+                url: "/service/depart/edit",
+                type: "post",
+                data: depart,
+                success: function (r) {
+                    if(r.rep){
+                        // showTip("保存成功", 1000)
+                        window.location.reload()
+                    }else{
+                        showTip("保存失败", 1000);
+                    }
+                }
+            })
+        }else {
+            showTip("部门名称不能为空", 500)
+        }
+    }
+    function setup(val, bol) {
+        var msg = ""
+        var i_isuse = 0
+        if (bol) {
+            msg = "确定启用?"
+            i_isuse = 1
+        }else {
+            msg = "确定停用?"
+            i_isuse = 0
+        }
+        showConfirm(msg, function() {
+            $.ajax({
+                url:"/service/customer/setup",
+                type:"post",
+                data:{"_id": val, "i_isuse": i_isuse},
+                success:function(r){
+                    if(r.rep){
+                        // showTip("设置成功", 1000);
+                        window.location.reload()
+                    }else{
+                        showTip("设置失败", 1000);
+                    }
+                }
+            })
+        });
+    }
+    function setupTag(val, bol) {
+        var msg = ""
+        var i_isuse = 0
+        if (bol) {
+            msg = "确定启用?"
+            i_isuse = 1
+        }else {
+            msg = "确定停用?"
+            i_isuse = 0
+        }
+        showConfirm(msg, function() {
+            $.ajax({
+                url:"/service/euser/tag/setup",
+                type:"post",
+                data:{"_id": val, "i_isuse": i_isuse},
+                success:function(r){
+                    if(r.rep){
+                        // showTip("设置成功", 1000);
+                        window.location.reload()
+                    }else{
+                        showTip("设置失败", 1000);
+                    }
+                }
+            })
+        });
+    }
+    function delTag(val) {
+        showConfirm("确定删除?", function () {
+            $.ajax({
+                url: "/service/euser/tag/del",
+                type: "post",
+                data: {"_id": val},
+                success: function (r) {
+                    if (r.rep) {
+                        window.location.reload()
+                    } else {
+                        showTip("删除失败", 1000);
+                    }
+                }
+            })
+        });
+    }
+    var tagid = ""
+    //关联规则
+    function associated(val) {
+        tagid = val.split(",")[0]
+        $('#cuserRule').DataTable({
+            "destroy": true,
+            "paging": true,
+            "lengthChange": false,
+            "searching": false,
+            "ordering": false,
+            "info": true,
+            "autoWidth": false,
+            "serverSide": true,
+            "ajax": {
+                url: "/service/tag/ruleList",
+                type: "post",
+                data: {"eid": val.split(",")[1]},
+            },
+            "language": {
+                "url": "/dist/js/dataTables.chinese.lang"
+            },
+            "fnDrawCallback": function () {
+                this.api().column(0).nodes().each(function (cell, i) {
+                    cell.innerHTML = i + 1;
+                });
+            },
+            "columns": [
+                {"data": null, width: "1%"},
+                {"data": "", width: "1%", render: function() {
+                        return `<input type="checkbox" name="ckb-rule" onclick="singleSelect(this)" style="text-align: center">`
+                    }},
+                {"data": "s_tagname", width: "20%"},
+                {"data": "s_depart", width: "10%"},
+                {"data": "i_updatetime", width:"10%", render: function (val) {
+                        var dt = new Date()
+                        dt.setTime(parseInt(val) * 1000);
+                        return dt.format("yyyy-MM-dd hh:mm:ss")
+                    }},
+            ],
+        });
+        $('#modal-associated').modal("show")
+    }
+    function saveTagrule() {
+        var ids = [];
+        var table = $('#cuserRule').dataTable();
+        var nTrs = table.fnGetNodes();
+        for (var i = 0; i < nTrs.length; i++) {
+            if ($(nTrs[i].childNodes[1]).find("input").prop("checked")) {
+                ids.push(table.fnGetData(nTrs[i])._id);
+            }
+        }
+        $.ajax({
+            url: "/service/euser/tag/associated",
+            type: "post",
+            data: {"_id": tagid, "ids": ids.join(",")},
+            success: function (r) {
+                if (r.rep) {
+                    // window.location.reload()
+                    showTip("关联成功", 1000);
+                    $('#modal-associated').modal("hide")
+                } else {
+                    showTip("关联失败", 1000);
+                }
+            }
+        })
+    }
+    function select_Depart() {
+        //获取用户自定义规则
+        var c_id = getParam("id")
+        $.ajax({
+           url:"/service/customer/history/allrule",
+           type:"POST",
+           data:{"c_id":c_id},
+           success:function (r) {
+                if (r.status=="success"){
+                    var html = ""
+                    var number = 1
+                    for (var i=0;i<r.data.length;i++){
+                        if (r.data[i].department_rule.length!=0){
+                            for (var l=0;l<r.data[i].department_rule.length;l++){
+                                html += '<tr><td><input type="checkbox" id="selrow"/></td><td>'+number+'</td><td>'+r.data[i].depart_name+'</td><td>'+r.data[i].department_rule[l].s_tagname+'</td><td>'+formattime(r.data[i].department_rule[l].i_updatetime)+'</td>';
+                                if (r.data[i].department_isuse ==1){
+                                    html += "<td>"+"已启用"
+                                }else{
+                                    html += "</td><td>"+"未启用"
+                                }
+                                if (r.data[i].department_rule[l].i_isuse ==1){
+                                    html += "</td><td>"+"已启用"+"</td>"
+                                }else{
+                                    html += "</td><td>"+"未启用"+"</td>"
+                                }
+                                html += "<td class='hidden'>"+r.data[i].department_rule[l]._id+"</td>"
+                                number++
+                            }
+                        }
+                    }
+                    $(".history_rules").html(html)
+                }
+           }
+        });
+        $("#modal-history").modal("show");
+    }
+    function getParam(name) {
+        var search = document.location.search;
+        // alert(search);
+        var pattern = new RegExp("[?&]" + name + "\=([^&]+)", "g");
+        var matcher = pattern.exec(search);
+        var items = null;
+        if (null != matcher) {
+            try {
+                items = decodeURIComponent(decodeURIComponent(matcher[1]));
+            } catch (e) {
+                try {
+                    items = decodeURIComponent(matcher[1]);
+                } catch (e) {
+                    items = matcher[1];
+                }
+            }
+        }
+        return items;
+    };
+    function formattime(date) {
+        let _date = new Date(parseInt(date)*1000);
+        let y = _date.getFullYear();
+        let m = _date.getMonth() + 1;
+        m = m < 10 ? ('0' + m) : m;
+        let d = _date.getDate();
+        d = d < 10 ? ('0' + d) : d;
+        // console.log( y + '-' + m + '-' + d + ' ' + ' ' + h + ':' + minute + ':' + second)
+        let dates = y + '-' + m + '-' + d;
+
+        return dates;
+    }
+    
+    function saveHistoryRule(e) {
+
+    }
+</script>
+/service/cuser/rule/list

+ 6 - 2
src/web/templates/private/customer_list.html

@@ -108,8 +108,9 @@
                         if (row.i_state == 1) {
                             tmp = '<div>' +
                                 '<a class="btn btn-sm btn-default" disabled="true">转企</a>&nbsp;&nbsp;' +
-                                '<a class="btn btn-sm btn-warning" onclick="SynEnt(\'' + val + '\')">同步</a>' +
-                                '</div>';
+                                '<a class="btn btn-sm btn-warning" onclick="SynEnt(\'' + val + '\')">同步</a>&nbsp;&nbsp;' +
+                                '<a class="btn btn-sm btn-default" onclick="historyData(\''+ val +'\')">历史数据</a>'+
+                                '</div>'
                         }else {
                             tmp = '<div>' +
                                 '<a class="btn btn-sm btn-primary" onclick="toEnt(\''+ val +'\')">转企</a>&nbsp;&nbsp;' +
@@ -173,4 +174,7 @@
             })
         })
     }
+    function historyData(val) {
+        window.location.href = "/service/customer/history?id=" + val
+    }
 </script>