search.go 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703
  1. package util
  2. import (
  3. MC "app.yhyue.com/moapp/jybase/common"
  4. ME "app.yhyue.com/moapp/jybase/encrypt"
  5. elastic "app.yhyue.com/moapp/jybase/esv1"
  6. "crypto/rand"
  7. "encoding/json"
  8. "fmt"
  9. "github.com/zeromicro/go-zero/core/logx"
  10. "io/ioutil"
  11. IC "jyBXCore/rpc/init"
  12. "jyBXCore/rpc/internal/config"
  13. "jyBXCore/rpc/model/es"
  14. "jyBXCore/rpc/type/bxcore"
  15. "math/big"
  16. "net/http"
  17. "net/url"
  18. "regexp"
  19. "strconv"
  20. "strings"
  21. "time"
  22. "unicode"
  23. )
  24. var ClearHtml = regexp.MustCompile("<[^>]*>")
  25. var MatchSpace = regexp.MustCompile("\\s+")
  26. var filterReg_3 = regexp.MustCompile("(项目|公告|公示)$")
  27. var filterReg_2 = regexp.MustCompile("^[)\\)>》】\\]}}〕,,;;::'\"“”。.\\??、/+=\\_—*&……\\^%$¥@!!`~·(\\(<《【\\[{{〔]+$")
  28. var filterReg_1 = regexp.MustCompile("^([0-9]{1,3}|[零一二三四五六七八九十]{1,2}|联系人?|电话|地址|编号|采购|政府采购|成交|更正|招标|中标|变更|结果)$")
  29. var filterReg = regexp.MustCompile("^[的人号时元万公告项目地址电话邮编日期联系招标中结果成交项目项目采购采购项目政府采购公告更正公告]+$")
  30. var PhoneReg = regexp.MustCompile("^[1][3-9][0-9]{9}$")
  31. var EmailPattern = regexp.MustCompile("^([a-zA-Z0-9_\\-\\.]+)@((\\[[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.)|(([a-zA-Z0-9\\-]+\\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\\]?)$")
  32. func SearchHistory(history, searchvalue string) (arrs []string) {
  33. if searchvalue != "" {
  34. arrs = strings.Split(history, ",")
  35. //新增历史记录
  36. if history == "" {
  37. arrs = make([]string, 0)
  38. }
  39. for k, v := range arrs {
  40. if v == strings.TrimSpace(searchvalue) {
  41. arrs = append(arrs[:k], arrs[k+1:]...)
  42. break
  43. }
  44. }
  45. arrs = append(arrs, searchvalue)
  46. if len(arrs) > 10 {
  47. arrs = arrs[1:11]
  48. }
  49. }
  50. return arrs
  51. }
  52. func FilteKey(k string) string {
  53. k = strings.TrimSpace(k)
  54. k = filterReg_3.ReplaceAllString(k, "")
  55. k = filterReg_2.ReplaceAllString(k, "")
  56. k = filterReg_1.ReplaceAllString(k, "")
  57. k = filterReg.ReplaceAllString(k, "")
  58. return k
  59. }
  60. //超过20个字,截断
  61. //返回截取后的字符串和截取掉中的前3个字
  62. func InterceptSearchKW(word string, isIntercept, isFilter bool) (b_word, a_word, s_word string) {
  63. if isFilter {
  64. word = FilteKey(word)
  65. }
  66. word = MatchSpace.ReplaceAllString(strings.TrimSpace(word), " ")
  67. words := []rune(word)
  68. if len(words) > 20 && isIntercept {
  69. b_word = string(words[:20])
  70. b_word = strings.TrimSpace(b_word)
  71. if len(words) > 23 {
  72. a_word = string(words[20:23])
  73. } else {
  74. a_word = string(words[20:])
  75. }
  76. } else {
  77. b_word = word
  78. }
  79. a_word = strings.TrimSpace(a_word)
  80. s_word = MatchSpace.ReplaceAllString(b_word, "+")
  81. return
  82. }
  83. func HttpEs(ques, analyzer, esAddress string) (res string) {
  84. var addrs []string
  85. surl := ""
  86. for _, s := range strings.Split(esAddress, ",") {
  87. addrs = append(addrs, s)
  88. }
  89. i, _ := rand.Int(rand.Reader, big.NewInt(int64(len(addrs)))) //随机
  90. surl = addrs[int(i.Int64())] + "/bidding/_analyze"
  91. URL, _ := url.Parse(surl)
  92. Q := URL.Query()
  93. Q.Add("text", ques)
  94. Q.Add("analyzer", analyzer)
  95. URL.RawQuery = Q.Encode()
  96. resp, err := http.Get(URL.String())
  97. if err != nil {
  98. logx.Info("es连接失败 err1:", err)
  99. resp, err = getesResp(ques, analyzer, addrs)
  100. if err != nil {
  101. return
  102. }
  103. }
  104. result, err := ioutil.ReadAll(resp.Body)
  105. if err == nil {
  106. defer resp.Body.Close()
  107. var resmap map[string]interface{}
  108. json.Unmarshal(result, &resmap)
  109. if resmap != nil && resmap["tokens"] != nil {
  110. tokens := MC.ObjArrToMapArr(resmap["tokens"].([]interface{}))
  111. for _, v := range tokens {
  112. token := MC.ObjToString(v["token"])
  113. if len([]rune(token)) == 1 && unicode.IsLetter([]rune(token)[0]) {
  114. continue
  115. }
  116. if res != "" {
  117. res += "+"
  118. }
  119. res += token
  120. }
  121. }
  122. }
  123. return
  124. }
  125. //
  126. func getesResp(ques, analyzer string, addrs []string) (resp *http.Response, err error) {
  127. for _, v := range addrs {
  128. surl := v + "/bidding/_analyze"
  129. URL, _ := url.Parse(surl)
  130. Q := URL.Query()
  131. Q.Add("text", ques)
  132. Q.Add("analyzer", analyzer)
  133. URL.RawQuery = Q.Encode()
  134. resp, err = http.Get(URL.String())
  135. if err == nil {
  136. break
  137. }
  138. }
  139. return resp, err
  140. }
  141. //pc、微信、app 招标信息搜索
  142. const (
  143. INDEX = "bidding"
  144. TYPE = "bidding"
  145. bidSearch_sort = `{"dataweight":-1,"publishtime":-1}`
  146. RedisName = "other"
  147. //招标搜索分页--每页显示数量
  148. SearchPageSize = 50
  149. //招标搜索分页--最大页数
  150. SearchMaxPageNum = 10 //免费用户500条记录
  151. SearchMaxPageNum_PAYED = 100 //付费用户5000条记录
  152. bidSearch_field_1 = `"_id","title","publishtime","dataweight","toptype","subtype","type","area","city","s_subscopeclass","bidamount","budget","buyerclass","spidercode","site"` //,"filetext"
  153. bidSearch_field = bidSearch_field_1 + `,"bidopentime","winner","buyer","projectname","projectcode","projectinfo"`
  154. bidSearch_field_file = `,"filetext","isValidFile"`
  155. query_bool_should = `{"bool":{"should":[%s],"minimum_should_match": 1}}`
  156. )
  157. //GetBidSearchData 标信息搜索
  158. func GetBidSearchData(in *bxcore.SearchReq) (count int64, list []*bxcore.SearchList) {
  159. t := time.Now()
  160. var hightlightContent bool = false //是否高亮正文
  161. var selectTypeArr = strings.Split(in.SelectType, ",")
  162. for _, v := range selectTypeArr {
  163. if v == "detail" {
  164. hightlightContent = true
  165. break
  166. }
  167. }
  168. qstr := GetSearchQuery(in, GetBidSearchQuery(in))
  169. var start = int((in.PageNum - 1) * in.PageSize)
  170. if start >= 0 {
  171. field := bidSearch_field_1
  172. if start == 0 {
  173. field = bidSearch_field
  174. }
  175. if IC.C.FileSignBool {
  176. field = field + bidSearch_field_file
  177. }
  178. biddingSearch := es.EsSearch{
  179. Index: INDEX,
  180. Itype: TYPE,
  181. Query: qstr,
  182. FindFields: "detail",
  183. Order: bidSearch_sort,
  184. Fields: field,
  185. Start: start,
  186. Limit: int(in.PageSize),
  187. Count: 0,
  188. HighLight: false,
  189. }
  190. var repl *[]map[string]interface{}
  191. if hightlightContent {
  192. biddingSearch.Count = 115
  193. biddingSearch.HighLight = hightlightContent
  194. }
  195. //repl = elastic.GetAllByNgram(INDEX, TYPE, qstr, ``, bidSearch_sort, field, start, int(in.PageSize), 0, false)
  196. count, repl = biddingSearch.GetAllByNgramWithCount()
  197. if repl != nil && *repl != nil && len(*repl) > 0 {
  198. BidListConvert(in.Industry, repl)
  199. list = searchListFormart(repl, true)
  200. }
  201. logx.Info("关键词 -1- 查询耗时:", time.Since(t).Seconds())
  202. MakeCollection(in.UserId, list)
  203. }
  204. logx.Info("关键词 查询耗时:", time.Since(t).Seconds())
  205. return
  206. }
  207. //
  208. var topTypeMap = map[string]string{
  209. "招标预告": "预告",
  210. "招标公告": "招标",
  211. "招标结果": "结果",
  212. "招标信用信息": "其它",
  213. "拟建项目": "拟建",
  214. "采购意向": "采购意向",
  215. }
  216. //
  217. func GetBidSearchQuery(in *bxcore.SearchReq) string {
  218. query := ``
  219. //省份
  220. area := in.Province
  221. if area != "" {
  222. query += `{"terms":{"area":[`
  223. for k, v := range strings.Split(area, ",") {
  224. if k > 0 {
  225. query += `,`
  226. }
  227. query += `"` + v + `"`
  228. }
  229. query += `]}}`
  230. }
  231. //
  232. city := in.City
  233. if city != "" {
  234. if len(query) > 0 {
  235. query += ","
  236. }
  237. query += `{"terms":{"city":[`
  238. for k, v := range strings.Split(city, ",") {
  239. if k > 0 {
  240. query += `,`
  241. }
  242. query += `"` + v + `"`
  243. }
  244. query += `]}}`
  245. }
  246. if query != "" {
  247. query = fmt.Sprintf(query_bool_should, query)
  248. }
  249. //发布时间
  250. publishtime := in.PublishTime
  251. if publishtime != "" && len(strings.Split(publishtime, "-")) > 1 {
  252. if len(query) > 0 {
  253. query += ","
  254. }
  255. starttime := strings.Split(publishtime, "-")[0]
  256. endtime := strings.Split(publishtime, "-")[1]
  257. query += `{"range":{"publishtime":{`
  258. if starttime != "" {
  259. query += `"gte":` + starttime
  260. }
  261. if starttime != "" && endtime != "" {
  262. query += `,`
  263. }
  264. if endtime != "" {
  265. query += `"lt":` + endtime
  266. }
  267. query += `}}}`
  268. }
  269. //信息类型-二级
  270. subtype := in.Subtype
  271. toptype := MC.If(in.Toptype != "", strings.Split(in.Toptype, ","), []string{}).([]string)
  272. alltype := ``
  273. //二级分类
  274. if subtype != "" {
  275. var typeInt = 0
  276. alltype += `{"terms":{"subtype":[`
  277. for k, v := range strings.Split(subtype, ",") {
  278. if ttype := MC.If(topTypeMap[v] != "" && in.Toptype == "", topTypeMap[v], "").(string); ttype != "" {
  279. toptype = append(toptype, ttype)
  280. typeInt += 1
  281. continue
  282. }
  283. if k > typeInt {
  284. alltype += `,`
  285. }
  286. alltype += `"` + v + `"`
  287. }
  288. alltype += `]}}`
  289. }
  290. //信息类型 一级分类
  291. logx.Info("toptype:", toptype)
  292. if len(toptype) > 0 {
  293. if alltype != "" {
  294. alltype += ","
  295. }
  296. alltype += `{"terms":{"toptype":[`
  297. for k, v := range toptype {
  298. if k > 0 {
  299. alltype += `,`
  300. }
  301. alltype += `"` + v + `"`
  302. }
  303. alltype += `]}}`
  304. }
  305. if alltype != "" {
  306. if query != "" {
  307. query += ","
  308. }
  309. query += fmt.Sprintf(query_bool_should, alltype)
  310. }
  311. //采购单位类型
  312. buyerclass := in.BuyerClass
  313. if buyerclass != "" {
  314. if len(query) > 0 {
  315. query += ","
  316. }
  317. query += `{"terms":{"buyerclass":[`
  318. for k, v := range strings.Split(buyerclass, ",") {
  319. if k > 0 {
  320. query += `,`
  321. }
  322. query += `"` + v + `"`
  323. }
  324. query += `]}}`
  325. }
  326. return query
  327. }
  328. //包含正文或 附件 不包含标题
  329. func DetailFileORTitle(findfields string) bool {
  330. return (strings.Contains(findfields, `"detail"`) || strings.Contains(findfields, `"filetext"`)) && !strings.Contains(findfields, `"title"`)
  331. }
  332. func GetSearchQuery(in *bxcore.SearchReq, mustquery string) (qstr string) {
  333. multi_match := `{"multi_match": {"query": "%s","type": "phrase", "fields": [%s]}}`
  334. query := `{"query":{"bool":{"must":[%s],"must_not":[%s]}}}`
  335. //query := `{"query": {"function_score": {"query": {"bool": {"must": [%s],"must_not": [%s]}},"field_value_factor": {"field": "dataweight","modifier": "ln1p","missing": 0}}}}`
  336. query_bool_should := `{"bool":{"should":[%s],"minimum_should_match": 1}}`
  337. query_bools_must := `{"bool":{"must":[{"range":{"bidamount":{%s}}}]}},{"bool":{"must":[{"range":{"budget":{%s}}}],"must_not":[{"range":{"bidamount":{"gte":-1}}}]}}`
  338. query_bool_must := `{"bool":{"must":[{"terms":{"s_subscopeclass":[%s]}}]}}`
  339. query_bool_must_term := `{"bool": {"must": [{ "term": {"isValidFile": %d }}]}}`
  340. query_missing := `{"constant_score":{"filter":{"missing":{"field":"%s"}}}}`
  341. gte := `"gte": %s`
  342. lte := `"lte": %s`
  343. musts, must_not := []string{}, []string{}
  344. if mustquery != "" {
  345. musts = append(musts, mustquery)
  346. }
  347. //搜索范围是否只有附件
  348. //搜索范围只选择附件,是否有附件条件无效;
  349. var isFileSearch bool = in.SelectType == "filetext"
  350. selectTypeArr := strings.Split(in.SelectType, ",")
  351. var findfields string
  352. if selectTypeArr == nil || len(selectTypeArr) == 0 {
  353. findfields = `"title"`
  354. } else {
  355. findfields = fmt.Sprintf(`"%s"`, strings.Join(selectTypeArr, "\",\""))
  356. }
  357. //此时关键词中间有+进行隔离
  358. if in.KeyWords != "" {
  359. shoulds := []string{}
  360. var switchBool = strings.Contains(findfields, "detail") && strings.Contains(findfields, "title") && IC.C.SearchTypeSwitch
  361. keyword_multi_match := fmt.Sprintf(multi_match, "%s", findfields)
  362. for _, v := range strings.Split(in.KeyWords, "+") {
  363. if elastic.ReplaceYH(v) == "" {
  364. continue
  365. }
  366. if len([]rune(elastic.ReplaceYH(v))) == 1 {
  367. //单个字 搜索范围 有全文或者附件 无标题 例如:学 虚拟机 detail 搜索的时候加上标题
  368. if DetailFileORTitle(findfields) {
  369. keyword_multi_match = fmt.Sprintf(multi_match, "%s", findfields+`,"title"`)
  370. }
  371. } else {
  372. //标题 全文搜索 搜索类型开关打开 默认搜索全文;(全文包含标题)(单字排除)
  373. if switchBool && len([]rune(elastic.ReplaceYH(v))) > 1 {
  374. if strings.Contains(findfields, `"title",`) {
  375. findfields = strings.Replace(findfields, `"title",`, ``, -1)
  376. } else if strings.Contains(findfields, `,"title"`) {
  377. findfields = strings.Replace(findfields, `,"title"`, ``, -1)
  378. }
  379. keyword_multi_match = fmt.Sprintf(multi_match, "%s", findfields)
  380. }
  381. }
  382. shoulds = append(shoulds, fmt.Sprintf(keyword_multi_match, elastic.ReplaceYH(v)))
  383. }
  384. musts = append(musts, fmt.Sprintf(elastic.NgramMust, strings.Join(shoulds, ",")))
  385. }
  386. if in.Industry != "" {
  387. industrys := strings.Split(in.Industry, ",")
  388. musts = append(musts, fmt.Sprintf(query_bool_must, `"`+strings.Join(industrys, `","`)+`"`))
  389. }
  390. //价格
  391. if in.Price != "" && len(strings.Split(in.Price, "-")) > 1 {
  392. minprice, maxprice := strings.Split(in.Price, "-")[0], strings.Split(in.Price, "-")[1]
  393. if minprice != "" || maxprice != "" {
  394. sq := ``
  395. if minprice != "" {
  396. min, _ := strconv.ParseFloat(minprice, 64)
  397. minprice = fmt.Sprintf("%.0f", min*10000)
  398. if minprice == "0" {
  399. minprice = ""
  400. }
  401. }
  402. if maxprice != "" {
  403. max, _ := strconv.ParseFloat(maxprice, 64)
  404. maxprice = fmt.Sprintf("%.0f", max*10000)
  405. if maxprice == "0" {
  406. maxprice = ""
  407. }
  408. }
  409. if minprice != "" {
  410. sq += fmt.Sprintf(gte, minprice)
  411. }
  412. if minprice != "" && maxprice != "" {
  413. sq += `,`
  414. }
  415. if maxprice != "" {
  416. sq += fmt.Sprintf(lte, maxprice)
  417. }
  418. if minprice != "" || maxprice != "" {
  419. query_price := fmt.Sprintf(query_bool_should, fmt.Sprintf(query_bools_must, sq, sq))
  420. musts = append(musts, query_price)
  421. }
  422. }
  423. }
  424. hasBuyerTel := in.BuyerTel
  425. if hasBuyerTel != "" {
  426. if hasBuyerTel == "y" {
  427. must_not = append(must_not, fmt.Sprintf(query_missing, "buyertel"))
  428. } else {
  429. musts = append(musts, fmt.Sprintf(query_missing, "buyertel"))
  430. }
  431. }
  432. hasWinnerTel := in.WinnerTel
  433. if hasWinnerTel != "" {
  434. if hasWinnerTel == "y" {
  435. must_not = append(must_not, fmt.Sprintf(query_missing, "winnertel"))
  436. } else {
  437. musts = append(musts, fmt.Sprintf(query_missing, "winnertel"))
  438. }
  439. }
  440. //排除词
  441. notkey := in.ExclusionWords
  442. if notkey = strings.TrimSpace(notkey); notkey != "" {
  443. notkey_multi_match := fmt.Sprintf(multi_match, "%s", findfields)
  444. notkey_must_not := []string{}
  445. for _, v := range strings.Split(notkey, " ") {
  446. v = strings.TrimSpace(v)
  447. if v == "" {
  448. continue
  449. }
  450. if len([]rune(elastic.ReplaceYH(v))) == 1 {
  451. //单个字 搜索范围 有全文或者附件 无标题 例如:学 虚拟机 detail 搜索的时候加上标题
  452. if DetailFileORTitle(findfields) {
  453. notkey_multi_match = fmt.Sprintf(multi_match, "%s", findfields+`,"title"`)
  454. }
  455. }
  456. notkey_must_not = append(notkey_must_not, fmt.Sprintf(notkey_multi_match, elastic.ReplaceYH(v)))
  457. }
  458. must_not = append(must_not, fmt.Sprintf(query_bool_should, strings.Join(notkey_must_not, ",")))
  459. }
  460. //
  461. fileExists := in.FileExists
  462. if !isFileSearch && fileExists != "" {
  463. if fileExists == "1" { //有附件
  464. must_not = append(must_not, fmt.Sprintf(query_missing, "isValidFile"))
  465. musts = append(musts, fmt.Sprintf(query_bool_must_term, 1))
  466. } else if fileExists == "-1" { //无附件
  467. musts = append(musts, fmt.Sprintf(query_missing, "isValidFile"))
  468. }
  469. }
  470. qstr = fmt.Sprintf(query, strings.Join(musts, ","), strings.Join(must_not, ","))
  471. logx.Info(qstr)
  472. return
  473. }
  474. /*
  475. * 结果列表转换,目前只换行行业字段
  476. * 所有的招标搜索都要调用此方法,列表中有展示行业的也可以用
  477. * industry 搜索条件中的行业,默认为空
  478. */
  479. func BidListConvert(industry string, res *[]map[string]interface{}) {
  480. if res == nil {
  481. return
  482. }
  483. commonSubstring := func(v string) (value string) {
  484. bcs := strings.Split(v, "_")
  485. if len(bcs) == 1 {
  486. value = bcs[0]
  487. } else if len(bcs) == 2 {
  488. value = bcs[0]
  489. if strings.TrimSpace(value) == "" {
  490. value = bcs[0]
  491. }
  492. }
  493. return
  494. }
  495. for _, v := range *res {
  496. v["id"] = v["_id"]
  497. delete(v, "_id")
  498. budget, _ := v["budget"].(float64)
  499. bidamount, _ := v["bidamount"].(float64)
  500. if budget == 0 || strings.TrimSpace(fmt.Sprint(v["budget"])) == "" {
  501. delete(v, "budget")
  502. }
  503. if bidamount == 0 || strings.TrimSpace(fmt.Sprint(v["bidamount"])) == "" {
  504. delete(v, "bidamount")
  505. }
  506. value := ""
  507. subscopeclass, _ := v["s_subscopeclass"].(string)
  508. subscopeclass = strings.Trim(subscopeclass, ",")
  509. bct := strings.Split(subscopeclass, ",")
  510. if bct == nil || len(bct) == 0 {
  511. continue
  512. }
  513. //搜索条件中没有行业的话,取查询结果中第一个行业
  514. if industry == "" {
  515. value = commonSubstring(bct[0])
  516. } else { //搜索条件中有行业的话,取行业中和搜索条件相对应的第一个
  517. industrys := strings.Split(industry, ",")
  518. L:
  519. for _, bc := range bct {
  520. for _, is := range industrys {
  521. if bc == is {
  522. value = commonSubstring(bc)
  523. break L
  524. }
  525. }
  526. }
  527. }
  528. if strings.TrimSpace(value) == "" {
  529. continue
  530. }
  531. v["industry"] = value
  532. }
  533. }
  534. //是否收藏
  535. func MakeCollection(userId string, list []*bxcore.SearchList) {
  536. if list == nil || len(list) == 0 {
  537. return
  538. }
  539. param := []interface{}{userId}
  540. wh := []string{}
  541. for _, v := range list {
  542. array := ME.DecodeArticleId2ByCheck(v.Id)
  543. if len(array) == 1 && array[0] != "" {
  544. param = append(param, array[0])
  545. wh = append(wh, "?")
  546. }
  547. }
  548. if len(wh) > 0 {
  549. result := IC.MainMysql.SelectBySql(`select bid from bdcollection where userid=? and bid in (`+strings.Join(wh, ",")+`)`, param...)
  550. bid_map := map[string]bool{}
  551. if result != nil {
  552. for _, v := range *result {
  553. bid_map[ME.EncodeArticleId2ByCheck(MC.ObjToString(v["bid"]))] = true
  554. }
  555. }
  556. for _, v := range list {
  557. if bid_map[v.Id] {
  558. v.IsCollected = true
  559. }
  560. }
  561. }
  562. }
  563. //默认查询缓存数据
  564. func SearchCahcheData(in *bxcore.SearchReq) (count int64, list []*bxcore.SearchList) {
  565. //最新招标信息
  566. findfields := `"title"`
  567. qstr := GetSearchQuery(in, GetBidSearchQuery(in))
  568. //首页
  569. if in.PageNum == 1 {
  570. count = elastic.Count(INDEX, TYPE, qstr)
  571. }
  572. if count > 0 || in.PageNum > 1 {
  573. repl := elastic.GetAllByNgram(INDEX, TYPE, qstr, findfields, bidSearch_sort, bidSearch_field, int(in.PageNum), int(in.PageSize), 0, false)
  574. if repl != nil && *repl != nil && len(*repl) > 0 {
  575. BidListConvert(in.Industry, repl)
  576. list = searchListFormart(repl, false)
  577. }
  578. }
  579. return
  580. }
  581. //格式化数据
  582. func searchListFormart(repl *[]map[string]interface{}, b bool) (list []*bxcore.SearchList) {
  583. for _, v := range *repl {
  584. var searchList = &bxcore.SearchList{}
  585. if b {
  586. //正文匹配检索关键词
  587. highlight, _ := v["highlight"].(map[string][]string)
  588. detail := ""
  589. for _, val := range highlight["detail"] {
  590. detail += ClearHtml.ReplaceAllString(val, "")
  591. }
  592. searchList.Detail = detail
  593. }
  594. searchList.Area = MC.ObjToString(v["area"])
  595. searchList.AreaUrl = IC.LabelMap[searchList.Area].Url
  596. searchList.BuyerClass = MC.ObjToString(v["buyerclass"])
  597. searchList.City = MC.ObjToString(v["city"])
  598. searchList.FileExists, _ = v["isValidFile"].(bool) //附件
  599. searchList.Id = ME.EncodeArticleId2ByCheck(MC.ObjToString(v["id"]))
  600. searchList.Industry = MC.ObjToString(v["industry"])
  601. searchList.IndustryUrl = IC.LabelMap[searchList.Industry].Url
  602. searchList.PublishTime = MC.Int64All(v["publishtime"])
  603. searchList.Subtype = MC.ObjToString(v["subtype"])
  604. searchList.SubtypeUrl = IC.LabelMap[searchList.Subtype].Url
  605. searchList.Title = MC.ObjToString(v["title"])
  606. searchList.Budget = MC.Int64All(v["budget"])
  607. searchList.Bidamount = MC.Int64All(v["bidamount"])
  608. searchList.ProjectName = MC.ObjToString(v["projectname"])
  609. searchList.ProjectCode = MC.ObjToString(v["projectcode"])
  610. searchList.ProjectInfo = &bxcore.PInfo{}
  611. if v["projectinfo"] != nil {
  612. pinfo := MC.ObjToMap(v["projectinfo"])
  613. searchList.ProjectInfo.ApproveCode = MC.ObjToString((*pinfo)["approvecode"])
  614. searchList.ProjectInfo.ApproveContent = MC.ObjToString((*pinfo)["approvecontent"])
  615. searchList.ProjectInfo.ApproveDept = MC.ObjToString((*pinfo)["approvedept"])
  616. searchList.ProjectInfo.ApproveStatus = MC.ObjToString((*pinfo)["approvestatus"])
  617. searchList.ProjectInfo.ProjectType = MC.ObjToString((*pinfo)["projecttype"])
  618. searchList.ProjectInfo.ApproveNumber = MC.ObjToString((*pinfo)["approvenumber"])
  619. searchList.ProjectInfo.ApproveTime = MC.ObjToString((*pinfo)["approvetime"])
  620. }
  621. searchList.Winner = MC.ObjToString(v["winner"])
  622. searchList.Buyer = MC.ObjToString(v["buyer"])
  623. searchList.BidopenTime = MC.Int64All(v["bidopentime"])
  624. searchList.Site = MC.ObjToString(v["site"])
  625. searchList.SpiderCode = MC.ObjToString(v["spidercode"])
  626. list = append(list, searchList)
  627. }
  628. return
  629. }
  630. //合并map数据,去重,排序
  631. func DelRepeatSearchData(resOne, resTwo []*bxcore.SearchList) []*bxcore.SearchList {
  632. if len(resOne) > 0 && len(resTwo) > 0 {
  633. for _, v := range resOne {
  634. for n, m := range resTwo {
  635. if v.Id == m.Id {
  636. resTwo = append(resTwo[0:n], resTwo[n+1:]...)
  637. break
  638. }
  639. }
  640. }
  641. resOne = append(resOne, resTwo...)
  642. } else {
  643. resOne = append(resOne, resTwo...)
  644. }
  645. //sort.Slice(resOne, func(i, j int) bool {
  646. // return resOne[i].PublishTime > resOne[j].PublishTime
  647. //})
  648. return resOne
  649. }
  650. //付费用户搜索优化
  651. func IsOptimize(cc config.Config, in *bxcore.SearchReq) bool {
  652. if cc.PaySearchLimit.Switch && in.UserType != "fType" {
  653. //首页----字数(<7)
  654. if in.PageNum <= cc.PaySearchLimit.PageNum && len([]rune(in.KeyWords)) < cc.PaySearchLimit.WordSize {
  655. //时间超过一年----
  656. if pTime := GetPublishTime(cc.PaySearchLimit.Year, cc.PaySearchLimit.Month, in.PublishTime); pTime != "" {
  657. in.PublishTime = pTime
  658. return true
  659. }
  660. }
  661. }
  662. return false
  663. }
  664. func GetPublishTime(y, m int, publishTime string) string {
  665. //发布时间
  666. timeArray := strings.Split(publishTime, "-")
  667. if len(timeArray) == 2 {
  668. startTime, err1 := strconv.ParseInt(timeArray[0], 10, 64)
  669. endTime, err2 := strconv.ParseInt(timeArray[1], 10, 64)
  670. if err1 == nil && err2 == nil {
  671. if endTime == 0 {
  672. endTime = time.Now().Unix()
  673. }
  674. pTime := time.Unix(endTime, 0).AddDate(y, m, 0).Unix()
  675. if endTime-startTime > pTime {
  676. return fmt.Sprintf("%d-%d", pTime, endTime)
  677. }
  678. }
  679. }
  680. return ""
  681. }