Jianghan %!s(int64=2) %!d(string=hai) anos
pai
achega
1544666e4c

+ 8 - 7
src/config.json

@@ -1,12 +1,12 @@
 {
     "port": "7000",
-    "mgodb": "192.168.3.207:27092",
+    "mgodb": "192.168.3.166:27082",
     "dbsize": 10,
-    "dbname": "datavalidation",
+    "dbname": "wjh",
     "alltocoll": "marked",
     "defaultpwd": "123",
     "bidding":{
-        "addr":"192.168.3.207:27092",
+        "addr":"192.168.3.166:27082",
         "db": "wjh",
         "coll1": "bidding",
         "coll2": "bidding_back",
@@ -15,7 +15,7 @@
         "password": ""
     },
     "extract":{
-        "addr": "192.168.3.207:27092",
+        "addr": "192.168.3.166:27082",
         "db": "mxs",
         "coll1": "extract",
         "coll2": "extract2",
@@ -23,16 +23,17 @@
         "size": 10
     },
     "jy": {
-        "addr":"192.168.3.207:27092",
+        "addr":"192.168.3.166:27082",
         "db": "qfw",
         "history": "usermail_history",
         "user": "user",
         "size": 10
     },
     "es":{
-        "addr": "http://127.0.0.1:9800",
+        "addr": "http://127.0.0.1:19800",
         "index": "bidding",
-        "itype": "bidding",
+        "user": "",
+        "password": "",
         "pool": 12
     },
     "customerfield":{

+ 1 - 0
src/front/front.go

@@ -25,6 +25,7 @@ type Front struct {
 	updatePwd          xweb.Mapper `xweb:"/front/updatepwd"`          //更新密码
 	userGroup          xweb.Mapper `xweb:"/front/group"`              //查所有用户组
 	userAll            xweb.Mapper `xweb:"/front/user"`               //查询用户列表
+	userTask           xweb.Mapper `xweb:"/front/user/task/new"`      //领取数据(创建用户任务)
 	userGroupNew       xweb.Mapper `xweb:"/front/group/new"`          //用户组新建
 	groupModify        xweb.Mapper `xweb:"/front/group/group/modify"` //用户组修改
 	userNew            xweb.Mapper `xweb:"/front/group/user/new"`     //新建用户

+ 3 - 1
src/front/project.go

@@ -1174,6 +1174,8 @@ func GetDataById(idInfoArr []util.Data, importType, s_sourceinfo string, success
 			if qu.ObjToString((*bidData)["toptype"]) != "采购意向" {
 				// 导入数据时,删掉purchasinglist(排除采购意向数据)
 				delete(*bidData, "purchasinglist")
+				// 删除多包字段 20230518
+				delete(*bidData, "package")
 			}
 			if bidData != nil && len(*bidData) > 0 { //bidding表数据存在
 				//2.查extract
@@ -1253,7 +1255,7 @@ func GetDataById(idInfoArr []util.Data, importType, s_sourceinfo string, success
 				(*bidData)["filetext"] = util.GetFileText(*bidData)
 				// 6.es查询项目合并信息
 				esQ := `{"query":{"bool":{"must":[{"term":{"ids":"` + id + `"}}]}}}`
-				info := util.Es.Get("projectset", "projectset", esQ)
+				info := util.Es.Get("projectset", esQ)
 				if len(*info) > 0 {
 					projectId := qu.ObjToString((*info)[0]["_id"])
 					project, _ := util.MgoE.FindById(util.ProjectColl, projectId, map[string]interface{}{"ids": 1})

+ 2 - 0
src/front/remark.go

@@ -109,6 +109,7 @@ func (f *Front) RemarkDetail() {
 	f.T["worder_new"] = rep["worder_new"]
 	f.T["pcl_new"] = rep["pcl_new"]
 	f.T["pkg_new"] = rep["pkg_new"]
+	f.T["keyword"] = rep["matchkey"]
 	f.T["nextid"] = GetNextDataId(did, coll, tid) //下一条id
 	_ = f.Render("project/remark_detail.html", &f.T)
 }
@@ -132,6 +133,7 @@ func getDetail(id, coll string) map[string]interface{} {
 	if !strings.HasPrefix(href, "http") {
 		baseInfo["href"] = "https://" + href
 	}
+	rep["matchkey"] = baseInfo["matchkey"]
 	rep["info"] = baseInfo
 	common, timeplace, other := setExtComMap(baseInfo, bzInfo) //拼装抽取common值
 	rep["common"] = common

+ 155 - 28
src/front/user.go

@@ -27,33 +27,33 @@ func (f *Front) Login() {
 		imgCode := f.GetString("imgCode")
 		passwordEn := qu.SE.EncodeString(password)
 		qu.Debug(username, passwordEn, imgCode)
-		//session, err := store.Get(f.Request, "dataTagLoginImgCode")
-		//if err != nil {
-		//	qu.Debug("图片验证码session获取失败-%s \n", username)
-		//	f.ServeJson(map[string]interface{}{
-		//		"code":    0,
-		//		"status":  false,
-		//		"message": "获取失败",
-		//	})
-		//	return
-		//}
-		//code := qu.ObjToString(session.Values["dataTagLoginImgCode"])
-		//if code == "" {
-		//	qu.Debug("图片验证码过期-%s \n", username)
-		//	f.ServeJson(map[string]interface{}{
-		//		"checked": false,
-		//		"message": "图片验证码过期",
-		//	})
-		//	return
-		//}
-		//if !captcha.VerifyString(code, imgCode) {
-		//	qu.Debug("图片验证码错误-%s \n", username)
-		//	f.ServeJson(map[string]interface{}{
-		//		"checked": false,
-		//		"message": "图片验证码错误",
-		//	})
-		//	return
-		//}
+		session, err := store.Get(f.Request, "dataTagLoginImgCode")
+		if err != nil {
+			qu.Debug("图片验证码session获取失败-%s \n", username)
+			f.ServeJson(map[string]interface{}{
+				"code":    0,
+				"status":  false,
+				"message": "获取失败",
+			})
+			return
+		}
+		code := qu.ObjToString(session.Values["dataTagLoginImgCode"])
+		if code == "" {
+			qu.Debug("图片验证码过期-%s \n", username)
+			f.ServeJson(map[string]interface{}{
+				"checked": false,
+				"message": "图片验证码过期",
+			})
+			return
+		}
+		if !captcha.VerifyString(code, imgCode) {
+			qu.Debug("图片验证码错误-%s \n", username)
+			f.ServeJson(map[string]interface{}{
+				"checked": false,
+				"message": "图片验证码错误",
+			})
+			return
+		}
 		query := map[string]interface{}{
 			"s_login":    username,
 			"s_password": passwordEn,
@@ -428,6 +428,9 @@ func (f *Front) UserAll() {
 			}
 		}
 		data, _ := Mgo.Find("s_user", query, `{"i_updatetime":-1}`, nil, false, start, limit)
+		for _, m := range *data {
+			m["s_password"] = qu.SE.DecodeString(qu.ObjToString(m["s_password"]))
+		}
 		count := Mgo.Count("s_user", query)
 		f.ServeJson(map[string]interface{}{
 			"draw":            draw,
@@ -766,7 +769,6 @@ func (f *Front) UserTaskList() {
 			}
 		}
 		count := Mgo.Count(TASKCOLLNAME, query)
-		qu.Debug("Query:", query, count)
 		list, _ := Mgo.Find(TASKCOLLNAME, query, map[string]interface{}{"_id": -1}, nil, false, start, limit)
 		for _, l := range *list {
 			if status := qu.ObjToString(l["s_status"]); status == "进行中" { //更新任务进度
@@ -1014,3 +1016,128 @@ func RetrieveCloseTaskByUser(sourceInfo, username string, userTaskIdStatus map[s
 	}
 	return
 }
+
+func (f *Front) UserTask() {
+	defer qu.Catch()
+
+	defer qu.Catch()
+	user := f.GetSession("user").(map[string]interface{})
+	//s_role := qu.ObjToString(user["i_role"])
+	s_userid := qu.ObjToString(user["id"])
+	gid := qu.ObjToString(user["s_groupid"])
+	s_login := qu.ObjToString(user["s_login"])
+	s_name := qu.ObjToString(user["s_name"])
+
+	if f.Method() == "POST" {
+
+		num, err := f.GetInteger("num")
+		grouptaskid := f.GetString("_id")
+		if err != nil {
+			f.ServeJson(map[string]interface{}{"success": false, "msg": "分发数据异常"})
+			return
+		}
+		success := false
+		msg := ""
+		groupTask, _ := Mgo.FindById(TASKCOLLNAME, grouptaskid, nil)
+		if len(*groupTask) == 0 {
+			qu.Debug("GroupTask Find Error:", grouptaskid)
+			msg = "用户组任务:" + grouptaskid + "查询失败"
+			f.ServeJson(map[string]interface{}{"success": false, "msg": msg})
+			return
+		}
+		sourceinfo := qu.ObjToString((*groupTask)["s_sourceinfo"])
+		userTaskId := primitive.NewObjectID()
+		userTask := map[string]interface{}{
+			"_id":           userTaskId,                                    //生成任务id
+			"s_projectid":   qu.ObjToString((*groupTask)["s_projectid"]),   //项目标识
+			"s_projectname": qu.ObjToString((*groupTask)["s_projectname"]), //项目名称
+			"s_status":      "未开始",                                         //任务状态
+			"s_personid":    s_userid,                                      //任务负责人标识
+			"s_personname":  s_name,                                        //任务负责人
+			"s_login":       s_login,                                       //用户账号
+			"s_groupname":   qu.ObjToString((*groupTask)["s_groupname"]),   //用户组名称
+			"s_groupid":     qu.ObjToString((*groupTask)["s_groupid"]),     //用户组标识
+			"i_givenum":     num,                                           //分发数据量
+			"s_createname":  s_name,                                        //创建人
+			"i_createtime":  time.Now().Unix(),                             //创建时间
+			"s_progress":    "0%",                                          //完成进度
+			"s_sourceinfo":  sourceinfo,                                    //源数据表
+			"s_stype":       "user",                                        //任务类型
+			"s_parentid":    grouptaskid,                                   //父任务及用户组任务id
+			"s_entname":     qu.ObjToString((*groupTask)["s_entname"]),     //公司名称
+			"s_departname":  qu.ObjToString((*groupTask)["s_departname"]),  //部门名称
+			"s_rulename":    qu.ObjToString((*groupTask)["s_rulename"]),    //规则名称
+		}
+
+		//分发数据后更新对应用户组任务信息
+		var userTaskIdArr []string
+		success = Mgo.UpdateById(TASKCOLLNAME, grouptaskid, map[string]interface{}{
+			"$push": map[string]interface{}{
+				"v_sonids": map[string]interface{}{
+					"$each": append(userTaskIdArr, mgo.BsonIdToSId(userTaskId)),
+				},
+			},
+		})
+		if !success {
+			msg = "更新用户组任务:" + grouptaskid + "关联用户任务失败"
+		} else { //用户分发任务
+			success = Mgo.SaveByOriID(TASKCOLLNAME, userTask)
+			if success {
+				//用户分发任务后更新该用户组任务的状态和开始时间
+				Mgo.Update(TASKCOLLNAME,
+					map[string]interface{}{
+						"_id":      mgo.StringTOBsonId(grouptaskid),
+						"s_status": "未开始",
+					},
+					map[string]interface{}{
+						"$set": map[string]interface{}{
+							"s_status":    "进行中",
+							"i_starttime": time.Now().Unix(),
+						}},
+					false, false)
+				msg = "任务分发成功"
+
+				ut := Task{
+					UserId:   s_userid,
+					GiveNum:  num,
+					UserName: s_login,
+				}
+				userTaskIdInfo := map[string]Task{}
+				userTaskIdInfo[mgo.BsonIdToSId(userTaskId)] = ut
+				UpdateSourceInfoByUser(sourceinfo, grouptaskid, userTaskIdInfo) //用户分发任务成功后,同时更新任务数据源表
+			}
+		}
+		f.ServeJson(map[string]interface{}{"success": success, "msg": msg})
+	} else {
+		// 聚合
+		//match := bson.M{"$match": bson.M{"s_groupid": gid, "s_stype": "group"}}
+		//group := bson.M{"$group": bson.M{"_id": bson.M{"pid": "$s_projectid", "info": "$s_sourceinfo", "name": "$s_projectname"}, "count": bson.M{"$sum": "$i_givenum"}}}
+		//info := make([]map[string]interface{}, 0)
+		//Mgo.GetMgoConn().DB(Mgo.DbName).C(TASKCOLLNAME).Pipe([]map[string]interface{}{match, group}).All(&info)
+		info, _ := Mgo.Find(TASKCOLLNAME, bson.M{"s_groupid": gid, "s_stype": "group"}, nil, bson.M{}, false, -1, -1)
+		var data []map[string]interface{}
+		for _, m := range *info {
+			d1 := make(map[string]interface{})
+			d1["_id"] = mgo.BsonIdToSId(m["_id"])
+			d1["pid"] = m["s_projectid"]
+			d1["coll"] = m["s_sourceinfo"]
+			d1["pname"] = m["s_projectname"]
+			d1["size"] = m["i_givenum"]
+			c := Mgo.Count(qu.ObjToString(d1["coll"]), bson.M{"b_isgiveuser": false, "s_groupid": gid})
+			d1["ungive"] = c
+			d1["isgive"] = qu.IntAll(d1["size"]) - c
+			data = append(data, d1)
+		}
+		//for _, dm := range data {
+		//	c := Mgo.Count(qu.ObjToString(dm["coll"]), bson.M{"b_isgiveuser": false, "s_groupid": gid})
+		//	dm["ungive"] = c
+		//	dm["isgive"] = qu.IntAll(dm["size"]) - c
+		//}
+		if data != nil && len(data) > 0 {
+			f.T["data"] = data
+		} else {
+			f.T["data"] = make([]map[string]interface{}, 0)
+		}
+		_ = f.Render("user/task_user_new.html", &f.T)
+	}
+}

+ 1 - 0
src/main.go

@@ -31,6 +31,7 @@ func init() {
 	xweb.RootApp().AppConfig.SessionTimeout = 1 * time.Hour
 	xweb.RootApp().Logger.SetOutputLevel(4)
 	//xweb.AddTmplVar("add", func(a, b int) int { return a + b })
+
 }
 
 func main() {

+ 6 - 5
src/util/config.go

@@ -4,7 +4,6 @@ import (
 	"go.mongodb.org/mongo-driver/mongo"
 	"mongodb"
 	qu "qfw/util"
-	"qfw/util/elastic"
 	"sort"
 	"sync"
 )
@@ -16,7 +15,7 @@ import (
 var (
 	Sysconfig            map[string]interface{} //配置文件
 	Quaconfig            map[string]interface{} //质量配置文件
-	Es                   *elastic.Elastic
+	Es                   *Elastic
 	Mgo                  *mongodb.MongodbSim
 	AllToColl            string              //所有标注数据汇总表
 	Password             string              //默认登陆密码
@@ -103,9 +102,11 @@ func InitConfig() {
 	MgoB.InitPool()
 
 	esConf := Sysconfig["es"].(map[string]interface{})
-	Es = &elastic.Elastic{
-		S_esurl: qu.ObjToString(esConf["addr"]), //http://172.17.145.170:9800
-		I_size:  qu.IntAllDef(esConf["pool"], 10),
+	Es = &Elastic{
+		S_esurl:  qu.ObjToString(esConf["addr"]), //http://172.17.145.170:9800
+		I_size:   qu.IntAllDef(esConf["pool"], 10),
+		Username: qu.ObjToString(esConf["user"]),
+		Password: qu.ObjToString(esConf["password"]),
 	}
 	Es.InitElasticSize()
 

+ 423 - 0
src/util/elasticSim.go

@@ -0,0 +1,423 @@
+package util
+
+import (
+	"context"
+	"encoding/json"
+	"errors"
+	"fmt"
+	es "gopkg.in/olivere/elastic.v7"
+	"log"
+	"qfw/util"
+	"runtime"
+	"strings"
+	"sync"
+	"time"
+)
+
+type Elastic struct {
+	S_esurl      string
+	I_size       int
+	Addrs        []string
+	Pool         chan *es.Client
+	lastTime     int64
+	lastTimeLock sync.Mutex
+	ntimeout     int
+	Username     string
+	Password     string
+}
+
+func (e *Elastic) InitElasticSize() {
+	e.Pool = make(chan *es.Client, e.I_size)
+	for _, s := range strings.Split(e.S_esurl, ",") {
+		e.Addrs = append(e.Addrs, s)
+	}
+	log.Println(e.Password, e.Username)
+	for i := 0; i < e.I_size; i++ {
+		client, _ := es.NewClient(es.SetURL(e.Addrs...), es.SetBasicAuth(e.Username, e.Password), es.SetMaxRetries(2), es.SetSniff(false))
+		e.Pool <- client
+	}
+}
+
+//关闭连接
+func (e *Elastic) DestoryEsConn(client *es.Client) {
+	select {
+	case e.Pool <- client:
+		break
+	case <-time.After(time.Second * 1):
+		if client != nil {
+			client.Stop()
+		}
+		client = nil
+	}
+}
+
+func (e *Elastic) GetEsConn() *es.Client {
+	select {
+	case c := <-e.Pool:
+		if c == nil || !c.IsRunning() {
+			log.Println("new esclient.", len(e.Pool))
+			client, err := es.NewClient(es.SetURL(e.Addrs...), es.SetBasicAuth(e.Username, e.Password),
+				es.SetSniff(false))
+			if err == nil && client.IsRunning() {
+				return client
+			}
+		}
+		return c
+	case <-time.After(time.Second * 4):
+		//超时
+		e.ntimeout++
+		e.lastTimeLock.Lock()
+		defer e.lastTimeLock.Unlock()
+		//12秒后允许创建链接
+		c := time.Now().Unix() - e.lastTime
+		if c > 12 {
+			e.lastTime = time.Now().Unix()
+			log.Println("add client..", len(e.Pool))
+			c, _ := es.NewClient(es.SetURL(e.Addrs...), es.SetBasicAuth(e.Username, e.Password), es.SetSniff(false))
+			go func() {
+				for i := 0; i < 2; i++ {
+					client, _ := es.NewClient(es.SetURL(e.Addrs...), es.SetBasicAuth(e.Username, e.Password), es.SetSniff(false))
+					e.Pool <- client
+				}
+			}()
+			return c
+		}
+		return nil
+	}
+}
+
+func (e *Elastic) Get(index, query string) *[]map[string]interface{} {
+	client := e.GetEsConn()
+	defer func() {
+		go e.DestoryEsConn(client)
+	}()
+	var res []map[string]interface{}
+	if client != nil {
+		defer func() {
+			if r := recover(); r != nil {
+				log.Println("[E]", r)
+				for skip := 1; ; skip++ {
+					_, file, line, ok := runtime.Caller(skip)
+					if !ok {
+						break
+					}
+					go log.Printf("%v,%v\n", file, line)
+				}
+			}
+		}()
+		searchResult, err := client.Search().Index(index).Source(query).Do(context.Background())
+		if err != nil {
+			log.Println("从ES查询出错", err.Error())
+			return nil
+		}
+		if searchResult.Hits != nil {
+			resNum := len(searchResult.Hits.Hits)
+			if resNum < 5000 {
+				res = make([]map[string]interface{}, resNum)
+				for i, hit := range searchResult.Hits.Hits {
+					parseErr := json.Unmarshal(hit.Source, &res[i])
+					if parseErr == nil && hit.Highlight != nil && res[i] != nil {
+						res[i]["highlight"] = map[string][]string(hit.Highlight)
+					}
+				}
+			} else {
+				log.Println("查询结果太多,查询到:", resNum, "条")
+			}
+		}
+	}
+	return &res
+}
+
+//关闭elastic
+func (e *Elastic) Close() {
+	for i := 0; i < e.I_size; i++ {
+		cli := <-e.Pool
+		cli.Stop()
+		cli = nil
+	}
+	e.Pool = nil
+	e = nil
+}
+
+//获取连接
+//func (e *Elastic) GetEsConn() (c *es.Client) {
+//	defer util.Catch()
+//	select {
+//	case c = <-e.Pool:
+//		if c == nil || !c.IsRunning() {
+//			client, err := es.NewClient(es.SetURL(addrs...),
+//				es.SetMaxRetries(2), es.SetSniff(false))
+//			if err == nil && client.IsRunning() {
+//				return client
+//			}
+//			return nil
+//		}
+//		return
+//	case <-time.After(time.Second * 7):
+//		//超时
+//		ntimeout++
+//		log.Println("timeout times:", ntimeout)
+//		return nil
+//	}
+//}
+
+func (e *Elastic) BulkSave(index string, obj []map[string]interface{}) {
+	client := e.GetEsConn()
+	defer e.DestoryEsConn(client)
+	if client != nil {
+		req := client.Bulk()
+		for _, v := range obj {
+			//if isDelBefore {
+			//	req = req.Add(es.NewBulkDeleteRequest().Index(index).Id(fmt.Sprintf("%v", v["_id"])))
+			//}
+			id := util.ObjToString(v["_id"])
+			delete(v, "_id")
+			req = req.Add(es.NewBulkIndexRequest().Index(index).Id(id).Doc(v))
+		}
+		_, err := req.Do(context.Background())
+		if err != nil {
+			log.Println("批量保存到ES出错", err.Error())
+		}
+	}
+}
+
+//根据id删除索引对象
+func (e *Elastic) DelById(index, itype, id string) bool {
+	client := e.GetEsConn()
+	defer e.DestoryEsConn(client)
+	b := false
+	if client != nil {
+		var err error
+		_, err = client.Delete().Index(index).Type(itype).Id(id).Do(context.Background())
+		if err != nil {
+			log.Println("更新检索出错:", err.Error())
+		} else {
+			b = true
+		}
+	}
+	return b
+}
+
+func (e *Elastic) GetNoLimit(index, query string) *[]map[string]interface{} {
+	client := e.GetEsConn()
+	defer e.DestoryEsConn(client)
+	var res []map[string]interface{}
+	if client != nil {
+		defer func() {
+			if r := recover(); r != nil {
+				log.Println("[E]", r)
+				for skip := 1; ; skip++ {
+					_, file, line, ok := runtime.Caller(skip)
+					if !ok {
+						break
+					}
+					go log.Printf("%v,%v\n", file, line)
+				}
+			}
+		}()
+		searchResult, err := client.Search().Index(index).Source(query).Do(context.Background())
+		if err != nil {
+			log.Println("从ES查询出错", err.Error())
+			return nil
+		}
+
+		if searchResult.Hits != nil {
+			resNum := len(searchResult.Hits.Hits)
+			util.Debug(resNum)
+			res = make([]map[string]interface{}, resNum)
+			for i, hit := range searchResult.Hits.Hits {
+				json.Unmarshal(hit.Source, &res[i])
+			}
+		}
+	}
+	return &res
+}
+
+//func (e *Elastic) GetByIdField(index, itype, id, fields string) *map[string]interface{} {
+//	client := e.GetEsConn()
+//	defer e.DestoryEsConn(client)
+//	if client != nil {
+//		defer func() {
+//			if r := recover(); r != nil {
+//				log.Println("[E]", r)
+//				for skip := 1; ; skip++ {
+//					_, file, line, ok := runtime.Caller(skip)
+//					if !ok {
+//						break
+//					}
+//					go log.Printf("%v,%v\n", file, line)
+//				}
+//			}
+//		}()
+//		query := `{"query":{"term":{"_id":"` + id + `"}}`
+//		if len(fields) > 0 {
+//			query = query + `,"_source":[` + fields + `]`
+//		}
+//		query = query + "}"
+//		searchResult, err := client.Search().Index(index).Type(itype).Source(query).Do()
+//		if err != nil {
+//			log.Println("从ES查询出错", err.Error())
+//			return nil
+//		}
+//		var res map[string]interface{}
+//		if searchResult.Hits != nil {
+//			resNum := len(searchResult.Hits.Hits)
+//			if resNum == 1 {
+//				res = make(map[string]interface{})
+//				for _, hit := range searchResult.Hits.Hits {
+//					json.Unmarshal(*hit.Source., &res)
+//				}
+//				return &res
+//			}
+//		}
+//	}
+//	return nil
+//}
+
+func (e *Elastic) Count(index, itype string, query interface{}) int64 {
+	client := e.GetEsConn()
+	defer e.DestoryEsConn(client)
+	if client != nil {
+		defer func() {
+			if r := recover(); r != nil {
+				log.Println("[E]", r)
+				for skip := 1; ; skip++ {
+					_, file, line, ok := runtime.Caller(skip)
+					if !ok {
+						break
+					}
+					go log.Printf("%v,%v\n", file, line)
+				}
+			}
+		}()
+		var qq es.Query
+		if qi, ok2 := query.(es.Query); ok2 {
+			qq = qi
+		}
+		n, err := client.Count(index).Query(qq).Do(context.Background())
+		if err != nil {
+			log.Println("统计出错", err.Error())
+		}
+
+		return n
+	}
+	return 0
+}
+
+//更新一个字段
+//func (e *Elastic) BulkUpdateArr(index, itype string, update []map[string]string) {
+//	client := e.GetEsConn()
+//	defer e.DestoryEsConn(client)
+//	if client != nil {
+//		defer func() {
+//			if r := recover(); r != nil {
+//				log.Println("[E]", r)
+//				for skip := 1; ; skip++ {
+//					_, file, line, ok := runtime.Caller(skip)
+//					if !ok {
+//						break
+//					}
+//					go log.Printf("%v,%v\n", file, line)
+//				}
+//			}
+//		}()
+//		for _, data := range update {
+//			id := data["id"]
+//			updateStr := data["updateStr"]
+//			if id != "" && updateStr != "" {
+//				_, err := client.Update().Index(index).Type(itype).Id(id).Script(updateStr).ScriptLang("groovy").Do()
+//				if err != nil {
+//					log.Println("更新检索出错:", err.Error())
+//				}
+//			} else {
+//				log.Println("数据错误")
+//			}
+//		}
+//	}
+//}
+
+//更新多个字段
+//func (e *Elastic) BulkUpdateMultipleFields(index, itype string, arrs [][]map[string]interface{}) {
+//	client := e.GetEsConn()
+//	defer e.DestoryEsConn(client)
+//	if client != nil {
+//		defer func() {
+//			if r := recover(); r != nil {
+//				log.Println("[E]", r)
+//				for skip := 1; ; skip++ {
+//					_, file, line, ok := runtime.Caller(skip)
+//					if !ok {
+//						break
+//					}
+//					go log.Printf("%v,%v\n", file, line)
+//				}
+//			}
+//		}()
+//		for _, arr := range arrs {
+//			id := arr[0]["id"].(string)
+//			update := arr[1]["update"].([]string)
+//			for _, str := range update {
+//				_, err := client.Update().Index(index).Type(itype).Id(id).Script(str).ScriptLang("groovy").Do()
+//				if err != nil {
+//					log.Println("更新检索出错:", err.Error())
+//				}
+//			}
+//		}
+//	}
+//}
+
+// UpdateBulk 批量修改文档
+func (e *Elastic) UpdateBulk(index, itype string, docs ...[]map[string]interface{}) {
+	client := e.GetEsConn()
+	defer e.DestoryEsConn(client)
+	bulkService := client.Bulk().Index(index).Refresh("true")
+	bulkService.Type(itype)
+	for _, d := range docs {
+		id := d[0]["_id"].(string)
+		doc := es.NewBulkUpdateRequest().Id(id).Doc(d[1])
+		bulkService.Add(doc)
+	}
+	_, err := bulkService.Do(context.Background())
+	if err != nil {
+		fmt.Printf("UpdateBulk all success err is %v\n", err)
+	}
+	//if len(res.Failed()) > 0 {
+	//	fmt.Printf("UpdateBulk all success failed is %v\n", (res.Items[0]))
+	//}
+}
+
+// UpsertBulk 批量修改文档(不存在则插入)
+func (e *Elastic) UpsertBulk(ctx context.Context, index string, ids []string, docs []interface{}) error {
+	client := e.GetEsConn()
+	defer e.DestoryEsConn(client)
+	bulkService := client.Bulk().Index(index).Refresh("true")
+	bulkService.Type("bidding")
+	for i := range ids {
+		doc := es.NewBulkUpdateRequest().Id(ids[i]).Doc(docs[i]).Upsert(docs[i])
+		bulkService.Add(doc)
+	}
+	res, err := bulkService.Do(context.Background())
+	if err != nil {
+		return err
+	}
+	if len(res.Failed()) > 0 {
+		return errors.New(res.Failed()[0].Error.Reason)
+	}
+	return nil
+}
+
+// 批量删除
+func (e *Elastic) DeleteBulk(index string, ids []string) {
+	client := e.GetEsConn()
+	defer e.DestoryEsConn(client)
+	bulkService := client.Bulk().Index(index).Refresh("true")
+	bulkService.Type("bidding")
+	for i := range ids {
+		req := es.NewBulkDeleteRequest().Id(ids[i])
+		bulkService.Add(req)
+	}
+	res, err := bulkService.Do(context.Background())
+	if err != nil {
+		fmt.Printf("DeleteBulk success is %v\n", len(res.Succeeded()))
+	}
+}

+ 10 - 5
src/web/templates/project/remark_detail.html

@@ -208,6 +208,7 @@
                 <!--edit-->
                 <div style="padding: 5px;margin-left: 20px">
                     <label style="color: red">备注:<input type="text" id="remark" style="height: 25px;width: 300px;display: inline-block;"></label>
+                    <label style="color: red" id="keyword">关键词:</label>
                 </div>
                 <div class="edit one" v-for="(one,oindex) in editData" :key="oindex">
                     <!--one-->
@@ -249,11 +250,7 @@
                     </div>
                     <transition tag="div">
                         <div>
-                            <div class="button-box" v-if="one.showCheck&&one.show">
-                                <button class="pass a-button" @click.stop="addChild(one, oindex)"
-                                        style="font-size: 14px;width: auto;float:left;">新增[[one.title]]
-                                </button>
-                            </div>
+
                             <div class="edit-content" :class="{t:one.title == '基本字段'}" v-show="one.show">
                                 <!--two-->
 
@@ -369,6 +366,11 @@
                                     </transition>
                                 </div>
                             </div>
+                            <div class="button-box" v-if="one.showCheck&&one.show">
+                                <button class="pass a-button" @click.stop="addChild(one, oindex)"
+                                        style="font-size: 14px;width: auto;float:left;">新增[[one.title]]
+                                </button>
+                            </div>
                         </div>
                     </transition>
                 </div>
@@ -416,6 +418,9 @@
     let purchasing_all_tag = {{ .T.ck_pclistag }}           // 标的物是否全部标记
     let package_status = {{ .T.ck_package }}                //包标记
     let winneroder_status = {{ .T.ck_winnerorder }}         //中标候选人标记
+
+    $("#keyword").html({{ .T.keyword }})
+
     // 页面数据
     var pageDataMap = {
         '原文': {

+ 137 - 0
src/web/templates/user/task_user_new.html

@@ -0,0 +1,137 @@
+{{include "com/inc.html"}}
+<!-- Main Header -->
+{{include "com/header.html"}}
+<!-- Left side column. 权限菜单 -->
+{{include "com/menu.html"}}
+<div class="content-wrapper">
+    <section class="content-header">
+        <h1><small></small></h1>
+        <ol class="breadcrumb">
+            <li><a href="#"><i class="fa fa-dashboard"></i> 领取数据</a></li>
+        </ol>
+    </section>
+    <!-- Main content -->
+    <section class="content">
+        <div class="row">
+            <div class="col-xs-12">
+                <div class="box">
+                    <div class="box-body">
+
+                        <div class="box-body margin">
+                            <h4><i class="glyphicon glyphicon-exclamation-sign" style="margin-right: 6px"></i>数据情况</h4>
+
+                            {{range $index, $item := .T.data}}
+                            <div class="form-group" style="margin-left: 10px;margin-top: 20px">
+                                <div class="col-xs-5" style="width: auto">
+                                    <label class="form-inline">项目名称:
+                                        <input type="text" class="form-control" readonly value="{{$item.pname}}"></label>
+                                    <label class="form-inline">数据总量:
+                                        <input type="text" class="form-control" style="width: 80px" readonly value="{{$item.size}}"></label>
+                                    <label class="form-inline" style="margin-left: 5px">已分发:
+                                        <input type="text" class="form-control" style="width: 80px" readonly value="{{$item.isgive}}"></label>
+                                    <label class="form-inline" style="margin-left: 5px">待分发:
+                                        <input type="text" class="form-control" style="width: 80px" readonly value="{{$item.ungive}}"></label>
+                                    <label class="form-inline">操作:
+                                        <input type="button" class="btn btn-sm btn-primary" onclick="dispatchTask('{{$index}}')" value="认领">
+                                    </label>
+                                </div>
+                            </div>
+                            {{end}}
+                        </div>
+
+
+                    </div>
+                    <!-- /.box-body -->
+                </div>
+                <!-- /.box -->
+            </div>
+        </div>
+    </section>
+</div>
+
+
+<div class="modal fade" id="modal-create-task" 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-info">
+                        <span class="glyphicon glyphicon-tasks" aria-hidden="true"></span>
+                        <span class="h3">新建任务</span>
+                    </div>
+                    <div class="edit-form">
+                        <form id="modal-form-task" class="form-horizontal">
+                            <div class="modal-body modal-task">
+                                <div class="form-group margin-bottom" style="margin-left: 15px">
+                                    <label class="control-label form-inline h4">待分发数据总量(条):
+                                        <span id="modal-num"></span>
+                                    </label>
+                                </div>
+                                <hr>
+                                <div class="form-group group-item" style="margin-left: 10px;">
+                                    <label class="control-label form-inline" style="margin-left: 20px">数据量(条):
+                                        <input type="number" class="form-control" id="modal-data-num"></label>
+                                </div>
+                            </div>
+                        </form>
+                    </div>
+                </div>
+            </div>
+            <div class="modal-footer">
+                <input type="button" onclick="saveTask()" class="btn btn-primary" value="保存">
+                <input type="button" onclick="cancelModel()" class="btn btn-default" value="取消">
+            </div>
+        </div>
+    </div><!-- /.modal -->
+</div>
+
+
+{{include "com/footer.html"}}
+
+<script>
+    menuActive("/front/user/task/new");
+
+    let data = {{ .T.data }}
+    let currentId = ""
+
+    function dispatchTask(i) {
+        console.log(data[i])
+        if (data[i]['ungive'] === 0) {
+            showTip("没有可分发的数据")
+            return
+        }
+        $('#modal-create-task').modal('show')
+        currentId = data[i]["_id"]
+        $('#modal-num').html(data[i]['ungive'])
+        let num = document.getElementById("modal-data-num");
+        num.setAttribute("max", data[i]['ungive'])
+        num.onkeyup = function(){
+            this.value=this.value.replace(/\D/g,'');
+            if(num.value > data[i]['ungive']){
+                num.value = data[i]['ungive'];
+            }
+        }
+    }
+
+    function saveTask() {
+        console.log($('#modal-data-num').val())
+        let num = parseInt($('#modal-data-num').val())
+        if (num > 0) {
+            $.ajax({
+                url: "/front/user/task/new",
+                type: "POST",
+                data: {num: num, _id: currentId},
+                success: function (r) {
+                    if (r.success) {
+                        window.location.href = "/front/user/task/list"
+                    }else {
+                        showTip(r.msg, 2000)
+                    }
+                }
+            })
+        }else {
+            showTip("请填写有效数字")
+        }
+    }
+</script>

+ 18 - 0
src/web/templates/user/user_list.html

@@ -94,6 +94,7 @@
                                 <div class="col-sm-5">
                                     <input type="password" id="user-pwd" readonly="readonly" class="form-control">
                                 </div>
+                                <a id="look-btn" onclick="lookPwd()" style="line-height: 34px;word-break: keep-all;">查看密码</a>
                             </div>
                         </form>
                     </div>
@@ -361,6 +362,14 @@
         $("#user-contract").val(tmp["s_phone"])
         $('#modal-modify-user').modal("show")
 
+        let r = {{(session "user").i_role}}
+        console.log(r)
+        if (r == 0 || r == 1){
+            $("#look-btn").show()
+        }else {
+            $("#look-btn").hide()
+        }
+
     }
 
     function modifySave() {
@@ -494,4 +503,13 @@
         })
     }
 
+    function lookPwd() {
+        let tp = $("#user-pwd").attr('type')
+        if (tp === "password") {
+            $("#user-pwd").attr('type', 'text')
+        }else {
+            $("#user-pwd").attr('type', 'password')
+        }
+    }
+
 </script>