Эх сурвалжийг харах

Merge branch 'develop' of 192.168.3.17:zhanghongbo/qfw into develop

zhangjinkun 9 жил өмнө
parent
commit
ed1eaac847
43 өөрчлөгдсөн 1055 нэмэгдсэн , 243 устгасан
  1. 4 0
      common/src/gopkg.in/mgo.v2/session.go
  2. 3 2
      common/src/main.go
  3. 18 8
      common/src/mainIndex.go
  4. 4 2
      common/src/qfw/util/common.go
  5. 13 6
      common/src/qfw/util/date.go
  6. 151 3
      common/src/qfw/util/elastic/elasticutil.go
  7. 8 0
      common/src/qfw/util/elastic/elasticutil_test.go
  8. 1 0
      common/src/qfw/util/encrypt_test.go
  9. 3 2
      common/src/qfw/util/redis/redisutil.go
  10. 1 1
      core/src/message.json
  11. 53 26
      core/src/qfw/manage/auditing.go
  12. 1 0
      core/src/qfw/mobile/mobile.go
  13. 35 1
      core/src/qfw/mobile/wxmenu.go
  14. 5 5
      core/src/qfw/search/searchService.go
  15. 6 5
      core/src/qfw/search/wxsearchservice.go
  16. 65 19
      core/src/qfw/yellowpage/yellowpagemanager.go
  17. 1 1
      core/src/timetask.json
  18. BIN
      core/src/web/staticres/images/swordfish/sf_01.jpg
  19. 17 3
      core/src/web/staticres/js/chart.js
  20. 7 0
      core/src/web/staticres/js/qfw.js
  21. 45 1
      core/src/web/staticres/wxswordfish/style.css
  22. 1 1
      core/src/web/templates/common/bottom.html
  23. 3 3
      core/src/web/templates/enterprise/detail.html
  24. 3 16
      core/src/web/templates/manage/tj.html
  25. 5 5
      core/src/web/templates/member/incmobile/baseinfo.html
  26. 1 1
      core/src/web/templates/member/incmobile/detailindex.html
  27. 4 4
      core/src/web/templates/member/incmobile/list.html
  28. 1 1
      core/src/web/templates/search/enterpriseList.html
  29. 39 0
      core/src/web/templates/swordfish/wxex.html
  30. 11 0
      core/src/web/templates/swordfish/wxpush.html
  31. 2 2
      core/src/web/templates/swordfish/wxrssset.html
  32. 1 2
      push/src/config.json
  33. 2 1
      push/src/qfw/push/dopush/dopush.go
  34. BIN
      weixin/doc/kfex.rar
  35. 10 8
      weixin/src/config.json
  36. 43 4
      weixin/src/main.go
  37. 1 1
      weixin/src/qfw/weixin/dao/userdao.go
  38. 1 2
      weixin/src/qfw/weixin/distinguishwork.go
  39. 8 1
      weixin/src/qfw/weixin/msgtxtchandler.go
  40. 272 0
      weixin/src/qfw/weixin/weixincustomer.go
  41. 178 106
      weixin/src/qfw/weixin/weixinsdk.go
  42. 26 0
      weixin/src/qfw/weixin/wxcustomer_test.go
  43. 2 0
      weixin/src/qfw/weixinconfig/weixinconfig.go

+ 4 - 0
common/src/gopkg.in/mgo.v2/session.go

