Browse Source

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

合并
wanghuidong 8 years ago
parent
commit
c1ee4170cd

+ 14 - 0
common/src/github.com/PuerkitoBio/goquery/property.go

@@ -99,6 +99,20 @@ func (s *Selection) Html() (ret string, e error) {
 	return
 }
 
+func (s *Selection) OuterHtml() (ret string, e error) {
+	// Since there is no .innerHtml, the HTML content must be re-created from
+	// the nodes using html.Render.
+	var buf bytes.Buffer
+	if len(s.Nodes) > 0 {
+		e = html.Render(&buf, s.Nodes[0])
+		if e != nil {
+			return
+		}
+		ret = buf.String()
+	}
+	return
+}
+
 // AddClass adds the given class(es) to each element in the set of matched elements.
 // Multiple class names can be specified, separated by a space or via multiple arguments.
 func (s *Selection) AddClass(class ...string) *Selection {

+ 5 - 1
common/src/github.com/go-xweb/xweb/action.go

@@ -82,7 +82,11 @@ func (c *Action) Url() string {
 
 // Site returns base site url as scheme://domain type.
 func (c *Action) Site() string {
-	return c.Scheme() + "://" + c.Domain()
+	schecm, _ := c.App.GetConfig("schecm").(string)
+	if schecm == "" {
+		schecm = c.Scheme()
+	}
+	return schecm + "://" + c.Domain()
 }
 
 // Scheme returns request scheme as "http" or "https".

+ 57 - 4
common/src/github.com/go-xweb/xweb/server.go

@@ -57,11 +57,11 @@ func NewServer(args ...string) *Server {
 		ServerNumber++
 	}
 	s := &Server{
-		Config:  Config,
-		Env:     map[string]interface{}{},
-		Apps:    map[string]*App{},
+		Config:       Config,
+		Env:          map[string]interface{}{},
+		Apps:         map[string]*App{},
 		AppsNamePath: map[string]string{},
-		Name:    name,
+		Name:         name,
 	}
 	Servers[s.Name] = s
 
@@ -171,6 +171,59 @@ func (s *Server) Process(w http.ResponseWriter, req *http.Request) {
 }
 
 // Run starts the web application and serves HTTP requests for s
+func (s *Server) RunBase(addr string, mux *http.ServeMux) {
+	addrs := strings.Split(addr, ":")
+	s.Config.Addr = addrs[0]
+	s.Config.Port, _ = strconv.Atoi(addrs[1])
+
+	s.initServer()
+
+	//mux := http.NewServeMux()
+	if s.Config.Profiler {
+		mux.Handle("/debug/pprof", http.HandlerFunc(pprof.Index))
+		mux.Handle("/debug/pprof/heap", pprof.Handler("heap"))
+		mux.Handle("/debug/pprof/block", pprof.Handler("block"))
+		mux.Handle("/debug/pprof/goroutine", pprof.Handler("goroutine"))
+		mux.Handle("/debug/pprof/threadcreate", pprof.Handler("threadcreate"))
+
+		mux.Handle("/debug/pprof/cmdline", http.HandlerFunc(pprof.Cmdline))
+		mux.Handle("/debug/pprof/profile", http.HandlerFunc(pprof.Profile))
+		mux.Handle("/debug/pprof/symbol", http.HandlerFunc(pprof.Symbol))
+
+		mux.Handle("/debug/pprof/startcpuprof", http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
+			StartCPUProfile()
+		}))
+		mux.Handle("/debug/pprof/stopcpuprof", http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
+			StopCPUProfile()
+		}))
+		mux.Handle("/debug/pprof/memprof", http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
+			runtime.GC()
+			runtimePprof.WriteHeapProfile(rw)
+		}))
+		mux.Handle("/debug/pprof/gc", http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
+			PrintGCSummary(rw)
+		}))
+
+	}
+
+	if c, err := XHook.Call("MuxHandle", mux); err == nil {
+		if ret := XHook.Value(c, 0); ret != nil {
+			mux = ret.(*http.ServeMux)
+		}
+	}
+	mux.Handle("/", s)
+
+	s.Logger.Infof("http server is listening %s", addr)
+
+	l, err := net.Listen("tcp", addr)
+	if err != nil {
+		s.Logger.Error("ListenAndServe:", err)
+	}
+	s.l = l
+	err = http.Serve(s.l, mux)
+	s.l.Close()
+}
+
 func (s *Server) Run(addr string) {
 	addrs := strings.Split(addr, ":")
 	s.Config.Addr = addrs[0]

+ 3 - 0
common/src/github.com/go-xweb/xweb/xweb.go

@@ -84,6 +84,9 @@ func Process(c http.ResponseWriter, req *http.Request) {
 func Run(addr string) {
 	mainServer.Run(addr)
 }
+func RunBase(addr string, mux *http.ServeMux) {
+	mainServer.RunBase(addr, mux)
+}
 
 func SimpleTLSConfig(certFile, keyFile string) (*tls.Config, error) {
 	config := &tls.Config{}

+ 24 - 0
common/src/github.com/xlstudio/wxbizdatacrypt/.gitignore

@@ -0,0 +1,24 @@
+# Compiled Object files, Static and Dynamic libs (Shared Objects)
+*.o
+*.a
+*.so
+
+# Folders
+_obj
+_test
+
+# Architecture specific extensions/prefixes
+*.[568vq]
+[568vq].out
+
+*.cgo1.go
+*.cgo2.c
+_cgo_defun.c
+_cgo_gotypes.go
+_cgo_export.*
+
+_testmain.go
+
+*.exe
+*.test
+*.prof

+ 201 - 0
common/src/github.com/xlstudio/wxbizdatacrypt/LICENSE

@@ -0,0 +1,201 @@
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "{}"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright {yyyy} {name of copyright owner}
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.

+ 42 - 0
common/src/github.com/xlstudio/wxbizdatacrypt/README.md

@@ -0,0 +1,42 @@
+###微信小程序加密数据解密算法Go版
+
+**微信小程序官方说明文档签名加密篇链接**
+> http://mp.weixin.qq.com/debug/wxadoc/dev/api/signature.html?t=20161107
+
+**使用方法**
+
+> go get github.com/xlstudio/wxbizdatacrypt
+
+**引入方法**
+```Go
+import (
+	"github.com/xlstudio/wxbizdatacrypt"
+)
+```
+**使用示例**
+
+```Go
+package main
+
+import (
+	"fmt"
+	"github.com/xlstudio/wxbizdatacrypt"
+)
+
+func main() {
+	appID := "wx4f4bc4dec97d474b"
+	sessionKey := "tiihtNczf5v6AKRyjwEUhQ=="
+	encryptedData := "CiyLU1Aw2KjvrjMdj8YKliAjtP4gsMZMQmRzooG2xrDcvSnxIMXFufNstNGTyaGS9uT5geRa0W4oTOb1WT7fJlAC+oNPdbB+3hVbJSRgv+4lGOETKUQz6OYStslQ142dNCuabNPGBzlooOmB231qMM85d2/fV6ChevvXvQP8Hkue1poOFtnEtpyxVLW1zAo6/1Xx1COxFvrc2d7UL/lmHInNlxuacJXwu0fjpXfz/YqYzBIBzD6WUfTIF9GRHpOn/Hz7saL8xz+W//FRAUid1OksQaQx4CMs8LOddcQhULW4ucetDf96JcR3g0gfRK4PC7E/r7Z6xNrXd2UIeorGj5Ef7b1pJAYB6Y5anaHqZ9J6nKEBvB4DnNLIVWSgARns/8wR2SiRS7MNACwTyrGvt9ts8p12PKFdlqYTopNHR1Vf7XjfhQlVsAJdNiKdYmYVoKlaRv85IfVunYzO0IKXsyl7JCUjCpoG20f0a04COwfneQAGGwd5oa+T8yO5hzuyDb/XcxxmK01EpqOyuxINew=="
+	iv := "r7BXXKkLb8qrSNn05n0qiA=="
+
+	pc := wxbizdatacrypt.WxBizDataCrypt{AppID: appID, SessionKey: sessionKey}
+	result, err := pc.Decrypt(encryptedData, iv, true) //第三个参数解释: 需要返回 JSON 数据类型时 使用 true, 需要返回 map 数据类型时 使用 false
+	if err != nil {
+		fmt.Println(err)
+	} else {
+		fmt.Println(result)
+	}
+}
+```
+
+

+ 96 - 0
common/src/github.com/xlstudio/wxbizdatacrypt/wxbizdatacrypt.go

@@ -0,0 +1,96 @@
+package wxbizdatacrypt
+
+import (
+	"crypto/aes"
+	"crypto/cipher"
+	"encoding/base64"
+	"encoding/json"
+	"errors"
+	"fmt"
+	"strings"
+)
+
+var errorCode = map[string]int{
+	"IllegalAesKey" : -41001,
+	"IllegalIv" :  -41002,
+	"IllegalBuffer" :  -41003,
+	"DecodeBase64Error" :  -41004,
+}
+
+// WxBizDataCrypt represents an active WxBizDataCrypt object
+type WxBizDataCrypt struct {
+	AppID      string
+	SessionKey string
+}
+
+type showError struct {
+	errorCode int
+	errorMsg error
+}
+
+func (e showError) Error() string {
+	return fmt.Sprintf("{code: %v, error: \"%v\"}", e.errorCode, e.errorMsg)
+}
+
+// Decrypt Weixin APP's AES Data
+// If isJSON is true, Decrypt return JSON type.
+// If isJSON is false, Decrypt return map type.
+func (wxCrypt *WxBizDataCrypt) Decrypt(encryptedData string, iv string, isJSON bool) (interface{}, error) {
+	if len(wxCrypt.SessionKey) != 24 {
+		return nil, showError{errorCode["IllegalAesKey"], errors.New("sessionKey length is error")}
+	}
+	aesKey, err := base64.StdEncoding.DecodeString(wxCrypt.SessionKey)
+	if err != nil {
+		return nil, showError{errorCode["DecodeBase64Error"], err}
+	}
+
+	if len(iv) != 24 {
+		return nil, showError{errorCode["IllegalIv"], errors.New("iv length is error")}
+	}
+	aesIV, err := base64.StdEncoding.DecodeString(iv)
+	if err != nil {
+		return nil, showError{errorCode["DecodeBase64Error"], err}
+	}
+
+	aesCipherText, err := base64.StdEncoding.DecodeString(encryptedData)
+	if err != nil {
+		return nil, showError{errorCode["DecodeBase64Error"], err}
+	}
+	aesPlantText := make([]byte, len(aesCipherText))
+
+	aesBlock, err := aes.NewCipher(aesKey)
+	if err != nil {
+		return nil, showError{errorCode["IllegalBuffer"], err}
+	}
+
+	mode := cipher.NewCBCDecrypter(aesBlock, aesIV)
+	mode.CryptBlocks(aesPlantText, aesCipherText)
+	aesPlantText = PKCS7UnPadding(aesPlantText)
+
+	var decrypted map[string]interface{}
+	aesPlantText = []byte(strings.Replace(string(aesPlantText), "\a", "", -1))
+	err = json.Unmarshal([]byte(aesPlantText), &decrypted)
+	if err != nil {
+		return nil, showError{errorCode["IllegalBuffer"], err}
+	}
+
+	if decrypted["watermark"].(map[string]interface{})["appid"] != wxCrypt.AppID {
+		return nil, showError{errorCode["IllegalBuffer"], errors.New("appId is not match")}
+	}
+
+	if isJSON == true {
+		return string(aesPlantText), nil
+	}
+
+	return decrypted, nil
+}
+
+// PKCS7UnPadding return unpadding []Byte plantText
+func PKCS7UnPadding(plantText []byte) []byte {
+	length := len(plantText)
+	unPadding := int(plantText[length-1])
+	if unPadding < 1 || unPadding > 32 {
+		unPadding = 0
+	}
+	return plantText[:(length - unPadding)]
+}

+ 57 - 1
common/src/qfw/util/common.go

@@ -8,6 +8,7 @@ import (
 	"fmt"
 	"io"
 	"log"
+	"math"
 	mathRand "math/rand"
 	"regexp"
 	"runtime"
@@ -23,6 +24,19 @@ const (
 	tmp = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ12345678900"
 )
 
+var SE = &SimpleEncrypt{Key: "topnet2015topnet2015"}
+
+//短地址加密
+func EncodeArticleId(keys ...string) string {
+	kstr := strings.Join(keys, ",")
+	return SE.EncodeString(kstr)
+}
+
+//短地址解密
+func DecodeArticleId(id string) []string {
+	return strings.Split(SE.DecodeString(id), ",")
+}
+
 func Uuid(length int) string {
 	ret := []string{}
 	for i := 0; i < length; i++ {
@@ -265,7 +279,8 @@ func ObjToString(old interface{}) string {
 	if nil == old {
 		return ""
 	} else {
-		return old.(string)
+		r, _ := old.(string)
+		return r
 	}
 }
 
@@ -427,6 +442,14 @@ func BsonIdToSId(uid interface{}) string {
 	}
 }
 
+func StringTOBsonId(id string) (bid bson.ObjectId) {
+	defer Catch()
+	if id != "" {
+		bid = bson.ObjectIdHex(id)
+	}
+	return
+}
+
 func GetSubDay(t1 int64) int {
 	tt1 := time.Unix(t1, 0)
 	tt2 := time.Now()
@@ -488,3 +511,36 @@ func WxSign(format string, param ...interface{}) string {
 	sign := strings.ToUpper(hex.EncodeToString(h.Sum(nil)))
 	return sign
 }
+
+//计算时差
+func TimeDiff(date time.Time) string {
+	var date1 = date                        //开始时间
+	var date2 = time.Now()                  //结束时间
+	var date3 = date2.Unix() - date1.Unix() //时间差的毫秒数
+	//计算出相差天数
+	var days = math.Floor(float64(date3 / (24 * 3600)))
+	//计算出小时数
+	var leave1 = date3 % (24 * 3600) //计算天数后剩余的毫秒数
+	var hours = math.Floor(float64(leave1 / (3600)))
+	//计算相差分钟数
+	var leave2 = leave1 % (3600) //计算小时数后剩余的毫秒数
+	var minutes = math.Floor(float64(leave2 / (60)))
+	//计算相差秒数
+	var td = "30秒前"
+	if days > 0 {
+		if days > 10 {
+			if date1.Year() < date2.Year() {
+				td = FormatDate(&date, Date_Short_Layout)
+			} else {
+				td = FormatDate(&date, Date_Small_Layout)
+			}
+		} else {
+			td = fmt.Sprint(days) + "天前"
+		}
+	} else if hours > 0 {
+		td = fmt.Sprint(hours) + "小时前"
+	} else if minutes > 0 {
+		td = fmt.Sprint(minutes) + "分钟前"
+	}
+	return td
+}

+ 100 - 1
common/src/qfw/util/elastic/elasticutil.go

@@ -913,12 +913,18 @@ const (
 	minq         = `{"multi_match": {"query": "%s","type": "phrase", "fields": [%s],"analyzer": "my_ngram"}}`
 	HL           = `"highlight": {"pre_tags": [""],"post_tags": [""],"fields": {%s}}`
 	highlightStr = `%s: {"fragment_size": %d,"number_of_fragments": 1}`
+
+	FilterQuery_New  = `{"query":{"bool":{"must": [%s%s%s],"should":[]}}}`
+	MatchQueryString = `{"match": {%s: { "query":"%s", "operator": "and"}}}`
+	HL_New           = `"highlight": {"pre_tags": ["<HL>"],"post_tags": ["<HL>"],"fields": {%s}}`
 )
 
 func GetNgramQuery(query interface{}, mustquery, findfields string) (qstr string) {
 	var words []string
 	if q, ok := query.(string); ok {
-		words = strings.Split(q, ",")
+		if q != "" {
+			words = strings.Split(q, ",")
+		}
 	} else if q, ok := query.([]string); ok {
 		words = q
 	} else if q, ok := query.([]interface{}); ok {
@@ -937,10 +943,70 @@ func GetNgramQuery(query interface{}, mustquery, findfields string) (qstr string
 		}
 		qstr = fmt.Sprintf(NgramStr, mustquery, strings.Join(musts, ","))
 		//log.Println("ngram-query", qstr)
+	} else {
+		qstr = fmt.Sprintf(NgramStr, mustquery, "")
 	}
 	return
 }
 
+func GetNgramQuery_New(querystring, querymust interface{}, must, findfields string) (qstring string) {
+	querymust_string := ""
+	var wordsMust []string
+	if q, ok := querymust.(string); ok {
+		if q != "" {
+			wordsMust = strings.Split(q, ",")
+		}
+	} else if q, ok := querymust.([]string); ok {
+		wordsMust = q
+	} else if q, ok := querymust.([]interface{}); ok {
+		wordsMust = util.ObjArrToStringArr(q)
+	}
+	if wordsMust != nil {
+		new_minq := fmt.Sprintf(minq, "%s", findfields)
+		musts := []string{}
+		for _, qs_wordsMust := range wordsMust {
+			qws := strings.Split(qs_wordsMust, "+")
+			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, ",")))
+		}
+		querymust_string = strings.Join(musts, ",")
+	}
+	//log.Println("must", must, querymust_string)
+
+	//querystring---------------------------------------------
+	query_string := ""
+	var querysShold []string
+	if q, ok := querystring.(string); ok {
+		if q != "" {
+			querysShold = strings.Split(q, ",")
+		}
+	} else if q, ok := querystring.([]string); ok {
+		querysShold = q
+	} else if q, ok := querystring.([]interface{}); ok {
+		querysShold = util.ObjArrToStringArr(q)
+	}
+	if querysShold != nil {
+		for k, name := range strings.Split(findfields, ",") {
+			for _, qs_querysShold := range querysShold {
+				if k > 0 {
+					query_string = query_string + "," + fmt.Sprintf(MatchQueryString, fmt.Sprint(name), qs_querysShold)
+				} else {
+					query_string = query_string + fmt.Sprintf(MatchQueryString, fmt.Sprint(name), qs_querysShold)
+				}
+			}
+		}
+	}
+	//log.Println("querystring", query_string)
+	if querymust_string == "" {
+		qstring = fmt.Sprintf(FilterQuery_New, must, query_string, querymust_string)
+	} else {
+		qstring = fmt.Sprintf(FilterQuery_New, must, query_string, ","+querymust_string)
+	}
+	return
+}
 func GetByNgram(index, itype string, query interface{}, mustquery, findfields, order, fields string, start, limit int) *[]map[string]interface{} {
 	return GetByNgramAll(index, itype, query, mustquery, findfields, order, fields, start, limit, false, false)
 }
@@ -1012,3 +1078,36 @@ func GetByNgramAll(index, itype string, query interface{}, mustquery, findfields
 		return nil
 	}
 }
+
+//增加高亮、过滤查询
+func GetByNgramAll_New(index, itype string, querystring, querymust interface{}, mustquery, findfields, order, fields string, start, limit int, highlight bool, filtermode bool) *[]map[string]interface{} {
+	defer util.Catch()
+	qstr := ""
+	if filtermode {
+		qstr = GetNgramQuery_New(querystring, querymust, mustquery, findfields)
+	} else {
+		qstr = GetNgramQuery_New(querystring, "", mustquery, findfields)
+	}
+	if qstr != "" {
+		if highlight {
+			ws := []string{}
+			for _, w := range strings.Split(findfields, ",") {
+				ws = append(ws, w+`:{"force_source": true}`)
+			}
+			qstr = qstr[:len(qstr)-1] + `,` + fmt.Sprintf(HL_New, strings.Join(ws, ",")) + `}`
+		}
+		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", order, qstr)
+		return Get(index, itype, qstr)
+	} else {
+		return nil
+	}
+}

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

@@ -33,6 +33,14 @@ func Test_save(t *testing.T) {
 	//log.Println("保存结果", b, DelById("test", "test", bid.String()))
 }
 
+func Test_GoQ(t *testing.T) {
+	InitElastic("http://192.168.3.18:9800")
+	//"$中不支持range查询"
+	//res := GetPage("bidding", "bidding", `{"$or":[{"TERM_toptype":"拟建"},{"TERM_toptype":"结果"}],"extracttype":{"$gt":1},"TERM_area": "广东","$and":[{"TERM_area": "广东"}{"TERM_area": "广东"}]}`, `{"id":-1}`, `"title","toptype"`, 0, 50)
+	res := GetPage("bidding", "bidding", `{"$or":[{"TERM_toptype":"拟建"},{"TERM_toptype":"结果"}]}`, `{"publishtime":-1}`, `"title","toptype","publishtime"`, 0, 50)
+	log.Println(res)
+}
+
 func Test_get(t *testing.T) {
 	InitElastic("http://192.168.3.14:9800")
 	a := make(chan int, 1)

File diff suppressed because it is too large
+ 1 - 0
common/src/qfw/util/encrypt_test.go


+ 11 - 0
common/src/qfw/util/fsw/fsw_test.go

@@ -12,3 +12,14 @@ func TestMatch(t *testing.T) {
 	ret2 := Repl("这是什么啊,怎么会有胡锦涛的名字,还有江泽民,我去,什么玩意")
 	log.Println(ret2)
 }
+
+func TestAdd(t *testing.T) {
+	tmp := NewFswScan()
+
+	tmp.AddWord("人民")
+	tmp.AddWord("共和国")
+	tmp.AddWords([]string{"解放军", "招标人"})
+	log.Println(tmp.FswDictionary)
+	f, ok := tmp.Filter("这个公司的招标人是XXX")
+	log.Println("识别结果", f, ok)
+}

+ 90 - 0
common/src/qfw/util/fsw/fswex.go

@@ -0,0 +1,90 @@
+package fsw
+
+//敏感词过滤,扩展,类似查字典
+type Fsw struct {
+	IsStart bool
+	IsEnd   bool
+	Char    string          //字符
+	Link    map[string]*Fsw //下级对象
+}
+
+//
+type FswScan struct {
+	FswDictionary map[string]*Fsw //
+}
+
+//
+func NewFswScan() *FswScan {
+	return &FswScan{
+		FswDictionary: make(map[string]*Fsw),
+	}
+}
+
+//追加
+func (fsw *FswScan) AddWord(word string) {
+	var cfsw *Fsw
+	rs := []rune(word)
+	for i, wv := range rs {
+		kc := string(wv)
+		if i == 0 { //初始化
+			if v, ok := fsw.FswDictionary[kc]; ok {
+				cfsw = v
+			} else {
+				cfsw = &Fsw{
+					Char:    kc,
+					IsStart: true,
+					Link:    make(map[string]*Fsw),
+				}
+				fsw.FswDictionary[kc] = cfsw
+			}
+			continue
+		}
+		//查询
+		if v, ok := (*cfsw).Link[kc]; ok {
+			cfsw = v
+		} else {
+			tmp := &Fsw{
+				Char:    kc,
+				IsStart: true,
+				Link:    make(map[string]*Fsw),
+			}
+			(*cfsw).Link[kc] = tmp
+			cfsw = tmp
+		}
+		//判断结束
+	}
+	(*cfsw).IsEnd = true
+}
+
+//
+func (fsw *FswScan) AddWords(words []string) {
+	for _, v := range words {
+		fsw.AddWord(v)
+	}
+}
+
+/*按字典查找
+匹配模式:最大匹配,最小匹配,
+目前是最小匹配
+*/
+func (fsw *FswScan) Filter(src string) (string, bool) {
+	rs := []rune(src)
+	for i := 0; i < len(rs); i++ {
+		char := string(rs[i])
+		if f, ok := fsw.FswDictionary[char]; ok { //某1个字匹配上了
+			for j := i + 1; j < len(rs); j++ {
+				char = string(rs[j])
+				if v, ok := f.Link[char]; ok {
+					f = v
+					if v.IsEnd { //找到了
+						return string(rs[i : j+1]), true
+					}
+				} else {
+					break
+				}
+			}
+		}
+
+	}
+	return "", false
+}

+ 71 - 0
common/src/qfw/util/mongodb/mongodbSim.go

@@ -1,6 +1,7 @@
 package mongodb
 
 import (
+	"errors"
 	"fmt"
 	"log"
 	"qfw/util"
@@ -77,6 +78,27 @@ func (m *MongodbSim) Count(c string, query interface{}) int {
 	return n
 }
 
+//统计
+func (m *MongodbSim) CountByErr(c string, query interface{}) (int, error) {
+	defer util.Catch()
+	sess := m.GetMgoConn()
+	//log.Println("count:", m.Size, m.MongodbAddr, m.DbName, sess, m.GetMgoConn(), m)
+	var n int = 0
+	if sess != nil {
+		defer m.DestoryMongoConn(sess)
+		coll := sess.DB(m.DbName).C(c)
+		var err error
+		n, err = coll.Find(ObjToM(query)).Count()
+		if nil != err {
+			return 0, err
+		} else {
+			return n, nil
+		}
+
+	}
+	return n, errors.New("no sess")
+}
+
 func (m *MongodbSim) Update(c string, query interface{}, set interface{}, upsert bool, multi bool) bool {
 	defer util.Catch()
 	sess := m.GetMgoConn()
@@ -102,6 +124,30 @@ func (m *MongodbSim) Update(c string, query interface{}, set interface{}, upsert
 	return b
 }
 
+func (m *MongodbSim) UpdateById(c string, id interface{}, set interface{}) bool {
+	defer util.Catch()
+	sess := m.GetMgoConn()
+	b := false
+	if sess != nil {
+		defer m.DestoryMongoConn(sess)
+		coll := sess.DB(m.DbName).C(c)
+		var q interface{}
+		if sid, ok := id.(string); ok {
+			q = M{"_id": util.StringTOBsonId(sid)}
+		} else {
+			q = M{"_id": id}
+		}
+		err := coll.Update(q, ObjToM(set))
+		if nil != err {
+			log.Println("UpdateByIdError", err)
+			b = false
+		} else {
+			b = true
+		}
+	}
+	return b
+}
+
 //批量更新
 func (m *MongodbSim) UpdateBulk(c string, doc ...[]map[string]interface{}) bool {
 	defer util.Catch()
@@ -127,6 +173,31 @@ func (m *MongodbSim) UpdateBulk(c string, doc ...[]map[string]interface{}) bool
 	return b
 }
 
+//批量更新
+func (m *MongodbSim) UpSertBulk(c string, doc ...[]map[string]interface{}) bool {
+	defer util.Catch()
+	sess := m.GetMgoConn()
+	b := true
+	if sess != nil {
+		defer m.DestoryMongoConn(sess)
+		coll := sess.DB(m.DbName).C(c)
+		bulk := coll.Bulk()
+		for _, v := range doc {
+			if len(v) == 2 {
+				bulk.Upsert(v[0], v[1])
+			}
+		}
+		_, err := bulk.Run()
+		if nil != err {
+			log.Println("BulkUpsertError", err)
+			b = false
+		}
+	} else {
+		b = false
+	}
+	return b
+}
+
 //批量插入
 func (m *MongodbSim) SaveBulk(c string, doc ...map[string]interface{}) bool {
 	defer util.Catch()

+ 44 - 0
common/src/qfw/util/redis/redisutil.go

@@ -78,6 +78,50 @@ func Put(code, key string, obj interface{}, timeout int) bool {
 	return b
 }
 
+func BulkPut(code string, timeout int, obj ...interface{}) bool {
+	b := false
+	defer func() {
+		if r := recover(); r != nil {
+			log.Println("[E]", r)
+			for skip := 1; ; skip++ {
+				_, file, line, ok := runtime.Caller(skip)
+				if !ok {
+					break
+				}
+				go log.Printf("%v,%v\n", file, line)
+			}
+		}
+	}()
+	conn := RedisPool[code].Get()
+	defer conn.Close()
+	var err error
+	for _, _tmp := range obj {
+		tmp, ok := _tmp.([]interface{})
+		if ok && len(tmp) == 2 {
+			key, kok := tmp[0].(string)
+			if kok && key != "" {
+				_obj, _err := json.Marshal(tmp[1])
+				if _err != nil {
+					log.Println("redisutil-SET-序列化出错Error", _err)
+					return b
+				}
+				if timeout < 1 {
+					_, err = conn.Do("SET", key, _obj)
+				} else {
+					_, err = conn.Do("SET", key, _obj, "EX", timeout)
+				}
+			}
+		}
+	}
+	if nil != err {
+		b = false
+		log.Println("redisutil-SETError-put", err)
+	} else {
+		b = b && true
+	}
+	return b
+}
+
 //直接存字节流
 func PutBytes(code, key string, data *[]byte, timeout int) (err error) {
 

+ 15 - 0
common/src/qfw/util/simpleencrypt.go

@@ -2,6 +2,7 @@ package util
 
 import (
 	"encoding/base64"
+	"encoding/hex"
 )
 
 type SimpleEncrypt struct {
@@ -15,6 +16,13 @@ func (s *SimpleEncrypt) EncodeString(str string) string {
 	return base64.StdEncoding.EncodeToString(bs)
 }
 
+//
+func (s *SimpleEncrypt) Encode2Hex(str string) string {
+	bs := []byte(str)
+	s.doEncode(&bs)
+	return hex.EncodeToString(bs)
+}
+
 //解密String
 func (s *SimpleEncrypt) DecodeString(str string) string {
 	bs, _ := base64.StdEncoding.DecodeString(str)
@@ -22,6 +30,13 @@ func (s *SimpleEncrypt) DecodeString(str string) string {
 	return string(bs)
 }
 
+//
+func (s *SimpleEncrypt) Decode4Hex(str string) string {
+	bs, _ := hex.DecodeString(str)
+	s.doEncode(&bs)
+	return string(bs)
+}
+
 //加密
 func (s *SimpleEncrypt) Encode(data *[]byte) {
 	s.doEncode(data)

+ 5 - 2
core/src/qfw/manage/article.go

@@ -37,6 +37,8 @@ type Article struct {
 	myFeedbacks xweb.Mapper `xweb:"/member/feedback/myFeedbacks"`
 }
 
+var se = util.SE
+
 //添加文章
 func (a *Article) Addarticle() error {
 	if a.GetSession("loginName") == nil {
@@ -83,7 +85,7 @@ func (a *Article) Savearticle() error {
 		rel := a.GetString("releasetime")
 		tim, _ := time.ParseInLocation("2006-01-02 15:04:05", rel, time.Local)
 		tim1 := tim.Unix()
-		tim1 = tim1 - 28798
+		//tim1 = tim1 - 28798
 		data["releasetime"] = tim1
 		saveDate := time.Now().Unix()
 		data["l_createdate"] = saveDate
@@ -133,7 +135,7 @@ func (a *Article) Updatearticle() error {
 		rel := a.GetString("releasetime")
 		tim, _ := time.ParseInLocation("2006-01-02 15:04:05", rel, time.Local)
 		tim1 := tim.Unix()
-		tim1 = tim1 - 28798
+		//tim1 = tim1 - 28798
 		data["releasetime"] = tim1
 		data["l_createdate"] = time.Now().Unix()
 		if Update("content", "{'_id':'"+_id+"'}", data, false, false) {
@@ -144,6 +146,7 @@ func (a *Article) Updatearticle() error {
 			redis.Del("other", "/front/webhelpcontent/"+_id+".html")
 			redis.Del("other", "/front/webindexcontent/"+_id+".html")
 			redis.Del("other", "/front/weixincontent/"+_id)
+			redis.Del("other", "jyblog"+se.EncodeString(_id))
 			redis.Del("other", "/")
 			redis.Del("other", "latestNews") //最新消息
 		} else {

+ 0 - 1
core/src/web/templates/enterprise/reldetail.html

@@ -118,7 +118,6 @@
 			{{else}}
 			<div class="b-com-last">
 				{{if .T.res.s_persion}}<span class="glyphicon ren2"></span>{{.T.res.s_persion}}<span class="margin-r-15"></span>{{end}}
-				{{if or .T.res.s_mobile .T.res.Tel}}<span class="glyphicon shouji"></span>{{if .T.res.s_mobile}}{{.T.res.s_mobile}}{{else if .T.res.Tel}}{{.T.res.Tel}}{{end}}<span class="margin-r-15"></span>{{end}}
 				{{if .T.res.s_qq}}
 					<span class="glyphicon qq1"></span>
 					<script type="text/javascript">

+ 1 - 0
core/src/web/templates/manage/addarticle.html

@@ -244,6 +244,7 @@ margin-top:-8px;
 		<option value="qtlm"> 其他栏目 </option>
 		<option value="wxlm"> 微信栏目 </option>
 		<option value="zhsk"> 知识库 </option>
+		<option value="jybk"> 剑鱼博客 </option>
 		</select>
 		</div>
 		</div>

+ 3 - 1
core/src/web/templates/manage/articlelist.html

@@ -53,7 +53,7 @@ $(function(){
   ,classname:"table-hover"
   ,css:{"height":"430px"}
   //,post:{"contenttype":""}
-  ,buttons: ['<div style="margin:5px 20px 0px 30px;" class="controls pull-right"><span><a href="/manage/addarticle"><button class="btn btn-info" onclick="" type="button">添加文章</button></a></span></div><div style="width:50%;margin:5px" class="input-group pull-right" id="search"><input type="text" id="searchtext" value=""  data-original-title="Search" class="form-control" placeholder="请输入检索条件..."><span class="input-group-btn"><button class="btn btn-success" onclick="SearchContent()" data-original-title="Search" id="searchtip" type="button" style="height:38px;">检索</button></span></div><div style="margin:5px;" class="controls pull-right"><select class="form-control" id="select" ><option value=""> 请选择栏目 </option><option value="qykb"> 企业网快报 </option><option value="mtpj">媒体评价 </option><option value="hyzx">行业资讯 </option><option value="zcfg"> 政策法规 </option><option  value="zthd"> 专题活动 </option><option value="zh" > 展会 </option><option value="qtlm"> 其他栏目 </option><option value="wxlm"> 微信栏目 </option><option value="zhsk"> 知识库 </option></select></div>']
+  ,buttons: ['<div style="margin:5px 20px 0px 30px;" class="controls pull-right"><span><a href="/manage/addarticle"><button class="btn btn-info" onclick="" type="button">添加文章</button></a></span></div><div style="width:50%;margin:5px" class="input-group pull-right" id="search"><input type="text" id="searchtext" value=""  data-original-title="Search" class="form-control" placeholder="请输入检索条件..."><span class="input-group-btn"><button class="btn btn-success" onclick="SearchContent()" data-original-title="Search" id="searchtip" type="button" style="height:38px;">检索</button></span></div><div style="margin:5px;" class="controls pull-right"><select class="form-control" id="select" ><option value=""> 请选择栏目 </option><option value="qykb"> 企业网快报 </option><option value="mtpj">媒体评价 </option><option value="hyzx">行业资讯 </option><option value="zcfg"> 政策法规 </option><option  value="zthd"> 专题活动 </option><option value="zh" > 展会 </option><option value="qtlm"> 其他栏目 </option><option value="wxlm"> 微信栏目 </option><option value="zhsk"> 知识库 </option><option value="jybk"> 剑鱼博客 </option></select></div>']
   , url: '/manage/articlelist/list'
   , columns: [
         {
@@ -95,6 +95,8 @@ $(function(){
 			v = "微信栏目"
 			}else if(v == "zhsk") {
 			v = "知识库"
+			}else if(v == "jybk") {
+			v = "剑鱼博客"
 			}
 			return v
 		}

+ 2 - 2
core/src/web/templates/manage/feedbackcontent.html

@@ -113,8 +113,8 @@ background-color:#FFFFFF;
 		</div>
 		<div class="form-group">
         <label id="col-sm-2" class="col-sm-2 control-label" for="name">问题来源页面:</label>
-	    <div class="col-sm-4">
-        <span style="background-color:#f0f0f0;" class="form-control">{{.T.s_source}}</span>
+	    <div class="col-sm-6">
+        <span style="background-color:#f0f0f0;cursor:pointer" title="点击访问此地址" onClick="window.open({{.T.s_source}})" class="form-control">{{.T.s_source}}</span>
 		</div>
 		<div class="col-sm-4">
 		</div>

+ 1 - 0
core/src/web/templates/manage/newscontent.html

@@ -260,6 +260,7 @@ margin-top:-8px;
 		<option value="qtlm" {{if  eq .T.s_contenttype "qtlm"}} selected="selected" {{end}}> 其他栏目 </option>
 		<option value="wxlm" {{if  eq .T.s_contenttype "wxlm"}} selected="selected" {{end}}> 微信栏目 </option>
 		<option value="zhsk" {{if  eq .T.s_contenttype "zhsk"}} selected="selected" {{end}}> 知识库 </option>
+		<option value="jybk" {{if  eq .T.s_contenttype "jybk"}} selected="selected" {{end}}> 剑鱼博客 </option>
 		</select>
 		</div>
 		</div>

Some files were not shown because too many files changed in this diff