@@ -1975,6 +1975,10 @@ func (c *Collection) Pipe(pipeline interface{}) *Pipe {
 
 
 // Iter executes the pipeline and returns an iterator capable of going
 // Iter executes the pipeline and returns an iterator capable of going
 // over all the generated results.
 // over all the generated results.
+func (p *Pipe) SetAllowDisk(b bool) *Pipe {
+	p.allowDisk = b
+	return p
+}
 func (p *Pipe) Iter() *Iter {
 func (p *Pipe) Iter() *Iter {
 	// Clone session and set it to Monotonic mode so that the server
 	// Clone session and set it to Monotonic mode so that the server
 	// used for the query may be safely obtained afterwards, if
 	// used for the query may be safely obtained afterwards, if

+ 3 - 2
common/src/main.go

@@ -2,7 +2,6 @@ package main
 
 
 import (
 import (
 	"fmt"
 	"fmt"
-	"gopkg.in/mgo.v2/bson"
 	"log"
 	"log"
 	"os"
 	"os"
 	util "qfw/util"
 	util "qfw/util"
@@ -10,12 +9,14 @@ import (
 	. "qfw/util/mongodbutil"
 	. "qfw/util/mongodbutil"
 	"strconv"
 	"strconv"
 	"strings"
 	"strings"
+
+	"gopkg.in/mgo.v2/bson"
 )
 )
 
 
 var N = 200
 var N = 200
 
 
 //生enterprise表
 //生enterprise表
-func main() {
+func mainS() {
 	//src.exe 192.168.3.18:27080 192.168.3.18:27090 http://192.168.3.18:9800 enterprise all enterprise enterprise 2000
 	//src.exe 192.168.3.18:27080 192.168.3.18:27090 http://192.168.3.18:9800 enterprise all enterprise enterprise 2000
 	// 参数 mongodb(网站库) mongodb(公示库) elastic collection area index-db inde-type N
 	// 参数 mongodb(网站库) mongodb(公示库) elastic collection area index-db inde-type N
 	log.Println("-------------开始-----------------")
 	log.Println("-------------开始-----------------")

+ 18 - 8
common/src/mainIndex.go

@@ -8,30 +8,38 @@ import (
 	"strconv"
 	"strconv"
 )
 )
 
 
-var NN = 200
+var NN = 1000
+var T = "qfw"
 
 
-//生content。service
-func mainS() {
+//生content。service
+func main() {
 	//6 参数 mongodb elastic collection index-db inde-type N
 	//6 参数 mongodb elastic collection index-db inde-type N
+	if len(os.Args) < 7 {
+		log.Println("程序 mgoIp elasticIp mgoCollection elasticIndex elasticType saveSize table")
+		return
+	}
+	if len(os.Args) > 7 {
+		T = os.Args[7]
+	}
 	n1, _ := strconv.Atoi(os.Args[6])
 	n1, _ := strconv.Atoi(os.Args[6])
 	if n1 > 0 {
 	if n1 > 0 {
 		NN = n1
 		NN = n1
 	}
 	}
-	mongodb.InitMongodbPool(1, os.Args[1], "qfw")
-	elastic.InitElastic(os.Args[2])
+	mongodb.InitMongodbPool(1, os.Args[1], T)
+	elastic.InitElasticSize((os.Args[2]), 5)
 	session := mongodb.GetMgoConn()
 	session := mongodb.GetMgoConn()
 	var m map[string]interface{} = nil
 	var m map[string]interface{} = nil
-	query := session.DB("qfw").C(os.Args[3]).Find(m).Iter()
+	query := session.DB(T).C(os.Args[3]).Find(m).Iter()
 	arr := make([]map[string]interface{}, NN)
 	arr := make([]map[string]interface{}, NN)
 	var n int
 	var n int
 	i := 0
 	i := 0
 	for tmp := make(map[string]interface{}); query.Next(tmp); i = i + 1 {
 	for tmp := make(map[string]interface{}); query.Next(tmp); i = i + 1 {
 		n++
 		n++
-		log.Printf("current index:=%d \n", n)
 		arr[i] = tmp
 		arr[i] = tmp
 		tmp = make(map[string]interface{})
 		tmp = make(map[string]interface{})
 		if i == NN-1 {
 		if i == NN-1 {
-			elastic.BulkSave(os.Args[4], os.Args[5], &arr, true)
+			tmp := arr
+			elastic.BulkSave(os.Args[4], os.Args[5], &tmp, true)
 			i = 0
 			i = 0
 			arr = make([]map[string]interface{}, NN)
 			arr = make([]map[string]interface{}, NN)
 		}
 		}
@@ -42,4 +50,6 @@ func mainS() {
 	if i > 0 {
 	if i > 0 {
 		elastic.BulkSave(os.Args[4], os.Args[5], &arr, true)
 		elastic.BulkSave(os.Args[4], os.Args[5], &arr, true)
 	}
 	}
+	mongodb.DestoryMongoConn(session)
+	log.Println("over....", n)
 }
 }

+ 4 - 2
common/src/qfw/util/common.go

@@ -193,8 +193,9 @@ func Int64All(num interface{}) int64 {
 	} else if i3, ok3 := num.(float32); ok3 {
 	} else if i3, ok3 := num.(float32); ok3 {
 		return int64(i3)
 		return int64(i3)
 	} else if i4, ok4 := num.(string); ok4 {
 	} else if i4, ok4 := num.(string); ok4 {
-		in, _ := strconv.Atoi(i4)
-		return int64(in)
+		i64, _ := strconv.ParseInt(i4, 10, 64)
+		//in, _ := strconv.Atoi(i4)
+		return i64
 	} else if i5, ok5 := num.(int16); ok5 {
 	} else if i5, ok5 := num.(int16); ok5 {
 		return int64(i5)
 		return int64(i5)
 	} else if i6, ok6 := num.(int8); ok6 {
 	} else if i6, ok6 := num.(int8); ok6 {
@@ -445,6 +446,7 @@ func EndWith(value, str string) bool {
 //出错拦截
 //出错拦截
 func Catch() {
 func Catch() {
 	if r := recover(); r != nil {
 	if r := recover(); r != nil {
+		log.Println(r)
 		for skip := 0; ; skip++ {
 		for skip := 0; ; skip++ {
 			_, file, line, ok := runtime.Caller(skip)
 			_, file, line, ok := runtime.Caller(skip)
 			if !ok {
 			if !ok {

+ 13 - 6
common/src/qfw/util/date.go

@@ -2,6 +2,7 @@
 package util
 package util
 
 
 import (
 import (
+	"fmt"
 	"time"
 	"time"
 )
 )
 
 
@@ -20,14 +21,20 @@ func FormatDate(src *time.Time, layout string) string {
 //兼容从Java转换过来的数据,java生成的时间戳是按微秒算的,Go中只能按毫秒或纳秒算
 //兼容从Java转换过来的数据,java生成的时间戳是按微秒算的,Go中只能按毫秒或纳秒算
 func formatDateWithInt64(src *int64, layout string) string {
 func formatDateWithInt64(src *int64, layout string) string {
 	var tmp int64
 	var tmp int64
-	if (*src) > 6000000000 {
-		tmp = (*src) / 1000
-	} else if (*src) < 1 {
-		return ""
+	if *src > 0 {
+		if len(fmt.Sprint(*src)) > 10 {
+			tmp = (*src) / 1000
+		} else {
+			tmp = (*src)
+		}
+
 	} else {
 	} else {
-		tmp = (*src)
+		if len(fmt.Sprint(*src)) > 11 {
+			tmp = (*src) / 1000
+		} else {
+			tmp = (*src)
+		}
 	}
 	}
-
 	date := time.Unix(tmp, 0)
 	date := time.Unix(tmp, 0)
 	return FormatDate(&date, layout)
 	return FormatDate(&date, layout)
 }
 }

+ 151 - 3
common/src/qfw/util/elastic/elasticutil.go

@@ -8,13 +8,14 @@ import (
 	//	"bytes"
 	//	"bytes"
 	"encoding/json"
 	"encoding/json"
 	"fmt"
 	"fmt"
-	es "gopkg.in/olivere/elastic.v1"
 	"log"
 	"log"
 	"qfw/util"
 	"qfw/util"
 	"qfw/util/mongodb"
 	"qfw/util/mongodb"
 	mongodbutil "qfw/util/mongodbutil"
 	mongodbutil "qfw/util/mongodbutil"
 	"reflect"
 	"reflect"
 
 
+	es "gopkg.in/olivere/elastic.v1"
+
 	"strconv"
 	"strconv"
 	"strings"
 	"strings"
 )
 )
@@ -189,6 +190,7 @@ func Get(index, itype, query string) *[]map[string]interface{} {
 
 
 //分页查询
 //分页查询
 //{"name":"张三","$and":[{"age":{"$gt":10}},{"age":{"$lte":20}}]}
 //{"name":"张三","$and":[{"age":{"$gt":10}},{"age":{"$lte":20}}]}
+//fields直接是 `"_id","title"`
 func GetPage(index, itype, query, order, field string, start, limit int) *[]map[string]interface{} {
 func GetPage(index, itype, query, order, field string, start, limit int) *[]map[string]interface{} {
 	return Get(index, itype, MakeQuery(query, order, field, start, limit))
 	return Get(index, itype, MakeQuery(query, order, field, start, limit))
 }
 }
@@ -739,10 +741,156 @@ func Count(index, itype string, query interface{}) int64 {
 		if err != nil {
 		if err != nil {
 			log.Println("统计出错", err.Error())
 			log.Println("统计出错", err.Error())
 		}
 		}
-		if n > 5000 {
-			n = 5000
+		if n > 500 {
+			n = 500
 		}
 		}
 		return n
 		return n
 	}
 	}
 	return 0
 	return 0
 }
 }
+
+//ngram精确查询
+/*
+{
+  "query": {
+    "bool": {
+      "should": [
+        {
+	"bool":{
+	  "must":[
+	  {     "multi_match": {
+            "query": "智能",
+            "type": "phrase",
+            "fields": [
+              "title"
+            ],
+           "analyzer": "my_ngram"
+          }
+        },{
+          "multi_match": {
+            "query": "机器",
+            "type": "phrase",
+            "fields": [
+              "title"
+            ],
+           "analyzer": "my_ngram"
+          }
+        },{
+          "multi_match": {
+            "query": "2016",
+            "type": "phrase",
+            "fields": [
+              "title"
+            ],
+           "analyzer": "my_ngram"
+          }
+	  }
+	  ]
+	}
+        },
+
+{
+	"bool":{
+	  "must":[
+	  {          "multi_match": {
+            "query": "河南",
+            "type": "phrase",
+            "fields": [
+              "title"
+            ],
+           "analyzer": "my_ngram"
+          }
+        },{
+          "multi_match": {
+            "query": "工商",
+            "type": "phrase",
+            "fields": [
+              "title"
+            ],
+           "analyzer": "my_ngram"
+          }
+        },{
+          "multi_match": {
+            "query": "2016",
+            "type": "phrase",
+            "fields": [
+              "title"
+            ],
+           "analyzer": "my_ngram"
+          }
+	  }
+	  ]
+	}
+        }
+      ],"minimum_should_match": 1
+    }
+  },
+  "_source": [
+    "_id",
+    "title"
+  ],
+  "from": 0,
+  "size": 10,
+  "sort": [{
+      "publishtime": "desc"
+    }]
+}
+
+*/
+//"2016+智能+办公,"河南+工商"
+//["2016+智能+办公","河南+工商"]
+//QStr = `{"query":{"bool":{should":[$or],"minimum_should_match" : 1}}}`
+//{"bool":{"must":[]}}
+//{"multi_match": {"query": "$word","type": "phrase", "fields": [$field],"analyzer": "my_ngram"}}
+const (
+	NgramStr  = `{"query":{"bool":{"must":[%s],"should":[%s],"minimum_should_match" : 1}}}`
+	NgramMust = `{"bool":{"must":[%s]}}`
+	minq      = `{"multi_match": {"query": "%s","type": "phrase", "fields": [%s],"analyzer": "my_ngram"}}`
+)
+
+func GetNgramQuery(query interface{}, mustquery, findfields string) (qstr string) {
+	var words []string
+	if q, ok := query.(string); ok {
+		words = strings.Split(q, ",")
+	} else if q, ok := query.([]string); ok {
+		words = q
+	} else if q, ok := query.([]interface{}); ok {
+		words = util.ObjArrToStringArr(q)
+	}
+	if words != nil {
+		new_minq := fmt.Sprintf(minq, "%s", findfields)
+		musts := []string{}
+		for _, qs_words := range words {
+			qws := strings.Split(qs_words, "+")
+			mq := []string{}
+			for _, qs_word := range qws {
+				mq = append(mq, fmt.Sprintf(new_minq, qs_word))
+			}
+			musts = append(musts, fmt.Sprintf(NgramMust, strings.Join(mq, ",")))
+		}
+		qstr = fmt.Sprintf(NgramStr, mustquery, strings.Join(musts, ","))
+		log.Println("ngram-query", qstr)
+	}
+	return
+}
+
+func GetByNgram(index, itype string, query interface{}, mustquery, findfields, order, fields string, start, limit int) *[]map[string]interface{} {
+	defer util.Catch()
+	qstr := GetNgramQuery(query, mustquery, findfields)
+	if qstr != "" {
+		if len(fields) > 0 {
+			qstr = qstr[:len(qstr)-1] + `,"_source":[` + fields + "]}"
+		}
+		if len(order) > 0 {
+			qstr = qstr[:len(qstr)-1] + `,"sort":[` + SR(SR(SR(SR(order, ",", "},{", -1), " ", "", -1), ":-1", `:"desc"`, -1), ":1", `:"asc"`, -1) + `]}`
+		}
+		if start > -1 {
+			qstr = qstr[:len(qstr)-1] + `,"from":` + strconv.Itoa(start) + `,"size":` + strconv.Itoa(limit) + "}"
+		}
+		log.Println("ngram-find", qstr)
+		return Get(index, itype, qstr)
+	} else {
+		return nil
+	}
+
+}

+ 8 - 0
common/src/qfw/util/elastic/elasticutil_test.go

@@ -206,3 +206,11 @@ func Test_q(t *testing.T) {
 	//log.Println(MakeQuery(`{"_id":"556349a6a442ab325177ade0"}`, "", `"_id"`, -1, -1))
 	//log.Println(MakeQuery(`{"_id":"556349a6a442ab325177ade0"}`, "", `"_id"`, -1, -1))
 	//Count("enterprise","enterprise",)
 	//Count("enterprise","enterprise",)
 }
 }
+
+func Test_bidding(t *testing.T) {
+	InitElasticSize("http://192.168.3.18:9800", 1)
+	//log.Println(Count("bidding_v1", "bidding", GetNgramQuery("河南+工商", `"title"`)))
+	//log.Println(GetByNgram("bidding_v1", "bidding", "河南省", `"title"`, `{"publishtime":-1}`, `"title"`, 0, 10))
+	res := GetPage("bidding", "bidding", "", `{"comeintime":-1}`, `"_id"`, 0, 1)
+	log.Println((*res)[0]["_id"])
+}

+ 1 - 0
common/src/qfw/util/encrypt_test.go

@@ -29,6 +29,7 @@ func Test_sim(t *testing.T) {
 	log.Println("=====", se.EncodeString(",1349385977,ASD"))
 	log.Println("=====", se.EncodeString(",1349385977,ASD"))
 	//log.Println("---", se.DecodeString("GyUlIhEDSGheAzEpJl8kHB9oVEwmLRkDPTN/CB1AHQtcX1RFHkNGWgYLFgcWHFNTRVwbAQ=="))
 	//log.Println("---", se.DecodeString("GyUlIhEDSGheAzEpJl8kHB9oVEwmLRkDPTN/CB1AHQtcX1RFHkNGWgYLFgcWHFNTRVwbAQ=="))
 	log.Println("---", se.DecodeString("GycHKzoDMykjDSEOMz0gLzI6FTtANCBNAiseVkkBHQtcX1FCRlpCV1JDR0MHFhUBBwccBxYA"))
 	log.Println("---", se.DecodeString("GycHKzoDMykjDSEOMz0gLzI6FTtANCBNAiseVkkBHQtcX1FCRlpCV1JDR0MHFhUBBwccBxYA"))
+	log.Println("==---==", se.DecodeString("Gw01HikBOSI0OzwhOUIKAh83Fj5AIwcBJSAKDUlYRVtGWVRCQFhGVkkHAwACCgMdBwcRDREdGwE="))
 
 
 	now := time.Now()
 	now := time.Now()
 	tom := time.Date(now.Year(), now.Month(), now.Day(), 18, 0, 0, 0, time.Local)
 	tom := time.Date(now.Year(), now.Month(), now.Day(), 18, 0, 0, 0, time.Local)

+ 3 - 2
common/src/qfw/util/redis/redisutil.go

@@ -4,19 +4,20 @@ import (
 	"encoding/json"
 	"encoding/json"
 	"errors"
 	"errors"
 	"fmt"
 	"fmt"
-	redigo "github.com/garyburd/redigo/redis"
 	"log"
 	"log"
 	"qfw/util/mongodb"
 	"qfw/util/mongodb"
 	"runtime"
 	"runtime"
 	"strings"
 	"strings"
 	"time"
 	"time"
+
+	redigo "github.com/garyburd/redigo/redis"
 )
 )
 
 
 var RedisPool map[string]*redigo.Pool
 var RedisPool map[string]*redigo.Pool
 
 
 //初始化redis 1为多个连接池,2为共用一个连接池
 //初始化redis 1为多个连接池,2为共用一个连接池
 func InitRedis(addrs string) {
 func InitRedis(addrs string) {
-	InitRedisBySize(addrs, 1000, 500, 240)
+	InitRedisBySize(addrs, 300, 30, 240)
 }
 }
 
 
 func InitRedisBySize(addrs string, maxSize, maxIdle, timeout int) {
 func InitRedisBySize(addrs string, maxSize, maxIdle, timeout int) {

+ 1 - 1
core/src/message.json

@@ -1,6 +1,6 @@
 {
 {
 	"weixinrpc":"127.0.0.1:82",
 	"weixinrpc":"127.0.0.1:82",
-	"swordfishaction":"/swordfish/page",
+	"swordfishaction":"/swordfish/excessive",
 	"signature":"/member/credit/myCredit",
 	"signature":"/member/credit/myCredit",
 	"entsearchaction":"/wx/search/enterprise/index.html",
 	"entsearchaction":"/wx/search/enterprise/index.html",
 	"lawsearchaction":"/law/qfw/index",
 	"lawsearchaction":"/law/qfw/index",

+ 53 - 26
core/src/qfw/manage/auditing.go

@@ -86,7 +86,6 @@ func (s *SystemManage) Countlist() {
 	compareafter := s.GetString("compareafter")
 	compareafter := s.GetString("compareafter")
 	comparetype := s.GetString("comparetype")
 	comparetype := s.GetString("comparetype")
 	checked := s.GetString("checked")
 	checked := s.GetString("checked")
-
 	if comparetype != "nfish" {
 	if comparetype != "nfish" {
 		//第一条图标数据
 		//第一条图标数据
 		t1, _ := time.ParseInLocation("2006-01-02 15:04:05", timebefore+" 00:00:00", time.Local)
 		t1, _ := time.ParseInLocation("2006-01-02 15:04:05", timebefore+" 00:00:00", time.Local)
@@ -108,54 +107,82 @@ func (s *SystemManage) Countlist() {
 		t1, _ := time.ParseInLocation("2006-01-02 15:04:05", timebefore+" 00:00:00", time.Local)
 		t1, _ := time.ParseInLocation("2006-01-02 15:04:05", timebefore+" 00:00:00", time.Local)
 		t2, _ := time.ParseInLocation("2006-01-02 15:04:05", timeafter+" 00:00:00", time.Local)
 		t2, _ := time.ParseInLocation("2006-01-02 15:04:05", timeafter+" 00:00:00", time.Local)
 		condition := M{"l_date": M{"$gte": t1.Unix(), "$lte": t2.Unix()}}
 		condition := M{"l_date": M{"$gte": t1.Unix(), "$lte": t2.Unix()}}
-		timers := *Find("web_viewcount", condition, `{"l_date": 1}`, `{"l_date":1,"i_viewcount":1}`, false, -1, -1)
+		timers := *Find("web_viewcount", condition, `{"l_date": 1}`, nil, false, -1, -1)
 		ns := make([]map[string]interface{}, 0)
 		ns := make([]map[string]interface{}, 0)
+		ns2 := make([]map[string]interface{}, 0)
 		for _, v := range timers {
 		for _, v := range timers {
 			var tmp = make(map[string]interface{})
 			var tmp = make(map[string]interface{})
 			tmp["_id"] = v["_id"]
 			tmp["_id"] = v["_id"]
 			tmp["intime"] = v["l_date"]
 			tmp["intime"] = v["l_date"]
 			tmp["nfish"] = v["i_viewcount"]
 			tmp["nfish"] = v["i_viewcount"]
+			if tmp["nfish"] == nil {
+				tmp["nfish"] = "0"
+			}
 			ns = append(ns, tmp)
 			ns = append(ns, tmp)
 		}
 		}
-		log.Println(ns)
-		s.ServeJson(M{"t": ns, "c": "[]"})
+		for _, v := range timers {
+			var tmp = make(map[string]interface{})
+			tmp["_id"] = v["_id"]
+			tmp["intime"] = v["l_date"]
+			tmp["nfish"] = v["i_nviewcount"]
+			if tmp["nfish"] == nil {
+				tmp["nfish"] = "0"
+			}
+			ns2 = append(ns2, tmp)
+		}
+		s.ServeJson(M{"t": ns, "c": ns2})
 	}
 	}
 }
 }
 
 
 func (s *SystemManage) Count() {
 func (s *SystemManage) Count() {
 	rs := *Find("tj", nil, `{"intime": 0}`, nil, false, 0, 1)
 	rs := *Find("tj", nil, `{"intime": 0}`, nil, false, 0, 1)
 	//获取关键词
 	//获取关键词
-	msg := *Find("user", `{"o_msgset":{"$exists":true}}`, nil, `{"o_msgset":1}`, false, -1, -1)
+	msg := *Find("user", `{"$or":[{"o_msgset":{"$exists":true}},{"o_jy":{"$exists":true}}]}`, nil, `{"o_msgset":1,"o_jy":1}`, false, -1, -1)
 	msgcount := 0
 	msgcount := 0
-	tenderarr := make(map[string]int)
-	bidarr := make(map[string]int)
+	keys := map[string]int{}
 	for _, v := range msg {
 	for _, v := range msg {
-		obj := v["o_msgset"].(map[string]interface{})
-		if obj["tender"] != nil {
-			tender := obj["tender"].(map[string]interface{})
-			if tender["a_key"] != nil {
-				arr := tender["a_key"].([]interface{})
-				for _, v := range arr {
-					vs := v.(string)
-					tenderarr[vs] = tenderarr[vs] + 1
+		if v["o_msgset"] != nil {
+			obj := v["o_msgset"].(map[string]interface{})
+			if obj["tender"] != nil {
+				tender := obj["tender"].(map[string]interface{})
+				if tender["a_key"] != nil {
+					arr := tender["a_key"].([]interface{})
+					for _, v := range arr {
+						vs := v.(string)
+						keys[vs] = keys[vs] + 1
+					}
 				}
 				}
 			}
 			}
-		}
-		if obj["bid"] != nil {
-			bid := obj["bid"].(map[string]interface{})
-			if bid["a_key"] != nil {
-				arr := bid["a_key"].([]interface{})
-				for _, v := range arr {
-					vs := v.(string)
-					bidarr[vs] = bidarr[vs] + 1
+
+			if obj["bid"] != nil {
+				bid := obj["bid"].(map[string]interface{})
+				if bid["a_key"] != nil {
+					arr := bid["a_key"].([]interface{})
+					for _, v := range arr {
+						vs := v.(string)
+						keys[vs] = keys[vs] + 1
+					}
 				}
 				}
 			}
 			}
 		}
 		}
+		if v["o_jy"] != nil {
+			obj := v["o_jy"].(map[string]interface{})
+			jykeys := []interface{}{}
+			if obj["a_key"] != nil {
+				jykeys = obj["a_key"].([]interface{})
+			}
+
+			for _, v := range jykeys {
+				vs := v.(string)
+				keys[vs] = keys[vs] + 1
+			}
+		}
 	}
 	}
-	msgcount = len(tenderarr) + len(bidarr)
+	msgcount = len(keys)
 	servicemap := make(map[string]interface{})
 	servicemap := make(map[string]interface{})
 	if len(rs) != 0 {
 	if len(rs) != 0 {
 		rsOne := rs[0]
 		rsOne := rs[0]
+
 		//获取服务种类总表
 		//获取服务种类总表
 		services := rsOne["services"]
 		services := rsOne["services"]
 		if services != nil {
 		if services != nil {
@@ -179,10 +206,10 @@ func (s *SystemManage) Count() {
 			}
 			}
 
 
 		}
 		}
+
 		s.T["rs"] = rsOne
 		s.T["rs"] = rsOne
 		s.T["msgcount"] = msgcount
 		s.T["msgcount"] = msgcount
-		s.T["tender"] = tenderarr
-		s.T["bid"] = bidarr
+		s.T["keys"] = keys
 		s.T["servicename"] = servicemap
 		s.T["servicename"] = servicemap
 	}
 	}
 	s.Render("/manage/tj.html")
 	s.Render("/manage/tj.html")

+ 1 - 0
core/src/qfw/mobile/mobile.go

@@ -24,6 +24,7 @@ type Mobile struct {
 	wxpushAjaxReq  xweb.Mapper `xweb:"/wxpush/bid/ajaxReq"`
 	wxpushAjaxReq  xweb.Mapper `xweb:"/wxpush/bid/ajaxReq"`
 	wxpushView     xweb.Mapper `xweb:"/wxpush/bid/wxpushview/(.*)"` //推送结果预览
 	wxpushView     xweb.Mapper `xweb:"/wxpush/bid/wxpushview/(.*)"` //推送结果预览
 	mobileindex    xweb.Mapper `xweb:"/mobile/mobileindex"`
 	mobileindex    xweb.Mapper `xweb:"/mobile/mobileindex"`
+	wxex           xweb.Mapper `xweb:"/swordfish/excessive"`
 }
 }
 
 
 func init() {
 func init() {

+ 35 - 1
core/src/qfw/mobile/wxmenu.go

@@ -4,6 +4,7 @@ import (
 	"encoding/json"
 	"encoding/json"
 	"fmt"
 	"fmt"
 	"github.com/go-xweb/xweb"
 	"github.com/go-xweb/xweb"
+	. "gopkg.in/mgo.v2/bson"
 	"log"
 	"log"
 	"math/rand"
 	"math/rand"
 	"net/rpc"
 	"net/rpc"
@@ -47,6 +48,40 @@ func (m *Mobile) Share(shareid string) error {
 	return m.Render("/swordfish/wxshare.html", &m.T)
 	return m.Render("/swordfish/wxshare.html", &m.T)
 }
 }
 
 
+//过度页面
+func (m *Mobile) Wxex() error {
+	var newsubscribe = "N"
+	if m.Session().Get("userId") != nil {
+		userInfo := mongodb.FindById("user", m.GetSession("userId").(string), nil)
+		if *userInfo != nil {
+			if (*userInfo)["s_unionid"] != nil || (*userInfo)["s_unionid"] != "" {
+				var unid = (*userInfo)["s_unionid"]
+				query := make(M)
+				query["s_unionid"] = unid
+				userdata := mongodb.Find("user", query, `{"l_date":-1}`, `{"s_m_openid":1}`, false, 0, 2)
+				if len(*userdata) > 0 {
+					for _, opendata := range *userdata {
+						if opendata["s_m_openid"] != (*userInfo)["s_m_openid"] {
+							var opid = opendata["s_m_openid"]
+							queryop := make(M)
+							queryop["s_m_openid"] = opid
+							jysb := mongodb.Find("jy_subscribe", queryop, `{"l_date":-1}`, `{"s_event":1}`, false, 0, 1)
+							if (*jysb)[0]["s_event"] == "subscribe" {
+								newsubscribe = "Y"
+							}
+						}
+					}
+
+				}
+			}
+		}
+		m.T["newsubscribe"] = newsubscribe
+		return m.Render("/swordfish/wxex.html", &m.T)
+	} else {
+		return m.Redirect("/swordfish/share/-1")
+	}
+}
+
 //进入订阅页面
 //进入订阅页面
 func (m *Mobile) Wxrssset() error {
 func (m *Mobile) Wxrssset() error {
 	searchname := m.GetString("searchnametwo")
 	searchname := m.GetString("searchnametwo")
@@ -110,7 +145,6 @@ func (m *Mobile) Wxrssset() error {
 			//取Shareid
 			//取Shareid
 			m.T["shareid"] = cutil.FindMyShareId("topjy", m.Session().Get("s_m_openid").(string))
 			m.T["shareid"] = cutil.FindMyShareId("topjy", m.Session().Get("s_m_openid").(string))
 		}
 		}
-
 		return m.Render("/swordfish/wxrssset.html", &m.T)
 		return m.Render("/swordfish/wxrssset.html", &m.T)
 	} else {
 	} else {
 		return m.Redirect("/swordfish/share/-1")
 		return m.Redirect("/swordfish/share/-1")

+ 5 - 5
core/src/qfw/search/searchService.go

@@ -348,7 +348,7 @@ func searhWebContentent(querymap map[string]string, n *Search, reqType string) (
 							  "must":[{"prefix":{"OpLocDistrict":"` + ipcity + `"}}]
 							  "must":[{"prefix":{"OpLocDistrict":"` + ipcity + `"}}]
 						    }
 						    }
 						}
 						}
-						,"_source":["_id","EntName","RegOrgName","RegNo","EntType","LeRep","EstDate","OpLocDistrictName","RegCap","OpStateName","OpState","s_servicenames","s_action","OpLocDistrict","s_submitid","RegCapCurName","s_avatar","excDirect"]
+						,"_source":["_id","EntName","RegOrgName","RegNo","EntType","LeRep","EstDate","OpLocDistrictName","RegCap","OpStateName","OpState","s_servicenames","s_action","OpLocDistrict","s_submitid","RegCapCurName","s_avatar","Ycml"]
 						,"from":0,
 						,"from":0,
 						"size":` + fmt.Sprintf("%v", perPage) + `,
 						"size":` + fmt.Sprintf("%v", perPage) + `,
 						  "sort": [{"_score": "desc"},{"OpSint":"desc"},{"RegCap":"desc"}]
 						  "sort": [{"_score": "desc"},{"OpSint":"desc"},{"RegCap":"desc"}]
@@ -512,7 +512,7 @@ func searhWebContentent(querymap map[string]string, n *Search, reqType string) (
 			"s_synopsis":{"force_source": true},
 			"s_synopsis":{"force_source": true},
 			"stock":{"force_source": true}
 			"stock":{"force_source": true}
         }
         }
-    },"_source":["_id","EntName","RegOrgName","RegNo","EntType","LeRep","EstDate","OpLocDistrictName","RegCap","OpStateName","OpState","s_servicenames","s_action","OpLocDistrict","s_submitid","RegCapCurName","s_avatar","excDirect"]
+    },"_source":["_id","EntName","RegOrgName","RegNo","EntType","LeRep","EstDate","OpLocDistrictName","RegCap","OpStateName","OpState","s_servicenames","s_action","OpLocDistrict","s_submitid","RegCapCurName","s_avatar","Ycml"]
 	,"from":` + fmt.Sprintf("%v", ((currentPage-1)*perPage)) + `,
 	,"from":` + fmt.Sprintf("%v", ((currentPage-1)*perPage)) + `,
 	"size":` + fmt.Sprintf("%v", perPage) +
 	"size":` + fmt.Sprintf("%v", perPage) +
 			`,"sort":[` + sort + `] }`
 			`,"sort":[` + sort + `] }`
@@ -545,6 +545,7 @@ func searhWebContentent(querymap map[string]string, n *Search, reqType string) (
 
 
 			EntName, _ := res[i]["EntName"].(string)
 			EntName, _ := res[i]["EntName"].(string)
 			tmpNo := res[i]["EntType"]
 			tmpNo := res[i]["EntType"]
+			res[i]["EntTypeCopy"] = tmpNo
 			res[i]["RegCapEntType"] = tmpNo
 			res[i]["RegCapEntType"] = tmpNo
 			if tmpNo != nil {
 			if tmpNo != nil {
 				switch tmpNo.(string) {
 				switch tmpNo.(string) {
@@ -613,12 +614,11 @@ func searhWebContentent(querymap map[string]string, n *Search, reqType string) (
 			tmpdate, _ := res[i]["EstDate"]
 			tmpdate, _ := res[i]["EstDate"]
 
 
 			//res[i]["OperatorLabel"] = getDiffName(EntType)[0]
 			//res[i]["OperatorLabel"] = getDiffName(EntType)[0]
-			if tmpdate != nil {
+			if tmpdate != nil && IntAll(tmpdate) != 0 {
 				res[i]["EstDate"] = FormatDateWithObj(&tmpdate, Date_Short_Layout)
 				res[i]["EstDate"] = FormatDateWithObj(&tmpdate, Date_Short_Layout)
 			} else {
 			} else {
 				res[i]["EstDate"] = ""
 				res[i]["EstDate"] = ""
 			}
 			}
-
 		}
 		}
 	}
 	}
 	//log.Println(total)
 	//log.Println(total)
@@ -741,7 +741,7 @@ func searhWebContententMp(querymap map[string]string, n *Search, reqType string,
 				res[i]["stock"] = template.HTML(stock)
 				res[i]["stock"] = template.HTML(stock)
 			}
 			}
 			tmpdate, _ := res[i]["EstDate"]
 			tmpdate, _ := res[i]["EstDate"]
-			if tmpdate != nil {
+			if tmpdate != nil && IntAll(tmpdate) != 0 {
 				res[i]["EstDate"] = FormatDateWithObj(&tmpdate, Date_Short_Layout)
 				res[i]["EstDate"] = FormatDateWithObj(&tmpdate, Date_Short_Layout)
 			} else {
 			} else {
 				res[i]["EstDate"] = ""
 				res[i]["EstDate"] = ""

+ 6 - 5
core/src/qfw/search/wxsearchservice.go

@@ -89,7 +89,7 @@ func FormatData(data *[]map[string]interface{}, action *xweb.Action) string {
 			"EstDate":       estDate,
 			"EstDate":       estDate,
 			"OpStateName":   fmt.Sprint(v["OpStateName"]),
 			"OpStateName":   fmt.Sprint(v["OpStateName"]),
 			"status":        "n",
 			"status":        "n",
-			"excDirect":     fmt.Sprint(v["excDirect"]),
+			"Ycml":          fmt.Sprint(v["Ycml"]),
 		}
 		}
 		//是否关注
 		//是否关注
 		entId := tmp["_id"]
 		entId := tmp["_id"]
@@ -152,7 +152,7 @@ func wxsearhWebContentent(querymap map[string]string, n *Wxsearch, reqType strin
 						      }
 						      }
 						    }
 						    }
 						  }
 						  }
-						,"_source":["_id","EntName","RegOrgName","RegNo","EntType","LeRep","EstDate","OpLocDistrictName","RegCap","OpStateName","OpState","s_servicenames","s_action","OpLocDistrict","RegCapCurName","s_avatar","excDirect"]
+						,"_source":["_id","EntName","RegOrgName","RegNo","EntType","LeRep","EstDate","OpLocDistrictName","RegCap","OpStateName","OpState","s_servicenames","s_action","OpLocDistrict","RegCapCurName","s_avatar","Ycml"]
 						,"from": ` + fmt.Sprintf("%v", ((currentPage-1)*perPage)) + `,
 						,"from": ` + fmt.Sprintf("%v", ((currentPage-1)*perPage)) + `,
 						"size":` + fmt.Sprintf("%v", perPage) + `,
 						"size":` + fmt.Sprintf("%v", perPage) + `,
 						  "sort": [{"_score": "desc"},{"OpSint":"desc"},{"RegCap":"desc"}]
 						  "sort": [{"_score": "desc"},{"OpSint":"desc"},{"RegCap":"desc"}]
@@ -293,7 +293,7 @@ func wxsearhWebContentent(querymap map[string]string, n *Wxsearch, reqType strin
 			"s_synopsis":{"force_source": true},
 			"s_synopsis":{"force_source": true},
 			"stock":{"force_source": true}
 			"stock":{"force_source": true}
         }
         }
-    },"_source":["_id","EntName","RegOrgName","RegNo","EntType","LeRep","EstDate","OpLocDistrictName","RegCap","OpStateName","OpState","s_servicenames","s_action","OpLocDistrict","s_submitid","RegCapCurName","s_avatar","excDirect"]
+    },"_source":["_id","EntName","RegOrgName","RegNo","EntType","LeRep","EstDate","OpLocDistrictName","RegCap","OpStateName","OpState","s_servicenames","s_action","OpLocDistrict","s_submitid","RegCapCurName","s_avatar","Ycml"]
 	,"from":` + fmt.Sprintf("%v", ((currentPage-1)*perPage)) + `,
 	,"from":` + fmt.Sprintf("%v", ((currentPage-1)*perPage)) + `,
 	"size":` + fmt.Sprintf("%v", perPage) +
 	"size":` + fmt.Sprintf("%v", perPage) +
 			`,"sort":[` + sort + `] }`
 			`,"sort":[` + sort + `] }`
@@ -321,6 +321,7 @@ func wxsearhWebContentent(querymap map[string]string, n *Wxsearch, reqType strin
 
 
 			EntName, _ := res[i]["EntName"].(string)
 			EntName, _ := res[i]["EntName"].(string)
 			tmpNo := res[i]["EntType"]
 			tmpNo := res[i]["EntType"]
+			res[i]["EntTypeCopy"] = tmpNo
 			res[i]["RegCapEntType"] = tmpNo
 			res[i]["RegCapEntType"] = tmpNo
 			if tmpNo != nil {
 			if tmpNo != nil {
 				switch tmpNo.(string) {
 				switch tmpNo.(string) {
@@ -423,7 +424,7 @@ func wxsearhWebContententMp(querymap map[string]string, n *Wxsearch, reqType str
 	    "_source": [
 	    "_source": [
 	        "_id","EntName","RegOrgName","RegNo","EntType",
 	        "_id","EntName","RegOrgName","RegNo","EntType",
 	        "LeRep","EstDate","OpLocDistrictName","OpStateName",
 	        "LeRep","EstDate","OpLocDistrictName","OpStateName",
-	        "s_servicenames","s_action","OpLocDistrict","s_submitid","excDirect"
+	        "s_servicenames","s_action","OpLocDistrict","s_submitid","Ycml"
 	    ],
 	    ],
 	    "from": 0,"size": 10,
 	    "from": 0,"size": 10,
 	    "sort": [{"OpSint": "desc","RegCap": "desc"}]
 	    "sort": [{"OpSint": "desc","RegCap": "desc"}]
@@ -501,7 +502,7 @@ func wxsearhWebContententMp(querymap map[string]string, n *Wxsearch, reqType str
 				res[i]["stock"] = template.HTML(stock)
 				res[i]["stock"] = template.HTML(stock)
 			}
 			}
 			tmpdate, _ := res[i]["EstDate"]
 			tmpdate, _ := res[i]["EstDate"]
-			if tmpdate != nil {
+			if tmpdate != nil && IntAll(tmpdate) != 0 {
 				res[i]["EstDate"] = FormatDateWithObj(&tmpdate, Date_Short_Layout)
 				res[i]["EstDate"] = FormatDateWithObj(&tmpdate, Date_Short_Layout)
 			} else {
 			} else {
 				res[i]["EstDate"] = ""
 				res[i]["EstDate"] = ""

+ 65 - 19
core/src/qfw/yellowpage/yellowpagemanager.go

@@ -48,7 +48,7 @@ func FormatDate(src *interface{}) string {
 }
 }
 
 
 func GetEntInfo(id string) map[string]interface{} {
 func GetEntInfo(id string) map[string]interface{} {
-	res := mongodbutil.FindById("enterprise", cf.SysConfig.EntMongodbAlias, cf.SysConfig.EntMongodbName, id, `{"RegNo":1, "EntName":1, "EntType":1, "EntTypeName":1, "OpLocDistrict":1, "LeRep":1, "LegCerNO":1, "Tel":1, "Dom":1, "OpScope":1, "OpFrom":1, "OpTo":1, "RegCap":1, "EstDate":1, "CompForm":1, "CompFormName":1, "OpState":1, "OpStateName":1, "RegOrgName":1, "IssBLicDate":1, "Timestamp":1,"SourceType":1, "Nb_email":1,"IndustryPhyName":1,"investor":1,"alterInfo":1,"staffinfo":1,"RegCapCurName":1,"excDirect":1}`)
+	res := mongodbutil.FindById("enterprise", cf.SysConfig.EntMongodbAlias, cf.SysConfig.EntMongodbName, id, `{"RegNo":1, "EntName":1, "EntType":1, "EntTypeName":1, "OpLocDistrict":1, "LeRep":1, "LegCerNO":1, "Tel":1, "Dom":1, "OpScope":1, "OpFrom":1, "OpTo":1, "RegCap":1, "EstDate":1, "CompForm":1, "CompFormName":1, "OpState":1, "OpStateName":1, "RegOrgName":1, "IssBLicDate":1, "Timestamp":1,"SourceType":1, "Nb_email":1,"IndustryPhyName":1,"investor":1,"alterInfo":1,"staffinfo":1,"RegCapCurName":1,"Ycml":1,"i_scale":1}`)
 	if res != nil && len(*res) > 0 {
 	if res != nil && len(*res) > 0 {
 		opl, _ := (*res)["OpLocDistrict"].(string)
 		opl, _ := (*res)["OpLocDistrict"].(string)
 		if len(opl) >= 2 {
 		if len(opl) >= 2 {
@@ -67,17 +67,18 @@ func GetEntInfo(id string) map[string]interface{} {
 			(*res)["OpFrom"] = FormatDate(&OpFrom)
 			(*res)["OpFrom"] = FormatDate(&OpFrom)
 		}
 		}
 		OpTo := (*res)["OpTo"]
 		OpTo := (*res)["OpTo"]
-		if OpFrom != nil {
+		if OpTo != nil {
 			(*res)["OpTo"] = FormatDate(&OpTo)
 			(*res)["OpTo"] = FormatDate(&OpTo)
 		}
 		}
 		IssBLicDate := (*res)["IssBLicDate"]
 		IssBLicDate := (*res)["IssBLicDate"]
-		if OpFrom != nil {
+		if IssBLicDate != nil {
 			(*res)["IssBLicDate"] = FormatDate(&IssBLicDate)
 			(*res)["IssBLicDate"] = FormatDate(&IssBLicDate)
 		}
 		}
 		EstDate := (*res)["EstDate"]
 		EstDate := (*res)["EstDate"]
-		if OpFrom != nil {
+		if EstDate != nil {
 			(*res)["EstDate"] = FormatDate(&EstDate)
 			(*res)["EstDate"] = FormatDate(&EstDate)
 		}
 		}
+		(*res)["EntTypeCopy"] = (*res)["EntType"]
 		var EntType string
 		var EntType string
 		if (*res)["EntType"] != nil && (*res)["EntType"].(string) != "" {
 		if (*res)["EntType"] != nil && (*res)["EntType"].(string) != "" {
 			EntType = (*res)["EntType"].(string)
 			EntType = (*res)["EntType"].(string)
@@ -323,22 +324,67 @@ func findRelation(certype, cerno, name, rtype string, level int, ls *list.List,
 
 
 //存储节点,只关心3个元素,节点名称、节点id、关系节点id
 //存储节点,只关心3个元素,节点名称、节点id、关系节点id
 func storeNode(data *map[string]interface{}, ls *list.List, dir /*方向反转*/ bool, level int) {
 func storeNode(data *map[string]interface{}, ls *list.List, dir /*方向反转*/ bool, level int) {
-	invtype := fmt.Sprint((*data)["invtype"])
+	invtype := ""
+	if (*data)["invtype"] != nil {
+		invtype = fmt.Sprint((*data)["invtype"])
+	}
 	//invcertype, _ := (*data)["invcertype"].(string)
 	//invcertype, _ := (*data)["invcertype"].(string)
-	invcerno := fmt.Sprint((*data)["invcerno"])
-	invname := fmt.Sprint((*data)["invname"])
-	entregno := fmt.Sprint((*data)["entregno"])
-	entname := fmt.Sprint((*data)["entname"])
-	invweight := fmt.Sprint((*data)["weight"])
-	invacconam := fmt.Sprint((*data)["invacconam"])
-	regcap := fmt.Sprint((*data)["regcap"])
-	invregcap := fmt.Sprint((*data)["invregcap"])
-	legcerno := fmt.Sprint((*data)["legcerno"])
-	opstate := fmt.Sprint((*data)["opstate"])
-	invopstate := fmt.Sprint((*data)["invopstate"])
-	regcapcurname := fmt.Sprint((*data)["regcapcurname"])
-	shortname := fmt.Sprint((*data)["shortname"])
-	entmid := util.BsonIdToSId((*data)["entmid"])
+	invcerno := ""
+	if (*data)["invcerno"] != nil {
+		invcerno = fmt.Sprint((*data)["invcerno"])
+	}
+	invname := ""
+	if (*data)["invname"] != nil {
+		invname = fmt.Sprint((*data)["invname"])
+	}
+	entregno := ""
+	if (*data)["entregno"] != nil {
+		entregno = fmt.Sprint((*data)["entregno"])
+	}
+	entname := ""
+	if (*data)["entname"] != nil {
+		entname = fmt.Sprint((*data)["entname"])
+	}
+	invweight := "0"
+	if (*data)["weight"] != nil {
+		invweight = fmt.Sprint((*data)["weight"])
+	}
+	invacconam := "0"
+	if (*data)["invacconam"] != nil {
+		invacconam = fmt.Sprint((*data)["invacconam"])
+	}
+	regcap := "0"
+	if (*data)["regcap"] != nil {
+		regcap = fmt.Sprint((*data)["regcap"])
+	}
+	invregcap := "0"
+	if (*data)["invregcap"] != nil {
+		invregcap = fmt.Sprint((*data)["invregcap"])
+	}
+	legcerno := ""
+	if (*data)["legcerno"] != nil {
+		legcerno = fmt.Sprint((*data)["legcerno"])
+	}
+	opstate := ""
+	if (*data)["opstate"] != nil {
+		opstate = fmt.Sprint((*data)["opstate"])
+	}
+	invopstate := ""
+	if (*data)["invopstate"] != nil {
+		invopstate = fmt.Sprint((*data)["invopstate"])
+	}
+	regcapcurname := ""
+	if (*data)["regcapcurname"] != nil {
+		regcapcurname = fmt.Sprint((*data)["regcapcurname"])
+	}
+	shortname := ""
+	if (*data)["shortname"] != nil {
+		shortname = fmt.Sprint((*data)["shortname"])
+	}
+	entmid := ""
+	if (*data)["entmid"] != nil {
+		entmid = util.BsonIdToSId((*data)["entmid"])
+	}
 	ls.PushBack([]string{invtype, invcerno, invname, entregno, entname, strconv.Itoa(level), invweight, invacconam, regcap, invregcap, legcerno, opstate, invopstate, regcapcurname, util.BsonIdToSId((*data)["_id"]), shortname, entmid})
 	ls.PushBack([]string{invtype, invcerno, invname, entregno, entname, strconv.Itoa(level), invweight, invacconam, regcap, invregcap, legcerno, opstate, invopstate, regcapcurname, util.BsonIdToSId((*data)["_id"]), shortname, entmid})
 }
 }
 
 

+ 1 - 1
core/src/timetask.json

@@ -1 +1 @@
-{"comment":{"c_rate":720,"commentrate":900},"market":{"demand":{"attr":["i_hits","i_bids","i_status"],"timepoint":"2016-06-13 10:20:06"},"service":{"attr":["i_hits","i_sales","i_comments","i_score","i_appcounts"],"timepoint":"2016-06-13 10:20:06"}},"marketisstart":true,"marketrate":300}
+{"comment":{"c_rate":720,"commentrate":900},"market":{"demand":{"attr":["i_hits","i_bids","i_status"],"timepoint":"2016-07-01 08:21:25"},"service":{"attr":["i_hits","i_sales","i_comments","i_score","i_appcounts"],"timepoint":"2016-07-01 08:21:25"}},"marketisstart":true,"marketrate":300}

BIN
core/src/web/staticres/images/swordfish/sf_01.jpg


+ 17 - 3
core/src/web/staticres/js/chart.js

@@ -36,17 +36,26 @@ function setOptions(rs){
 				"type":"line",
 				"type":"line",
 				"data":rs.yData[0]			//此处为纵坐标值	
 				"data":rs.yData[0]			//此处为纵坐标值	
 				};
 				};
+		var name=$(me).text()+"(对比)";
+		if($(me).text()== "剑鱼用户(活跃)"){
+			name="(新)剑鱼用户(活跃)";
+		}
 		var s2={
 		var s2={
-				"name":$(me).text()+"(对比)",
+				"name":name,
 				"type":"line",
 				"type":"line",
 				"data":rs.yData[1]			//此处为纵坐标值	
 				"data":rs.yData[1]			//此处为纵坐标值	
 			};
 			};
 		xAxis.push(x1);
 		xAxis.push(x1);
 		series.push(s1);
 		series.push(s1);
-		if(check.prop("checked")&& rs.xData1.length>0) {
+		if(check.prop("checked")&& rs.xData1.length>0 || $(me).text()== "剑鱼用户(活跃)") {
 			xAxis.push(x2);
 			xAxis.push(x2);
 			series.push(s2);
 			series.push(s2);
-			legend.push($(me).text()+"(对比)");
+			if ($(me).text()== "剑鱼用户(活跃)"){
+				legend.push("(新)剑鱼用户(活跃)");
+			}else{
+				legend.push($(me).text()+"(对比)");
+			}
+			
 		}
 		}
 		 option = {
 		 option = {
 			tooltip : {
 			tooltip : {
@@ -74,6 +83,11 @@ function setOptions(rs){
 }
 }
 function loadData(me){
 function loadData(me){
 	if(me){
 	if(me){
+		if($(me).text()=="剑鱼用户(活跃)"){
+			$("#comparecheck").parent().hide();
+		}else{
+			$("#comparecheck").parent().show();
+		}
 		$("#comparetype").val($(me).attr("id"));
 		$("#comparetype").val($(me).attr("id"));
 	}
 	}
 	var data={};
 	var data={};

+ 7 - 0
core/src/web/staticres/js/qfw.js

@@ -751,6 +751,13 @@ function processAlterInfo(alterInfo){
 			altDate = $.trim(altDate);
 			altDate = $.trim(altDate);
 			if(altDate == "" || altDate == "null" || altDate == "空"){
 			if(altDate == "" || altDate == "null" || altDate == "空"){
 				altDate = null;
 				altDate = null;
+			}else{
+				var altDateTmp = altDate;
+				try{
+					altDate = new Date(altDate).FormatEnhance("yyyy-mm-dd HH:MM:ss");
+				 }catch(e){
+					altDate = altDateTmp;
+				 }
 			}
 			}
 		}
 		}
 		object.AltDate = altDate;
 		object.AltDate = altDate;

+ 45 - 1
core/src/web/staticres/wxswordfish/style.css

@@ -706,4 +706,48 @@ img{
 	top: 0px;
 	top: 0px;
 	bottom: 0px;
 	bottom: 0px;
 	display: none;
 	display: none;
-}
+}
+.hidden{
+	display:none;
+}
+.text-center{
+	text-align:center;
+}
+.sf_promptone{
+	padding:40px 20px 0px 10px;
+}
+.sf_promptone div{
+	width:100%;
+}
+.sf_font1{
+	color:#999;
+	text-align:left;
+}
+.sf_promptone img{
+	width: 60%;
+}
+.sf_button span{
+    border: 1px solid #24C0D7;
+    display: inline-block;
+    width: 20%;
+    padding: 7px;
+    border-radius: 6px;
+	margin-bottom:20px;
+}
+.sf_tis{
+	text-align:center;
+	padding:20px;
+}
+.sf_tis img{
+	text-align:center;
+	width:60%;
+}
+.sf_mc a{
+	text-decoration:none;
+	color:blue;
+}
+
+
+
+
+

+ 1 - 1
core/src/web/templates/common/bottom.html

@@ -12,7 +12,7 @@
 		<div class="text-center hidden-xs"><img src="{{Msg "seo" "cdn"}}/images/bottom_logo.png"/></div>
 		<div class="text-center hidden-xs"><img src="{{Msg "seo" "cdn"}}/images/bottom_logo.png"/></div>
 		<div class="qfw-bottom-text text-center">
 		<div class="qfw-bottom-text text-center">
 			<a href="/front/web_A_help.html">关于我们</a><span>|</span><a href="/front/web_L_help.html">联系我们</a><span>|</span>&nbsp;&nbsp;<span style="color:white">法律声明&nbsp;&nbsp;&nbsp;&nbsp;</span><span>|</span><a href="/front/web_H_help.html">帮助中心</a><span>|</span><a href="/front/web_O_help.html">意见反馈</a>
 			<a href="/front/web_A_help.html">关于我们</a><span>|</span><a href="/front/web_L_help.html">联系我们</a><span>|</span>&nbsp;&nbsp;<span style="color:white">法律声明&nbsp;&nbsp;&nbsp;&nbsp;</span><span>|</span><a href="/front/web_H_help.html">帮助中心</a><span>|</span><a href="/front/web_O_help.html">意见反馈</a>
-			<div style="line-height:25px">©2015-2016&nbsp;拓普丰联&nbsp;版权所有<span>&nbsp;|&nbsp;</span><a href="http://www.miibeian.gov.cn/state/outPortal/loginPortal.action" target="_blank">豫ICP备15016539号-1</a></div>
+			<div style="line-height:25px">©2015-2016&nbsp;拓普丰联&nbsp;版权所有<span>&nbsp;|&nbsp;</span><a href="http://www.miibeian.gov.cn/state/outPortal/loginPortal.action" target="_blank">京ICP备14030217号-2</a></div>
 		</div>
 		</div>
 	</div>
 	</div>
 </div>
 </div>

+ 3 - 3
core/src/web/templates/enterprise/detail.html

@@ -204,7 +204,7 @@
 							<span>
 							<span>
 								<div>
 								<div>
 									<span id="entinfo-status" class="entinfo-status">
 									<span id="entinfo-status" class="entinfo-status">
-										{{if .T.res.excDirect}}
+										{{if .T.res.Ycml}}
 										<span class="ent-exception">经营异常</span>
 										<span class="ent-exception">经营异常</span>
 										{{end}}
 										{{end}}
 									</span>
 									</span>
@@ -284,8 +284,8 @@
 					  	<div class="entinfo-entscale">
 					  	<div class="entinfo-entscale">
 							<img src="{{Msg "seo" "cdn"}}/images/entcommunity/rens.png">
 							<img src="{{Msg "seo" "cdn"}}/images/entcommunity/rens.png">
 							<div>
 							<div>
-								<span class="entinfo-disabled">公司规模</span>
-								<span></span>
+								<span{{if not .T.res.i_scale}} class="entinfo-disabled"{{end}}>公司规模</span>
+								<span>{{if .T.res.i_scale}}{{.T.res.i_scale}}人{{end}}</span>
 							</div>
 							</div>
 						</div>
 						</div>
 						<a class="entinfo-entscale-round"></a>
 						<a class="entinfo-entscale-round"></a>

+ 3 - 16
core/src/web/templates/manage/tj.html

@@ -110,7 +110,7 @@
 						</tr>
 						</tr>
 						<tr>
 						<tr>
 							<td></td>
 							<td></td>
-							<td>剑鱼:<span class="number">{{.T.rs.sfish}}</span> &nbsp;&nbsp;<a  href="javascript:void(0);" onclick="controlShowOrHide(this)" value="用户订阅详细" data-set="countfish">用户订阅详细</a></td>
+							<td>剑鱼:<span class="number"><script>document.write({{.T.rs.snfish}}+{{.T.rs.sfish}});</script></span> &nbsp;&nbsp;</td>
 							<td>订阅关键词:<span class="number">{{.T.msgcount}}</span>,<a href="javascript:void(0);" onclick="controlShowOrHide(this)" value="关键词汇总表" data-set="f-keytable">关键词汇总表</a></td>
 							<td>订阅关键词:<span class="number">{{.T.msgcount}}</span>,<a href="javascript:void(0);" onclick="controlShowOrHide(this)" value="关键词汇总表" data-set="f-keytable">关键词汇总表</a></td>
 							<td></td>
 							<td></td>
 							<td></td>
 							<td></td>
@@ -123,23 +123,10 @@
 				</div>
 				</div>
 				<div class="showtop f-keytable lm" style="margin-top:20px;display:none;">
 				<div class="showtop f-keytable lm" style="margin-top:20px;display:none;">
 					<div style="text-align:right;margin-bottom:10px;color:#ff5a5f;">
 					<div style="text-align:right;margin-bottom:10px;color:#ff5a5f;">
-						招标订阅关键字
+						订阅关键字
 					</div>
 					</div>
 					<ul>
 					<ul>
-						{{range $k,$v:=.T.tender}}
-							<li>
-							{{$k}}(<span>{{$v}}</span>)
-							</li>
-						{{end}}
-					</ul>
-					<div style="clear:both;">
-						<hr />
-					</div>
-					<div style="text-align:right;margin-bottom:10px;color:#ff5a5f;">
-						中标订阅关键字
-					</div>
-					<ul>
-						{{range $k,$v:=.T.bid}}
+						{{range $k,$v:=.T.keys}}
 							<li>
 							<li>
 							{{$k}}(<span>{{$v}}</span>)
 							{{$k}}(<span>{{$v}}</span>)
 							</li>
 							</li>

+ 5 - 5
core/src/web/templates/member/incmobile/baseinfo.html

@@ -91,13 +91,13 @@
 			</div>
 			</div>
 			<span class="col-xs-7 entvar ">{{if .T.res.RegOrgName}}{{.T.res.RegOrgName}}{{else}}空{{end}}</span>
 			<span class="col-xs-7 entvar ">{{if .T.res.RegOrgName}}{{.T.res.RegOrgName}}{{else}}空{{end}}</span>
 		</li>
 		</li>
-<!--		<li>
+		<li>
 			<div class="col-xs-5">
 			<div class="col-xs-5">
-				<span class="glyphicon glyphicon-flag colorc"></span>
-				<span class="ent entnull">公司规模</span>
+				<img src="{{Msg "seo" "cdn"}}/images/entcommunity/rens.png"  width=23 />
+				<span class="ent {{if not .T.res.i_scale}}entnull{{end}}">公司规模</span>
 			</div>
 			</div>
-			<span class="col-xs-7 entvar">空</span>
-		</li>-->
+			<span class="col-xs-7 entvar">{{if .T.res.i_scale}}{{.T.res.i_scale}}人{{else}}{{end}}</span>
+		</li>
 		<li>
 		<li>
 			<div class="col-xs-12">
 			<div class="col-xs-12">
 				<img src="{{Msg "seo" "cdn"}}/images/entcommunity/ren.png"  width=23 />
 				<img src="{{Msg "seo" "cdn"}}/images/entcommunity/ren.png"  width=23 />

+ 1 - 1
core/src/web/templates/member/incmobile/detailindex.html

@@ -129,7 +129,7 @@
 					
 					
 				</script>
 				</script>
 				</div>
 				</div>
-				{{if .T.res.excDirect}}<span class="excDirect">经营异常</span>{{end}}
+				{{if .T.res.Ycml}}<span class="excDirect">经营异常</span>{{end}}
 			</div>
 			</div>
 		</div>
 		</div>
 		<div class="col-xs-12 index-middle">
 		<div class="col-xs-12 index-middle">

+ 4 - 4
core/src/web/templates/member/incmobile/list.html

@@ -154,7 +154,7 @@
 		var data={{.T.data}}
 		var data={{.T.data}}
 		var entcard={{.T.querymap.entcard}};
 		var entcard={{.T.querymap.entcard}};
 		var strVar = "";
 		var strVar = "";
-		strVar += "<li class=\"col-xs-12\"><table align=\"center\"><caption><div class=\"left\"><a href=\"/enterprise/$Id.html\">$EntName</a><b class=\"$status\" style='display:inline-block;'>$OpStateName<\/b><b class=\"$excClass\" style='display:inline-block;'>$excDirect<\/b><\/div><div style='float:right;margin-right:10px;' data-id='$Id' onclick='follow(this)'><i class='glyphicon $gz' style=\"font-size:11px;\">&nbsp;<\/i><span>$gzt</span><\/div><\/caption><tr><td class=\"left\">注册号:<span>$RegNo<\/span><\/td><td align='right'>法定代表人:<span>$LeRep<\/span><\/td><\/tr><tr><td class=\"left\">成立时间:<span>$EstDate<\/span><\/td><td align='right'>所在地:<span>$OpLocDistrict<\/span><\/td><\/tr><tr><td colspan=\"2\"><hr class=\"list\"><\/td><\/tr><\/table><\/li>";
+		strVar += "<li class=\"col-xs-12\"><table align=\"center\"><caption><div class=\"left\"><a href=\"/enterprise/$Id.html\">$EntName</a><b class=\"$status\" style='display:inline-block;'>$OpStateName<\/b><b class=\"$excClass\" style='display:inline-block;'>$Ycml<\/b><\/div><div style='float:right;margin-right:10px;' data-id='$Id' onclick='follow(this)'><i class='glyphicon $gz' style=\"font-size:11px;\">&nbsp;<\/i><span>$gzt</span><\/div><\/caption><tr><td class=\"left\">注册号:<span>$RegNo<\/span><\/td><td align='right'>法定代表人:<span>$LeRep<\/span><\/td><\/tr><tr><td class=\"left\">成立时间:<span>$EstDate<\/span><\/td><td align='right'>所在地:<span>$OpLocDistrict<\/span><\/td><\/tr><tr><td colspan=\"2\"><hr class=\"list\"><\/td><\/tr><\/table><\/li>";
 		function loadData(){
 		function loadData(){
 			   var li="";
 			   var li="";
 				var len=i+5;
 				var len=i+5;
@@ -178,8 +178,8 @@
 											 .replace("$RegNo",data[i]["RegNo"])
 											 .replace("$RegNo",data[i]["RegNo"])
 											 .replace("$LeRep",data[i]["LeRep"])
 											 .replace("$LeRep",data[i]["LeRep"])
 											 .replace("$OpLocDistrict",data[i]["OpLocDistrict"])
 											 .replace("$OpLocDistrict",data[i]["OpLocDistrict"])
-											 .replace("$EstDate",data[i]["EstDate"]=="1970-01-01"?"":data[i]["EstDate"])
-											 .replace("$excDirect",data[i]["excDirect"]=="true"?"经营异常":"$excDirect")
+											 .replace("$EstDate",data[i]["EstDate"])
+											 .replace("$Ycml",data[i]["Ycml"]=="true"?"经营异常":"$Ycml")
 											 .replace("$Id",data[i]["_id"])
 											 .replace("$Id",data[i]["_id"])
 											 .replace("$Id",data[i]["_id"])
 											 .replace("$Id",data[i]["_id"])
 											 .replace("$OpStateName",data[i]["OpStateName"]=="<nil>"?"":data[i]["OpStateName"].substr(0,2));
 											 .replace("$OpStateName",data[i]["OpStateName"]=="<nil>"?"":data[i]["OpStateName"].substr(0,2));
@@ -188,7 +188,7 @@
 					}else{
 					}else{
 						tmp=tmp.replace("$status","ent-status-g");
 						tmp=tmp.replace("$status","ent-status-g");
 					}
 					}
-					tmp=tmp.replace("<b class=\"$excClass\" style='display:inline-block;'>$excDirect<\/b>","");
+					tmp=tmp.replace("<b class=\"$excClass\" style='display:inline-block;'>$Ycml<\/b>","");
 					tmp=tmp.replace("$excClass","excDirect");
 					tmp=tmp.replace("$excClass","excDirect");
 					if(data[i]["status"]=="y"){
 					if(data[i]["status"]=="y"){
 						tmp=tmp.replace("$gz","yiguanzhu").replace("$gzt","已关注");	
 						tmp=tmp.replace("$gz","yiguanzhu").replace("$gzt","已关注");	

+ 1 - 1
core/src/web/templates/search/enterpriseList.html

@@ -89,7 +89,7 @@
 									</script>
 									</script>
 									</span>
 									</span>
 									{{end}}
 									{{end}}
-									{{if $v.excDirect}}
+									{{if $v.Ycml}}
 									<span class="margin-l-5 ent-exception">经营异常</span>
 									<span class="margin-l-5 ent-exception">经营异常</span>
 									{{end}}
 									{{end}}
 									{{ if index $v "s_action"}}
 									{{ if index $v "s_action"}}

+ 39 - 0
core/src/web/templates/swordfish/wxex.html

@@ -0,0 +1,39 @@
+<html>
+<head>
+<title>企明星-剑鱼-提醒页面</title>
+<meta http-equiv="X-UA-Compatible" content="IE=edge,Chrome=1" />
+<meta name="viewport" content="width=device-width, initial-scale=1">
+<meta name="renderer" content="webkit">
+<link href="{{Msg "seo" "cdn"}}/wxswordfish/style.css" rel="stylesheet">
+<script type="text/javascript" src="http://res.wx.qq.com/open/js/jweixin-1.0.0.js"></script>
+<script src="{{Msg "seo" "cdn"}}/js/jquery.js"></script>
+<script>
+	var newsubscribe = {{.T.newsubscribe}};
+	$(function(){
+		if (newsubscribe == "Y"){	
+		window.location.href="http://mp.weixin.qq.com/s?__biz=MzIyNTM1NDUyNw==&mid=100000024&idx=1&sn=c2f4271a361bfb90b22abb1d6818accc#rd";
+		return false;
+		}else{
+			$(".sf_promptone").removeClass("hidden");
+		};
+		$(".sf_button").click(function(){
+			window.location.href="/swordfish/page";
+		});
+	})
+</script>
+</head>
+<body>
+<div class="sf_promptone text-center hidden">
+
+<div class="sf_font1">  为了提供更好的服务,剑鱼招标订阅开通了独立的微信公众号,企明星公众号中的剑鱼很快将不再更新。</div>
+<br>
+<div>请长按下面的图片、选择“识别二维码”。</div>
+<div><img src="/images/swordfish/sf_01.jpg" alt="剑鱼招标订阅微信公众号"></div>
+<div>关注“剑鱼招标订阅”</div>
+<br>
+<div class="sf_font1">  您不用担心原订阅记录会消失,只要扫码关注新服务号,订阅关键词依然还在,招标信息将会通过新号继续向您推送,使用剑鱼将不再消耗积分。您在启用新服务号并成功订阅后,原“企明星”的订阅服务将会自动关闭。</div>
+<br>
+<div class="sf_button text-center"><span>跳 过</span></div>
+</div>
+</body>
+</html>

+ 11 - 0
core/src/web/templates/swordfish/wxpush.html

@@ -177,6 +177,17 @@ a{
 </style>
 </style>
 </head>
 </head>
 <body>
 <body>
+<div class="sf_tis">
+<div>长按图片、选择“识别二维码”</div>
+<br>
+<div>关注“剑鱼招标订阅”</div>
+<div><img src="/images/swordfish/sf_01.jpg"/></div>
+<br>
+<div style="text-align:left;">  您不用担心原订阅记录会消失,只要扫码关注新服务号,订阅关键词依然还在,招标信息将会通过新号继续向您推送,使用剑鱼将不再消耗积分。您在启用新服务号并成功订阅后,原“企明星”的订阅服务将会自动关闭。</div>
+
+
+
+</div>
 {{include "/swordfish/wxtoolbar.html"}}
 {{include "/swordfish/wxtoolbar.html"}}
 {{include "/common/baiducc.html"}}
 {{include "/common/baiducc.html"}}
 </body>
 </body>

+ 2 - 2
core/src/web/templates/swordfish/wxrssset.html

@@ -21,8 +21,8 @@ if(sessionStorage&&sessionStorage.version=="1"){
 <script>
 <script>
 	var msgset= {{.T.msgset}};
 	var msgset= {{.T.msgset}};
 	initShare({{.T.signature}},{{.T.shareid}});
 	initShare({{.T.signature}},{{.T.shareid}});
-	var searchname = {{.T.searchname}}
-	var s_type = {{.T.s_type}}
+	var searchname = {{.T.searchname}};
+	var s_type = {{.T.s_type}};
 </script>
 </script>
 </head>
 </head>
 <body>
 <body>

+ 1 - 2
push/src/config.json

@@ -1,2 +1 @@
-{"bid":{"interval":60,"lastpushtime":"2015-10-10 10:10:10"},"bidStartTime":"2015-12-30 17:28:48","bidTitle":"亲!剑鱼为您速报最新鲜的中标信息啦","bidViewDomain":"192.168.3.132","durationMinutes":10,"fixPush":"oJULtwzXo6EFV1Ah-XeyRBimXGM8","mail_bid":"\u003cdiv\u003e%s\u003c/div\u003e,想了解更多信息,请访问http://www.qimingxing.info。","maxPushSize":50,"mgoAddr":"192.168.3.18:27080","mgoSize":10,"province":{"上海":9,"云南":25,"内蒙古":5,"北京":1,"台湾":32,"吉林":7,"四川":13,"天津":2,"宁夏":30,"安徽":12,"山东":15,"山西":4,"广东":19,"广西":20,"新疆":31,"江苏":10,"江西":14,"河北":3,"河南":16,"浙江":11,"海南":21,"湖北":17,"湖南":18,"澳门":34,"甘肃":28,"福建":13,"西藏":26,"贵州":24,"辽宁":6,"重庆":22,"陕西":27,"青海":29,"香港":33,"黑龙江":8},"pushInfoScopeDays":50,"rpcPort":"8766","smtpAddr":"smtp.exmail.qq.com","smtpFromUser":"企明星","smtpPort":465,"smtpPwd":"qy123456","smtpUser":"qyfw@topnet.net.cn","tenderStartTime":"2016-05-25 18:10:19","tenderTitle":"亲!剑鱼为您速报最新鲜的招标信息啦","weixinRpcServer":"127.0.0.1:82","weixin_bid":"\u003cdiv\u003e%s最新招标信息\u003c/div\u003e\u003cdiv\u003e%s\u003c/div\u003e","wxRpcRemark":"请到网站个人中心查看详细."}
-
+{"bid":{"interval":60,"lastpushtime":"2015-10-10 10:10:10"},"bidStartTime":"2016-05-26 00:00:00","bidTitle":"亲!剑鱼为您速报最新鲜的中标信息啦","bidViewDomain":"192.168.3.132","durationMinutes":10,"fixPush":"oJULtwzXo6EFV1Ah-XeyRBimXGM8","mail_bid":"\u003cdiv\u003e%s\u003c/div\u003e,想了解更多信息,请访问http://www.qimingxing.info。","maxPushSize":50,"mgoAddr":"192.168.3.18:27080","mgoSize":10,"province":{"上海":9,"云南":25,"内蒙古":5,"北京":1,"台湾":32,"吉林":7,"四川":13,"天津":2,"宁夏":30,"安徽":12,"山东":15,"山西":4,"广东":19,"广西":20,"新疆":31,"江苏":10,"江西":14,"河北":3,"河南":16,"浙江":11,"海南":21,"湖北":17,"湖南":18,"澳门":34,"甘肃":28,"福建":13,"西藏":26,"贵州":24,"辽宁":6,"重庆":22,"陕西":27,"青海":29,"香港":33,"黑龙江":8},"pushInfoScopeDays":50,"rpcPort":"8766","smtpAddr":"smtp.exmail.qq.com","smtpFromUser":"企明星","smtpPort":465,"smtpPwd":"qy123456","smtpUser":"qyfw@topnet.net.cn","tenderStartTime":"2016-05-30 17:07:34","tenderTitle":"亲!剑鱼为您速报最新鲜的招标信息啦","weixinRpcServer":"127.0.0.1:82","weixin_bid":"\u003cdiv\u003e%s最新招标信息\u003c/div\u003e\u003cdiv\u003e%s\u003c/div\u003e","wxRpcRemark":"请到网站个人中心查看详细."}

+ 2 - 1
push/src/qfw/push/dopush/dopush.go

@@ -392,7 +392,8 @@ func SendWeixin(k *push.MemberInterest, TITLE, ShortTitle, str, stype string, no
 		wxDate = time.Now().Local().Add(time.Duration(-n2) * time.Second).Add(time.Duration(-n1*24) * time.Hour).Format(util.Date_Full_Layout)
 		wxDate = time.Now().Local().Add(time.Duration(-n2) * time.Second).Add(time.Duration(-n1*24) * time.Hour).Format(util.Date_Full_Layout)
 	}
 	}
 
 
-	wxstr := "\n点击下方“详情”查看详细信息。\n以上" + ShortTitle + "信息,是剑鱼根据关键字“" + strings.Join(k.Interest, ";") + "”奋力查找并推送,如不合您心意,请猛戳企明星菜单“剑鱼”进行修改。"
+	//wxstr := "\n点击下方“详情”查看详细信息。\n以上" + ShortTitle + "信息,是剑鱼根据关键字“" + strings.Join(k.Interest, ";") + "”奋力查找并推送,如不合您心意,请猛戳企明星菜单“剑鱼”进行修改。"
+	wxstr := "\n为了提供更好的服务,剑鱼招标订阅开通了独立的微信公众号,企明星公众号中的剑鱼很快将不再更新,点击“详情”可关注公众号“剑鱼招标订阅”。感谢您的支持!"
 	push.SendWinXin(&qrpc.NotifyMsg{
 	push.SendWinXin(&qrpc.NotifyMsg{
 		Openid:  k.Openid,
 		Openid:  k.Openid,
 		Title:   push.PushConfig[stype+"Title"].(string),
 		Title:   push.PushConfig[stype+"Title"].(string),

BIN
weixin/doc/kfex.rar


+ 10 - 8
weixin/src/config.json

@@ -1,18 +1,18 @@
 {
 {
-	"port":"82",
+	"port":"80",
 	"domain":"127.0.0.1",
 	"domain":"127.0.0.1",
 	"imgpath":"E:/go_workspace/qfw/core/src/web/staticres",
 	"imgpath":"E:/go_workspace/qfw/core/src/web/staticres",
-	"mongodbServers": "10.116.86.154:27080",
-	"elasticsearch":"http://10.116.86.154:9800",
+	"mongodbServers": "192.168.3.18:27080",
+	"elasticsearch":"http://192.168.3.18:9800",
     "elasticPoolSize": 30,
     "elasticPoolSize": 30,
 	"mongodbPoolSize":5,
 	"mongodbPoolSize":5,
-	"mongodbName":"qfw",	"redisServers":"enterprise=10.116.86.154:1379,service=10.116.86.154:2379,other=10.116.86.154:3379,sso=10.116.86.154:1379,credit=10.116.86.154:4379",
+	"mongodbName":"qfw",	"redisServers":"enterprise=192.168.3.14:1379,service=192.168.3.14:2379,other=192.168.3.14:3379,sso=192.168.3.14:1379,credit=192.168.3.14:4379",
 	"rpcport":"83",
 	"rpcport":"83",
 	"serviceTip":"服务指南",
 	"serviceTip":"服务指南",
 	"appcontext":"weixin",
 	"appcontext":"weixin",
-	"appid":"wx76e1309b01a7b17e",
-	"token":"topnet2015",
-	"appsecret":"dd00e71cb2370432d9de848b674eb8e7",
+	"appid":"wxb8c5625e055bd967",
+	"token":"zzfykc",
+	"appsecret":"288436fdde6349725b4869d54162d3c6",
     "aboutmeurl":"http://www.qimingxing.info/article/aboutme",
     "aboutmeurl":"http://www.qimingxing.info/article/aboutme",
     "conactusurl":"http://www.qimingxing.info/article/contactus",
     "conactusurl":"http://www.qimingxing.info/article/contactus",
 	"wsqurl": "http://s.p.qq.com/pub/jump?d=AAAXeGLZ",
 	"wsqurl": "http://s.p.qq.com/pub/jump?d=AAAXeGLZ",
@@ -54,5 +54,7 @@
 		"out":"您已退出本次工作,辛苦了,再见。",
 		"out":"您已退出本次工作,辛苦了,再见。",
 		"reply":"已收到您的回复,谢谢。",
 		"reply":"已收到您的回复,谢谢。",
 		"msgError":"该功能暂不可用,请稍后再试!"
 		"msgError":"该功能暂不可用,请稍后再试!"
-	}
+	},
+	"kfprefix":"企明星-",
+	"kfport":"9896"
 }
 }

+ 43 - 4
weixin/src/main.go

@@ -1,8 +1,10 @@
 package main
 package main
 
 
 import (
 import (
-	"endless"
+	"encoding/json"
+	//"endless"
 	"log"
 	"log"
+	"net/http"
 	//"net/http"
 	//"net/http"
 	"qfw/util"
 	"qfw/util"
 	"qfw/util/elastic"
 	"qfw/util/elastic"
@@ -32,6 +34,9 @@ func init() {
 	weixin.InitWeixinSdk()
 	weixin.InitWeixinSdk()
 	//连接消息总线
 	//连接消息总线
 	go weixin.InitDgWork()
 	go weixin.InitDgWork()
+	go kf()
+	go weixin.GetKfMsgJob()
+	go weixin.GetOnlineKfJob()
 }
 }
 
 
 func main() {
 func main() {
@@ -39,8 +44,42 @@ func main() {
 	//启动Rpc服务
 	//启动Rpc服务
 	rpc.StartWeixinRpc(weixin.Mux)
 	rpc.StartWeixinRpc(weixin.Mux)
 	//启动web服务
 	//启动web服务
-	//http.ListenAndServe(":"+wf.SysConfig.Port, nil) // 启动接收微信数据服务器
-	endless.ListenAndServe(":"+wf.SysConfig.Port, nil, func() {
-		rpc.DestoryRpc()
+	http.ListenAndServe(":"+wf.SysConfig.Port, nil) // 启动接收微信数据服务器
+	/*
+		endless.ListenAndServe(":"+wf.SysConfig.Port, nil, func() {
+			rpc.DestoryRpc()
+		})
+		**/
+	b := make(chan bool, 1)
+	<-b
+}
+
+func kf() {
+	defer util.Catch()
+	http.HandleFunc("/wxkf/getkflist", func(w http.ResponseWriter, r *http.Request) {
+		w.Header().Set("Content-Type", "application/json")
+		//内存
+		b, _ := json.Marshal(weixin.GetKfList(10))
+		w.Write(b)
+	})
+	http.HandleFunc("/wxkf/getkflist0", func(w http.ResponseWriter, r *http.Request) {
+		w.Header().Set("Content-Type", "application/json")
+		//url
+		b, _ := json.Marshal(weixin.GetKfList(0))
+		w.Write(b)
+	})
+	http.HandleFunc("/wxkf/getkflist1", func(w http.ResponseWriter, r *http.Request) {
+		w.Header().Set("Content-Type", "application/json")
+		//url+客服信息
+		b, _ := json.Marshal(weixin.GetKfList(1))
+		w.Write(b)
+	})
+
+	http.HandleFunc("/msginit/init", func(w http.ResponseWriter, r *http.Request) {
+		w.Header().Set("Content-Type", "application/json")
+		weixin.GetKfMsg(1)
+		b, _ := json.Marshal(map[string]interface{}{"res": true})
+		w.Write(b)
 	})
 	})
+	http.ListenAndServe(":"+wf.SysConfig.Kfport, nil)
 }
 }

+ 1 - 1
weixin/src/qfw/weixin/dao/userdao.go

@@ -92,7 +92,7 @@ func AddUser(openid, unionid, bindweixin, userphoto string) (err error, flag int
 	} else {
 	} else {
 		log.Println("update user:", userphoto)
 		log.Println("update user:", userphoto)
 		//已经注册过
 		//已经注册过
-		Update("user", M{"s_unionid": unionid, "i_type": 3}, `{"$set":{"s_m_openid":"`+openid+`","s_avatar":"`+userphoto+`"}}`, true, false)
+		Update("user", M{"s_m_openid": openid}, `{"$set":{"s_avatar":"`+userphoto+`"}}`, true, false)
 		return errors.New("该微信号已经注册过"), 0
 		return errors.New("该微信号已经注册过"), 0
 	}
 	}
 }
 }

+ 1 - 2
weixin/src/qfw/weixin/distinguishwork.go

@@ -49,8 +49,7 @@ func init() {
 }
 }
 
 
 func InitDgWork() {
 func InitDgWork() {
-	client, _ = util.StartClient(processevent, wf.SysConfig.Msgserver, []int{util.SERVICE_DISTINGUISH}, 20)
-	client.ResetMyName("识别验证码")
+	client, _ = util.StartClient(processevent, wf.SysConfig.Msgserver, "识别验证码", []int{util.SERVICE_DISTINGUISH})
 }
 }
 func processevent(p *util.Packet) {
 func processevent(p *util.Packet) {
 	event := int(p.Event)
 	event := int(p.Event)

+ 8 - 1
weixin/src/qfw/weixin/msgtxtchandler.go

@@ -131,7 +131,14 @@ func MsgTxtHandler(w ResponseWriter, r *Request) {
 			dao.SaveWeixinOfflineMessage(r.FromUserName, r.Content, now.Unix())
 			dao.SaveWeixinOfflineMessage(r.FromUserName, r.Content, now.Unix())
 			w.ReplyText(wf.SysConfig.WeixinAutoRpl)
 			w.ReplyText(wf.SysConfig.WeixinAutoRpl)
 		} else {
 		} else {
-			w.Reply2CustomerService()
+			//w.Reply2CustomerService()
+			//转接到平台客服
+			s := GetMinKf()
+			if s == "" {
+				w.Reply2CustomerService()
+			} else {
+				w.TransferCustomerService2(r.FromUserName, GetMinKf())
+			}
 		}
 		}
 	}
 	}
 }
 }

+ 272 - 0
weixin/src/qfw/weixin/weixincustomer.go

@@ -0,0 +1,272 @@
+package weixin
+
+import (
+	"encoding/json"
+	"log"
+	"qfw/util"
+	"qfw/util/mongodb"
+	"qfw/weixinconfig"
+	"sort"
+	"strings"
+	"sync"
+	"time"
+)
+
+//定时获取客服状态
+//客服分类,平台客服 企明星-小微,业务客服 工商注册-小张
+//处理客服类型
+/**
+{
+   "kf_online_list": [
+       {
+           "kf_account": "test1@test",
+           "status": 1,
+           "kf_id": "1001",
+           "accepted_case": 1
+       },
+       {
+           "kf_account": "test2@test",
+           "status": 1,
+           "kf_id": "1002",
+           "accepted_case": 2
+       }
+   ]
+}
+**/
+
+//通过url获取在线客服信息
+func GetKfList(t int) []map[string]interface{} {
+	if t == 10 {
+		return ONLINE_KF
+	}
+	defer util.Catch()
+	ONLINE_KF_LOCK.Lock()
+	defer ONLINE_KF_LOCK.Unlock()
+	defer util.Catch()
+	l := Mux.GetOnlineKfList()
+	kf_online_list := l["kf_online_list"].([]interface{})
+	QMX_ONLINE_KF = arr{}
+	ONLINE_KF = []map[string]interface{}{}
+	if len(kf_online_list) > 0 {
+		GetKfInfoByMap(t)
+		for _, onlist := range kf_online_list {
+			tmp := onlist.(map[string]interface{})
+			kf_account := tmp["kf_account"].(string)
+			onekf := KF_INFO[kf_account]
+			if onekf == nil {
+				log.Println("数据库客服列表不完整...")
+			} else {
+				for k, v := range onekf {
+					tmp[k] = v
+				}
+			}
+			kf_nick := tmp["kf_nick"].(string)
+			if strings.HasPrefix(kf_nick, weixinconfig.SysConfig.Kfprefix) {
+				k := map[string]interface{}{}
+				k["kf_account"] = tmp["kf_account"].(string)
+				k["accepted_case"] = util.IntAll(tmp["accepted_case"])
+				QMX_ONLINE_KF = append(QMX_ONLINE_KF, k)
+			}
+			ONLINE_KF = append(ONLINE_KF, tmp)
+		}
+	}
+	//排序
+	sort.Sort(&QMX_ONLINE_KF)
+	//log.Println(res)
+	return ONLINE_KF
+}
+
+//定时更新在线客服
+func GetOnlineKfJob() {
+	hour := time.Now().Hour()
+	if hour > 6 && hour < 23 {
+		GetKfList(0)
+	}
+	time.AfterFunc(1*time.Minute, GetOnlineKfJob)
+}
+
+//获取最少的客服
+func GetMinKf() string {
+	defer util.Catch()
+	ONLINE_KF_LOCK.Lock()
+	defer ONLINE_KF_LOCK.Unlock()
+	res := ""
+	if QMX_ONLINE_KF.Len() > 0 {
+		res = QMX_ONLINE_KF[0]["kf_account"].(string)
+	}
+	return res
+}
+
+type arr []map[string]interface{}
+
+func (a *arr) Len() int {
+	return len(*a)
+}
+func (a *arr) Less(i, j int) bool {
+	return (*a)[i]["accepted_case"].(int) < (*a)[j]["accepted_case"].(int)
+}
+func (a *arr) Swap(i, j int) {
+	tmp := (*a)[i]
+	(*a)[i] = (*a)[j]
+	(*a)[j] = tmp
+}
+
+/**
+{
+    "kf_list" : [
+       {
+          "kf_account" : "test1@test",
+          "kf_headimgurl" : "http://mmbiz.qpic.cn/mmbiz/4whpV1VZl2iccsvYbHvnphkyGtnvjfUS8Ym0GSaLic0FD3vN0V8PILcibEGb2fPfEOmw/0",
+          "kf_id" : "1001",
+          "kf_nick" : "ntest1",
+          "kf_wx" : "kfwx1"
+       }]
+}
+**/
+const (
+	KF_COLLECTION    = "wxkf_info"
+	KFMSG_COLLECTION = "wxkf_msg"
+)
+
+var Last_kf_time = int64(0) //获取客服的最后时间
+var KF_INFO = map[string]map[string]interface{}{}
+var KF_LOCK = sync.Mutex{}                 //客服信息锁
+var ONLINE_KF_LOCK = sync.Mutex{}          //在线客服锁
+var ONLINE_KF = []map[string]interface{}{} //所有在线客服
+var QMX_ONLINE_KF = arr{}                  //平台在线客服
+var MSG_LOCL = sync.Mutex{}                //客服消息锁
+var Last_msg_time = int64(0)               //获取聊天记录的最后时间
+
+//获取客服信息
+func GetKfInfoByApi() {
+	defer util.Catch()
+	KF_LOCK.Lock()
+	defer KF_LOCK.Unlock()
+	if time.Now().Unix()-Last_kf_time > 60 {
+		Last_kf_time = time.Now().Unix()
+		l := Mux.GetKfList()
+		if l != nil && len(l) > 0 {
+			kf_list := l["kf_list"].([]interface{})
+			if len(kf_list) > 0 {
+				KF_INFO = map[string]map[string]interface{}{}
+				for _, kf := range kf_list {
+					onekf := kf.(map[string]interface{})
+					kf_account := onekf["kf_account"].(string)
+					KF_INFO[kf_account] = onekf
+				}
+				if mongodb.Del(KF_COLLECTION, nil) {
+					if !mongodb.SaveBulk(KF_COLLECTION, util.ObjArrToMapArr(kf_list)...) {
+						time.Sleep(10 * time.Second)
+						mongodb.SaveBulk(KF_COLLECTION, util.ObjArrToMapArr(kf_list)...)
+					}
+				}
+			}
+		}
+	}
+}
+
+//从内存中或Url获取客服信息/0从内存 /1从url
+func GetKfInfoByMap(t int) map[string]map[string]interface{} {
+	if t == 0 {
+		if len(KF_INFO) == 0 {
+			KF_LOCK.Lock()
+			defer KF_LOCK.Unlock()
+			res := mongodb.Find(KF_COLLECTION, nil, nil, nil, false, -1, -1)
+			if res != nil && *res != nil && len(*res) > 0 {
+				for _, kf := range *res {
+					kf_account := kf["kf_account"].(string)
+					KF_INFO[kf_account] = kf
+				}
+			} else {
+				GetKfInfoByApi()
+			}
+		}
+	} else if t == 1 {
+		GetKfInfoByApi()
+	}
+	return KF_INFO
+}
+
+type Msg struct {
+	MsgId     int64 `json:"msgid"`
+	StartTime int64 `json:"starttime"`
+	EndTime   int64 `json:"endtime"`
+	Number    int   `json:"number"`
+}
+
+//获取客服聊天记录
+//查询时间不能超过24小时
+//t=0 正常 t=1初始化取24小时
+func GetKfMsg(t int) {
+	defer util.Catch()
+	MSG_LOCL.Lock()
+	defer MSG_LOCL.Unlock()
+	now := time.Now().Unix() - 3*60
+	if Last_msg_time == 0 {
+		res := mongodb.Find(KFMSG_COLLECTION, nil, `{"l_gettime":-1}`, `{"l_gettime":1}`, false, 0, 1)
+		if res != nil && *res != nil && len(*res) == 1 {
+			Last_msg_time = util.Int64All((*res)[0]["l_gettime"])
+		}
+		if Last_msg_time == 0 || now-Last_msg_time > 86400 {
+			if t == 1 {
+				Last_msg_time = now - 86400
+			} else {
+				log.Println("请检查客服信息初始化..")
+				return
+			}
+		}
+	}
+	msg := Msg{}
+	msg.MsgId = 1
+	msg.Number = 2000
+	msg.EndTime = now
+	msg.StartTime = Last_msg_time
+	for i := 0; i < 15; i++ {
+		bs, err := Mux.PostCustomMsg("https://api.weixin.qq.com/customservice/msgrecord/getmsglist?access_token=", msg)
+		if err == nil {
+			var res map[string]interface{}
+			_err := json.Unmarshal(bs, &res)
+			if _err == nil && res != nil {
+				recordlist := res["recordlist"].([]interface{})
+				recMap := []map[string]interface{}{}
+				for _, record := range recordlist {
+					tmp := record.(map[string]interface{})
+					tmp["l_gettime"] = now
+					tmp["opercode"] = util.IntAll(tmp["opercode"])
+					recMap = append(recMap, tmp)
+				}
+				//保存客服消息
+				if len(recMap) > 0 {
+					if !mongodb.SaveBulk(KFMSG_COLLECTION, recMap...) {
+						break
+					}
+				}
+				num := util.IntAll(res["number"])
+				mid := util.Int64All(res["msgid"])
+				log.Println(num, mid, i, len(recMap))
+				if num < msg.Number {
+					Last_msg_time = now
+					break
+				} else {
+					msg.MsgId = mid
+				}
+			} else {
+				log.Println(_err)
+				break
+			}
+		} else {
+			log.Println(err)
+			break
+		}
+		time.Sleep(1 * time.Second)
+	}
+}
+
+//定时抓取客服记录
+func GetKfMsgJob() {
+	hour := time.Now().Hour()
+	if hour > 6 && hour < 20 {
+		GetKfMsg(0)
+	}
+	time.AfterFunc(15*time.Minute, GetKfMsgJob)
+}

+ 178 - 106
weixin/src/qfw/weixin/weixinsdk.go

@@ -18,7 +18,7 @@ import (
 	"path/filepath"
 	"path/filepath"
 	"regexp"
 	"regexp"
 	"sort"
 	"sort"
-	"strings"
+	"sync/atomic"
 	"time"
 	"time"
 )
 )
 
 
@@ -27,7 +27,7 @@ const (
 	msgEvent          = "event"
 	msgEvent          = "event"
 	EventSubscribe    = "subscribe"
 	EventSubscribe    = "subscribe"
 	EventUnsubscribe  = "unsubscribe"
 	EventUnsubscribe  = "unsubscribe"
-	EventScan         = "scan"
+	EventScan         = "SCAN"
 	EventView         = "VIEW"
 	EventView         = "VIEW"
 	EventClick        = "CLICK"
 	EventClick        = "CLICK"
 	EventLocation     = "LOCATION"
 	EventLocation     = "LOCATION"
@@ -78,6 +78,7 @@ const (
 	weixinHost               = "https://api.weixin.qq.com/cgi-bin"
 	weixinHost               = "https://api.weixin.qq.com/cgi-bin"
 	weixinQRScene            = "https://api.weixin.qq.com/cgi-bin/qrcode"
 	weixinQRScene            = "https://api.weixin.qq.com/cgi-bin/qrcode"
 	weixinShowQRScene        = "https://mp.weixin.qq.com/cgi-bin/showqrcode"
 	weixinShowQRScene        = "https://mp.weixin.qq.com/cgi-bin/showqrcode"
+	weixinMaterialURL        = "https://api.weixin.qq.com/cgi-bin/material"
 	weixinShortURL           = "https://api.weixin.qq.com/cgi-bin/shorturl"
 	weixinShortURL           = "https://api.weixin.qq.com/cgi-bin/shorturl"
 	weixinUserInfo           = "https://api.weixin.qq.com/cgi-bin/user/info"
 	weixinUserInfo           = "https://api.weixin.qq.com/cgi-bin/user/info"
 	weixinFileURL            = "http://file.api.weixin.qq.com/cgi-bin/media"
 	weixinFileURL            = "http://file.api.weixin.qq.com/cgi-bin/media"
@@ -88,17 +89,19 @@ const (
 	// Max retry count
 	// Max retry count
 	retryMaxN = 3
 	retryMaxN = 3
 	// Reply format
 	// Reply format
-	replyText               = "<xml>%s<MsgType><![CDATA[text]]></MsgType><Content><![CDATA[%s]]></Content></xml>"
-	replyImage              = "<xml>%s<MsgType><![CDATA[image]]></MsgType><Image><MediaId><![CDATA[%s]]></MediaId></Image></xml>"
-	replyVoice              = "<xml>%s<MsgType><![CDATA[voice]]></MsgType><Voice><MediaId><![CDATA[%s]]></MediaId></Voice></xml>"
-	replyVideo              = "<xml>%s<MsgType><![CDATA[video]]></MsgType><Video><MediaId><![CDATA[%s]]></MediaId><Title><![CDATA[%s]]></Title><Description><![CDATA[%s]]></Description></Video></xml>"
-	replyMusic              = "<xml>%s<MsgType><![CDATA[music]]></MsgType><Music><Title><![CDATA[%s]]></Title><Description><![CDATA[%s]]></Description><MusicUrl><![CDATA[%s]]></MusicUrl><HQMusicUrl><![CDATA[%s]]></HQMusicUrl><ThumbMediaId><![CDATA[%s]]></ThumbMediaId></Music></xml>"
-	replyNews               = "<xml>%s<MsgType><![CDATA[news]]></MsgType><ArticleCount>%d</ArticleCount><Articles>%s</Articles></xml>"
-	replyHeader             = "<ToUserName><![CDATA[%s]]></ToUserName><FromUserName><![CDATA[%s]]></FromUserName><CreateTime>%d</CreateTime>"
-	replyArticle            = "<item><Title><![CDATA[%s]]></Title> <Description><![CDATA[%s]]></Description><PicUrl><![CDATA[%s]]></PicUrl><Url><![CDATA[%s]]></Url></item>"
-	transferCustomerService = "<xml>" + replyHeader + "<MsgType><![CDATA[transfer_customer_service]]></MsgType></xml>"
-	reply2CustomerService   = "<xml>%s<MsgType><![CDATA[transfer_customer_service]]></MsgType></xml>"
-
+	replyText                = "<xml>%s<MsgType><![CDATA[text]]></MsgType><Content><![CDATA[%s]]></Content></xml>"
+	replyImage               = "<xml>%s<MsgType><![CDATA[image]]></MsgType><Image><MediaId><![CDATA[%s]]></MediaId></Image></xml>"
+	replyVoice               = "<xml>%s<MsgType><![CDATA[voice]]></MsgType><Voice><MediaId><![CDATA[%s]]></MediaId></Voice></xml>"
+	replyVideo               = "<xml>%s<MsgType><![CDATA[video]]></MsgType><Video><MediaId><![CDATA[%s]]></MediaId><Title><![CDATA[%s]]></Title><Description><![CDATA[%s]]></Description></Video></xml>"
+	replyMusic               = "<xml>%s<MsgType><![CDATA[music]]></MsgType><Music><Title><![CDATA[%s]]></Title><Description><![CDATA[%s]]></Description><MusicUrl><![CDATA[%s]]></MusicUrl><HQMusicUrl><![CDATA[%s]]></HQMusicUrl><ThumbMediaId><![CDATA[%s]]></ThumbMediaId></Music></xml>"
+	replyNews                = "<xml>%s<MsgType><![CDATA[news]]></MsgType><ArticleCount>%d</ArticleCount><Articles>%s</Articles></xml>"
+	replyHeader              = "<ToUserName><![CDATA[%s]]></ToUserName><FromUserName><![CDATA[%s]]></FromUserName><CreateTime>%d</CreateTime>"
+	replyArticle             = "<item><Title><![CDATA[%s]]></Title> <Description><![CDATA[%s]]></Description><PicUrl><![CDATA[%s]]></PicUrl><Url><![CDATA[%s]]></Url></item>"
+	transferCustomerService  = "<xml>" + replyHeader + "<MsgType><![CDATA[transfer_customer_service]]></MsgType></xml>"
+	transferCustomerService2 = "<xml>" + replyHeader + "<MsgType><![CDATA[transfer_customer_service]]></MsgType><TransInfo><KfAccount><![CDATA[%s]]></KfAccount></TransInfo></xml>"
+	reply2CustomerService    = "<xml>%s<MsgType><![CDATA[transfer_customer_service]]></MsgType></xml>"
+	// Material request
+	requestMaterial = `{"type":"%s","offset":%d,"count":%d}`
 	// QR scene request
 	// QR scene request
 	requestQRScene      = `{"expire_seconds":%d,"action_name":"QR_SCENE","action_info":{"scene":{"scene_id":%d}}}`
 	requestQRScene      = `{"expire_seconds":%d,"action_name":"QR_SCENE","action_info":{"scene":{"scene_id":%d}}}`
 	requestQRLimitScene = `{"action_name":"QR_LIMIT_SCENE","action_info":{"scene":{"scene_id":%d}}}`
 	requestQRLimitScene = `{"action_name":"QR_LIMIT_SCENE","action_info":{"scene":{"scene_id":%d}}}`
@@ -200,6 +203,38 @@ type UserInfo struct {
 	GroupId       int    `json:"groupid,omitempty"`
 	GroupId       int    `json:"groupid,omitempty"`
 }
 }
 
 
+type Material struct {
+	MediaId    string `json:"media_id,omitempty"`
+	Name       string `json:"name,omitempty"`
+	UpdateTime int64  `json:"update_time,omitempty"`
+	CreateTime int64  `json:"create_time,omitempty"`
+	Url        string `json:"url,omitempty"`
+	Content    struct {
+		NewsItem []struct {
+			Title            string `json:"title,omitempty"`
+			ThumbMediaId     string `json:"thumb_media_id,omitempty"`
+			ShowCoverPic     int    `json:"show_cover_pic,omitempty"`
+			Author           string `json:"author,omitempty"`
+			Digest           string `json:"digest,omitempty"`
+			Content          string `json:"content,omitempty"`
+			Url              string `json:"url,omitempty"`
+			ContentSourceUrl string `json:"content_source_url,omitempty"`
+		} `json:"news_item,omitempty"`
+	} `json:"content,omitempty"`
+}
+
+type Materials struct {
+	TotalCount int        `json:"total_count,omitempty"`
+	ItemCount  int        `json:"item_count,omitempty"`
+	Items      []Material `json:"item,omitempty"`
+}
+
+type TmplData map[string]TmplItem
+type TmplItem struct {
+	Value string `json:"value,omitempty"`
+	Color string `json:"color,omitempty"`
+}
+
 // Use to output reply
 // Use to output reply
 type ResponseWriter interface {
 type ResponseWriter interface {
 	// Get weixin
 	// Get weixin
@@ -208,12 +243,14 @@ type ResponseWriter interface {
 	// Reply message
 	// Reply message
 	ReplyOK()
 	ReplyOK()
 	ReplyText(text string)
 	ReplyText(text string)
-	Reply2CustomerService()
 	ReplyImage(mediaId string)
 	ReplyImage(mediaId string)
 	ReplyVoice(mediaId string)
 	ReplyVoice(mediaId string)
 	ReplyVideo(mediaId string, title string, description string)
 	ReplyVideo(mediaId string, title string, description string)
 	ReplyMusic(music *Music)
 	ReplyMusic(music *Music)
 	ReplyNews(articles []Article)
 	ReplyNews(articles []Article)
+
+	Reply2CustomerService()
+
 	TransferCustomerService(serviceId string)
 	TransferCustomerService(serviceId string)
 	// Post message
 	// Post message
 	PostText(text string) error
 	PostText(text string) error
@@ -222,12 +259,14 @@ type ResponseWriter interface {
 	PostVideo(mediaId string, title string, description string) error
 	PostVideo(mediaId string, title string, description string) error
 	PostMusic(music *Music) error
 	PostMusic(music *Music) error
 	PostNews(articles []Article) error
 	PostNews(articles []Article) error
-	PostTemplateMessage(templateid string, url string, data interface{}) (string, error)
+	PostTemplateMessage(templateid string, url string, data TmplData) (int32, error)
 	// Media operator
 	// Media operator
 	UploadMediaFromFile(mediaType string, filepath string) (string, error)
 	UploadMediaFromFile(mediaType string, filepath string) (string, error)
 	DownloadMediaToFile(mediaId string, filepath string) error
 	DownloadMediaToFile(mediaId string, filepath string) error
 	UploadMedia(mediaType string, filename string, reader io.Reader) (string, error)
 	UploadMedia(mediaType string, filename string, reader io.Reader) (string, error)
 	DownloadMedia(mediaId string, writer io.Writer) error
 	DownloadMedia(mediaId string, writer io.Writer) error
+	//
+	TransferCustomerService2(serviceId, kfaccount string)
 	GetUserBaseInfo(openid string) (map[string]interface{}, error)
 	GetUserBaseInfo(openid string) (map[string]interface{}, error)
 }
 }
 
 
@@ -262,17 +301,16 @@ type jsApiTicket struct {
 }
 }
 
 
 type Weixin struct {
 type Weixin struct {
-	token      string
-	routes     []*route
-	tokenChan  chan accessToken
-	ticketChan chan jsApiTicket
-	userData   interface{}
-	appId      string
-	appSecret  string
+	token        string
+	routes       []*route
+	tokenChan    chan accessToken
+	ticketChan   chan jsApiTicket
+	userData     interface{}
+	appId        string
+	appSecret    string
+	refreshToken int32
 }
 }
 
 
-//
-
 // Convert qr scene to url
 // Convert qr scene to url
 func (qr *QRScene) ToURL() string {
 func (qr *QRScene) ToURL() string {
 	return (weixinShowQRScene + "?ticket=" + qr.Ticket)
 	return (weixinShowQRScene + "?ticket=" + qr.Ticket)
@@ -284,9 +322,10 @@ func New(token string, appid string, secret string) *Weixin {
 	wx.token = token
 	wx.token = token
 	wx.appId = appid
 	wx.appId = appid
 	wx.appSecret = secret
 	wx.appSecret = secret
+	wx.refreshToken = 0
 	if len(appid) > 0 && len(secret) > 0 {
 	if len(appid) > 0 && len(secret) > 0 {
 		wx.tokenChan = make(chan accessToken)
 		wx.tokenChan = make(chan accessToken)
-		go createAccessToken(wx.tokenChan, appid, secret)
+		go wx.createAccessToken(wx.tokenChan, appid, secret)
 		wx.ticketChan = make(chan jsApiTicket)
 		wx.ticketChan = make(chan jsApiTicket)
 		go createJsApiTicket(wx.tokenChan, wx.ticketChan)
 		go createJsApiTicket(wx.tokenChan, wx.ticketChan)
 	}
 	}
@@ -307,6 +346,11 @@ func (wx *Weixin) GetAppSecret() string {
 	return wx.appSecret
 	return wx.appSecret
 }
 }
 
 
+func (wx *Weixin) RefreshAccessToken() {
+	atomic.StoreInt32(&wx.refreshToken, 1)
+	<-wx.tokenChan
+}
+
 // Register request callback.
 // Register request callback.
 func (wx *Weixin) HandleFunc(pattern string, handler HandlerFunc) {
 func (wx *Weixin) HandleFunc(pattern string, handler HandlerFunc) {
 	regex, err := regexp.Compile(pattern)
 	regex, err := regexp.Compile(pattern)
@@ -333,28 +377,6 @@ func (wx *Weixin) PostText(touser string, text string) error {
 	return postMessage(wx.tokenChan, &msg)
 	return postMessage(wx.tokenChan, &msg)
 }
 }
 
 
-//Post custom message(消息结构体完全由开发人员自定义)
-func (wx *Weixin) PostTextCustom(url string, obj interface{}) error {
-	data, err := json.Marshal(obj)
-	if err != nil {
-		return err
-	}
-	//log.Println("custom msg:", string(data))
-	_, err = postRequest(url, wx.tokenChan, data)
-	return err
-}
-
-//Post custom message(消息结构体完全由开发人员自定义)
-func (wx *Weixin) PostCustomMsg(url string, obj interface{}) (bs []byte, err error) {
-	var data []byte
-	data, err = json.Marshal(obj)
-	if err != nil {
-		return
-	}
-	bs, err = postRequest(url, wx.tokenChan, data)
-	return
-}
-
 // Post image message
 // Post image message
 func (wx *Weixin) PostImage(touser string, mediaId string) error {
 func (wx *Weixin) PostImage(touser string, mediaId string) error {
 	var msg struct {
 	var msg struct {
@@ -462,6 +484,20 @@ func (wx *Weixin) DownloadMedia(mediaId string, writer io.Writer) error {
 	return downloadMedia(wx.tokenChan, mediaId, writer)
 	return downloadMedia(wx.tokenChan, mediaId, writer)
 }
 }
 
 
+// Batch Get Material
+func (wx *Weixin) BatchGetMaterial(materialType string, offset int, count int) (*Materials, error) {
+	reply, err := postRequest(weixinMaterialURL+"/batchget_material?access_token=", wx.tokenChan,
+		[]byte(fmt.Sprintf(requestMaterial, materialType, offset, count)))
+	if err != nil {
+		return nil, err
+	}
+	var materials Materials
+	if err := json.Unmarshal(reply, &materials); err != nil {
+		return nil, err
+	}
+	return &materials, nil
+}
+
 // Get ip list
 // Get ip list
 func (wx *Weixin) GetIpList() ([]string, error) {
 func (wx *Weixin) GetIpList() ([]string, error) {
 	reply, err := sendGetRequest(weixinHost+"/getcallbackip?access_token=", wx.tokenChan)
 	reply, err := sendGetRequest(weixinHost+"/getcallbackip?access_token=", wx.tokenChan)
@@ -530,16 +566,12 @@ func (wx *Weixin) ShortURL(url string) (string, error) {
 
 
 // Custom menu
 // Custom menu
 func (wx *Weixin) CreateMenu(menu *Menu) error {
 func (wx *Weixin) CreateMenu(menu *Menu) error {
-	data, err := json.Marshal(menu)
+	data, err := marshal(menu)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
-	} else {
-		tmp := string(data)
-		tmp = strings.Replace(tmp, "\\u0026", "&", -1)
-		log.Println("MenuJson", string(tmp))
-		_, err := postRequest(weixinHost+"/menu/create?access_token=", wx.tokenChan, []byte(tmp))
-		return err
 	}
 	}
+	_, err = postRequest(weixinHost+"/menu/create?access_token=", wx.tokenChan, data)
+	return err
 }
 }
 
 
 func (wx *Weixin) GetMenu() (*Menu, error) {
 func (wx *Weixin) GetMenu() (*Menu, error) {
@@ -599,12 +631,12 @@ func (wx *Weixin) AddTemplate(shortid string) (string, error) {
 	return templateId.Id, nil
 	return templateId.Id, nil
 }
 }
 
 
-func (wx *Weixin) PostTemplateMessage(touser string, templateid string, url string, data interface{}) (string, error) {
+func (wx *Weixin) PostTemplateMessage(touser string, templateid string, url string, data TmplData) (int32, error) {
 	var msg struct {
 	var msg struct {
-		ToUser     string      `json:"touser"`
-		TemplateId string      `json:"template_id"`
-		Url        string      `json:"url,omitempty"`
-		Data       interface{} `json:"data,omitempty"`
+		ToUser     string   `json:"touser"`
+		TemplateId string   `json:"template_id"`
+		Url        string   `json:"url,omitempty"`
+		Data       TmplData `json:"data,omitempty"`
 	}
 	}
 	msg.ToUser = touser
 	msg.ToUser = touser
 	msg.TemplateId = templateid
 	msg.TemplateId = templateid
@@ -612,17 +644,17 @@ func (wx *Weixin) PostTemplateMessage(touser string, templateid string, url stri
 	msg.Data = data
 	msg.Data = data
 	msgStr, err := marshal(msg)
 	msgStr, err := marshal(msg)
 	if err != nil {
 	if err != nil {
-		return "", err
+		return 0, err
 	}
 	}
 	reply, err := postRequest(weixinHost+"/message/template/send?access_token=", wx.tokenChan, msgStr)
 	reply, err := postRequest(weixinHost+"/message/template/send?access_token=", wx.tokenChan, msgStr)
 	if err != nil {
 	if err != nil {
-		return "", err
+		return 0, err
 	}
 	}
 	var resp struct {
 	var resp struct {
-		MsgId string `json:"msgid,omitempty"`
+		MsgId int32 `json:"msgid,omitempty"`
 	}
 	}
 	if err := json.Unmarshal(reply, &resp); err != nil {
 	if err := json.Unmarshal(reply, &resp); err != nil {
-		return "", err
+		return 0, err
 	}
 	}
 	return resp.MsgId, nil
 	return resp.MsgId, nil
 }
 }
@@ -708,7 +740,6 @@ func (wx *Weixin) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 		log.Println("Weixin receive message failed:", err)
 		log.Println("Weixin receive message failed:", err)
 		http.Error(w, "", http.StatusBadRequest)
 		http.Error(w, "", http.StatusBadRequest)
 	} else {
 	} else {
-		//log.Println("recive data:", string(data))
 		var msg Request
 		var msg Request
 		if err := xml.Unmarshal(data, &msg); err != nil {
 		if err := xml.Unmarshal(data, &msg); err != nil {
 			log.Println("Weixin parse message failed:", err)
 			log.Println("Weixin parse message failed:", err)
@@ -764,7 +795,6 @@ func checkSignature(t string, w http.ResponseWriter, r *http.Request) bool {
 	}
 	}
 	h := sha1.New()
 	h := sha1.New()
 	h.Write([]byte(str))
 	h.Write([]byte(str))
-	//fmt.Printf("checksignature:%s %s \n %s %s \n", str, timestamp, signature, nonce)
 	return fmt.Sprintf("%x", h.Sum(nil)) == signature
 	return fmt.Sprintf("%x", h.Sum(nil)) == signature
 }
 }
 
 
@@ -812,11 +842,12 @@ func getJsApiTicket(c chan accessToken) (*jsApiTicket, error) {
 
 
 }
 }
 
 
-func createAccessToken(c chan accessToken, appid string, secret string) {
+func (wx *Weixin) createAccessToken(c chan accessToken, appid string, secret string) {
 	token := accessToken{"", time.Now()}
 	token := accessToken{"", time.Now()}
 	c <- token
 	c <- token
 	for {
 	for {
-		if time.Since(token.expires).Seconds() >= 0 {
+		swapped := atomic.CompareAndSwapInt32(&wx.refreshToken, 1, 0)
+		if swapped || time.Since(token.expires).Seconds() >= 0 {
 			var expires time.Duration
 			var expires time.Duration
 			token.token, expires = authAccessToken(appid, secret)
 			token.token, expires = authAccessToken(appid, secret)
 			token.expires = time.Now().Add(expires)
 			token.expires = time.Now().Add(expires)
@@ -843,7 +874,6 @@ func sendGetRequest(reqURL string, c chan accessToken) ([]byte, error) {
 	for i := 0; i < retryMaxN; i++ {
 	for i := 0; i < retryMaxN; i++ {
 		token := <-c
 		token := <-c
 		if time.Since(token.expires).Seconds() < 0 {
 		if time.Since(token.expires).Seconds() < 0 {
-			log.Println("token:", token.token)
 			r, err := http.Get(reqURL + token.token)
 			r, err := http.Get(reqURL + token.token)
 			if err != nil {
 			if err != nil {
 				return nil, err
 				return nil, err
@@ -856,15 +886,14 @@ func sendGetRequest(reqURL string, c chan accessToken) ([]byte, error) {
 			var result response
 			var result response
 			if err := json.Unmarshal(reply, &result); err != nil {
 			if err := json.Unmarshal(reply, &result); err != nil {
 				return nil, err
 				return nil, err
-			} else {
-				switch result.ErrorCode {
-				case 0:
-					return reply, nil
-				case 42001: // access_token timeout and retry
-					continue
-				default:
-					return nil, errors.New(fmt.Sprintf("WeiXin send get request reply[%d]: %s", result.ErrorCode, result.ErrorMessage))
-				}
+			}
+			switch result.ErrorCode {
+			case 0:
+				return reply, nil
+			case 42001: // access_token timeout and retry
+				continue
+			default:
+				return nil, errors.New(fmt.Sprintf("WeiXin send get request reply[%d]: %s", result.ErrorCode, result.ErrorMessage))
 			}
 			}
 		}
 		}
 	}
 	}
@@ -877,29 +906,24 @@ func postRequest(reqURL string, c chan accessToken, data []byte) ([]byte, error)
 		if time.Since(token.expires).Seconds() < 0 {
 		if time.Since(token.expires).Seconds() < 0 {
 			r, err := http.Post(reqURL+token.token, "application/json; charset=utf-8", bytes.NewReader(data))
 			r, err := http.Post(reqURL+token.token, "application/json; charset=utf-8", bytes.NewReader(data))
 			if err != nil {
 			if err != nil {
-				log.Println("err1", err.Error())
 				return nil, err
 				return nil, err
 			}
 			}
 			defer r.Body.Close()
 			defer r.Body.Close()
 			reply, err := ioutil.ReadAll(r.Body)
 			reply, err := ioutil.ReadAll(r.Body)
-			log.Println("repl:", string(reply))
 			if err != nil {
 			if err != nil {
-				log.Println("err2", err.Error())
 				return nil, err
 				return nil, err
 			}
 			}
 			var result response
 			var result response
 			if err := json.Unmarshal(reply, &result); err != nil {
 			if err := json.Unmarshal(reply, &result); err != nil {
-				log.Println("err3", err.Error())
 				return nil, err
 				return nil, err
-			} else {
-				switch result.ErrorCode {
-				case 0:
-					return reply, nil
-				case 42001: // access_token timeout and retry
-					continue
-				default:
-					return nil, errors.New(fmt.Sprintf("WeiXin send post request reply[%d]: %s", result.ErrorCode, result.ErrorMessage))
-				}
+			}
+			switch result.ErrorCode {
+			case 0:
+				return reply, nil
+			case 42001: // access_token timeout and retry
+				continue
+			default:
+				return nil, errors.New(fmt.Sprintf("WeiXin send post request reply[%d]: %s", result.ErrorCode, result.ErrorMessage))
 			}
 			}
 		}
 		}
 	}
 	}
@@ -907,7 +931,7 @@ func postRequest(reqURL string, c chan accessToken, data []byte) ([]byte, error)
 }
 }
 
 
 func postMessage(c chan accessToken, msg interface{}) error {
 func postMessage(c chan accessToken, msg interface{}) error {
-	data, err := json.Marshal(msg)
+	data, err := marshal(msg)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
@@ -949,15 +973,14 @@ func uploadMedia(c chan accessToken, mediaType string, filename string, reader i
 			err = json.Unmarshal(reply, &result)
 			err = json.Unmarshal(reply, &result)
 			if err != nil {
 			if err != nil {
 				return "", err
 				return "", err
-			} else {
-				switch result.ErrorCode {
-				case 0:
-					return result.MediaId, nil
-				case 42001: // access_token timeout and retry
-					continue
-				default:
-					return "", errors.New(fmt.Sprintf("WeiXin upload[%d]: %s", result.ErrorCode, result.ErrorMessage))
-				}
+			}
+			switch result.ErrorCode {
+			case 0:
+				return result.MediaId, nil
+			case 42001: // access_token timeout and retry
+				continue
+			default:
+				return "", errors.New(fmt.Sprintf("WeiXin upload[%d]: %s", result.ErrorCode, result.ErrorMessage))
 			}
 			}
 		}
 		}
 	}
 	}
@@ -1024,11 +1047,6 @@ func (w responseWriter) ReplyText(text string) {
 	msg := fmt.Sprintf(replyText, w.replyHeader(), text)
 	msg := fmt.Sprintf(replyText, w.replyHeader(), text)
 	w.writer.Write([]byte(msg))
 	w.writer.Write([]byte(msg))
 }
 }
-func (w responseWriter) Reply2CustomerService() {
-	msg := fmt.Sprintf(reply2CustomerService, w.replyHeader(), "")
-	//log.Println("repl msg:", msg)
-	w.writer.Write([]byte(msg))
-}
 
 
 // Reply image message
 // Reply image message
 func (w responseWriter) ReplyImage(mediaId string) {
 func (w responseWriter) ReplyImage(mediaId string) {
@@ -1070,6 +1088,12 @@ func (w responseWriter) TransferCustomerService(serviceId string) {
 	w.writer.Write([]byte(msg))
 	w.writer.Write([]byte(msg))
 }
 }
 
 
+func (w responseWriter) TransferCustomerService2(serviceId, kfaccount string) {
+	msg := fmt.Sprintf(transferCustomerService2, serviceId, w.fromUserName, time.Now().Unix(), kfaccount)
+	log.Println("send custom msg:::", msg)
+	w.writer.Write([]byte(msg))
+}
+
 // Post text message
 // Post text message
 func (w responseWriter) PostText(text string) error {
 func (w responseWriter) PostText(text string) error {
 	return w.wx.PostText(w.toUserName, text)
 	return w.wx.PostText(w.toUserName, text)
@@ -1101,7 +1125,7 @@ func (w responseWriter) PostNews(articles []Article) error {
 }
 }
 
 
 // Post template message
 // Post template message
-func (w responseWriter) PostTemplateMessage(templateid string, url string, data interface{}) (string, error) {
+func (w responseWriter) PostTemplateMessage(templateid string, url string, data TmplData) (int32, error) {
 	return w.wx.PostTemplateMessage(w.toUserName, templateid, url, data)
 	return w.wx.PostTemplateMessage(w.toUserName, templateid, url, data)
 }
 }
 
 
@@ -1125,6 +1149,37 @@ func (w responseWriter) DownloadMedia(mediaId string, writer io.Writer) error {
 	return w.wx.DownloadMedia(mediaId, writer)
 	return w.wx.DownloadMedia(mediaId, writer)
 }
 }
 
 
+//
+func (wx *Weixin) PostCustomMsg(url string, obj interface{}) (bs []byte, err error) {
+	var data []byte
+	data, err = json.Marshal(obj)
+	if err != nil {
+		return
+	}
+	bs, err = postRequest(url, wx.tokenChan, data)
+	return
+}
+
+//
+func (wx *Weixin) GetKfList() (ret map[string]interface{}) {
+	bs, err := sendGetRequest("https://api.weixin.qq.com/cgi-bin/customservice/getkflist?access_token=", wx.tokenChan)
+	if err != nil {
+		return nil
+	}
+	json.Unmarshal(bs, &ret)
+	return
+}
+
+//
+func (wx *Weixin) GetOnlineKfList() (ret map[string]interface{}) {
+	bs, err := sendGetRequest("https://api.weixin.qq.com/cgi-bin/customservice/getonlinekflist?access_token=", wx.tokenChan)
+	if err != nil {
+		return nil
+	}
+	json.Unmarshal(bs, &ret)
+	return
+}
+
 func (rw responseWriter) GetUserBaseInfo(openid string) (map[string]interface{}, error) {
 func (rw responseWriter) GetUserBaseInfo(openid string) (map[string]interface{}, error) {
 	return rw.wx.GetUserBaseInfo(openid)
 	return rw.wx.GetUserBaseInfo(openid)
 }
 }
@@ -1142,3 +1197,20 @@ func (w *Weixin) GetUserBaseInfo(openid string) (map[string]interface{}, error)
 		return nil, err
 		return nil, err
 	}
 	}
 }
 }
+
+func (w responseWriter) Reply2CustomerService() {
+	msg := fmt.Sprintf(reply2CustomerService, w.replyHeader(), "")
+	//log.Println("repl msg:", msg)
+	w.writer.Write([]byte(msg))
+}
+
+//Post custom message(消息结构体完全由开发人员自定义)
+func (wx *Weixin) PostTextCustom(url string, obj interface{}) error {
+	data, err := json.Marshal(obj)
+	if err != nil {
+		return err
+	}
+	//log.Println("custom msg:", string(data))
+	_, err = postRequest(url, wx.tokenChan, data)
+	return err
+}

+ 26 - 0
weixin/src/qfw/weixin/wxcustomer_test.go

@@ -0,0 +1,26 @@
+package weixin
+
+import (
+	"log"
+	"sort"
+	"testing"
+)
+
+func Test_1(t *testing.T) {
+	A := arr{}
+	m1 := map[string]interface{}{
+		"accepted_case": 2,
+		"kf_account":    "as@123",
+	}
+	m2 := map[string]interface{}{
+		"accepted_case": 6,
+		"kf_account":    "as@444",
+	}
+	m3 := map[string]interface{}{
+		"accepted_case": 16,
+		"kf_account":    "a1111s@4ee",
+	}
+	A = append(A, m2, m1, m3)
+	sort.Sort(&A)
+	log.Println(A[0]["kf_account"])
+}

+ 2 - 0
weixin/src/qfw/weixinconfig/weixinconfig.go

@@ -39,6 +39,8 @@ type wxconfig struct {
 	Rpcserver             string                 `json:"rpcserver"`
 	Rpcserver             string                 `json:"rpcserver"`
 	Msgserver             string                 `json:"msgserver"`
 	Msgserver             string                 `json:"msgserver"`
 	Qmxcdn                string                 `json:"qmxcdn"`
 	Qmxcdn                string                 `json:"qmxcdn"`
+	Kfprefix              string                 `json:"kfprefix"`
+	Kfport                string                 `json:"kfport"`
 }
 }
 
 
 //系统配置
 //系统配置