search.go 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458
  1. package util
  2. import (
  3. "bp.jydev.jianyu360.cn/BaseService/jyMicroservices/jyBXCore/entity"
  4. IC "bp.jydev.jianyu360.cn/BaseService/jyMicroservices/jyBXCore/rpc/init"
  5. "bp.jydev.jianyu360.cn/BaseService/jyMicroservices/jyBXCore/rpc/internal/config"
  6. "bp.jydev.jianyu360.cn/BaseService/jyMicroservices/jyBXCore/rpc/type/bxcore"
  7. "fmt"
  8. "github.com/gogf/gf/v2/util/gconv"
  9. "log"
  10. "regexp"
  11. "strconv"
  12. "strings"
  13. "time"
  14. MC "app.yhyue.com/moapp/jybase/common"
  15. ME "app.yhyue.com/moapp/jybase/encrypt"
  16. "app.yhyue.com/moapp/jybase/es"
  17. )
  18. var (
  19. ClearHtml = regexp.MustCompile("<[^>]*>")
  20. MatchSpace = regexp.MustCompile("\\s+")
  21. filterReg3 = regexp.MustCompile("(项目|公告|公示)$")
  22. filterReg2 = regexp.MustCompile("^[)\\)>》】\\]}}〕,,;;::'\"“”。.\\??、/+=\\_—*&……\\^%$¥@!!`~·(\\(<《【\\[{{〔]+$")
  23. filterReg1 = regexp.MustCompile("^([0-9]{1,3}|[零一二三四五六七八九十]{1,2}|联系人?|电话|地址|编号|采购|政府采购|成交|更正|招标|中标|变更|结果)$")
  24. filterReg = regexp.MustCompile("^[的人号时元万公告项目地址电话邮编日期联系招标中结果成交项目项目采购采购项目政府采购公告更正公告]+$")
  25. filterReg_4 = regexp.MustCompile("([)>》】\\]\\}}〕,,;;::'\"“”。\\.\\??、/\\+=\\\\_—\\*&……\\^%$¥@!!`~·(\\(<《【\\[\\{{〔])")
  26. //PhoneReg = regexp.MustCompile("^[1][3-9][0-9]{9}$")
  27. //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})(\\]?)$")
  28. )
  29. // SearchHistory 格式化 关键词搜索历史记录
  30. func SearchHistory(history, searchValue, additionalWords string) (arrS []string) {
  31. //主关键词
  32. var searchKeys = strings.Split(searchValue, IC.C.JYKeyMark)
  33. //附加词
  34. if additionalWords != "" {
  35. for _, aws := range strings.Split(additionalWords, ",") {
  36. for _, as := range strings.Split(aws, IC.C.JYKeyMark) {
  37. searchKeys = append(searchKeys, as)
  38. }
  39. }
  40. }
  41. //关键词 和 附加词 合并,作为新的关键词历史搜索记录
  42. if len(searchKeys) > 0 {
  43. arrS = strings.Split(history, ",")
  44. //新增历史记录
  45. if history == "" {
  46. arrS = make([]string, 0)
  47. }
  48. for _, sv := range searchKeys {
  49. for k, v := range arrS {
  50. if v == strings.TrimSpace(sv) {
  51. arrS = append(arrS[:k], arrS[k+1:]...)
  52. break
  53. }
  54. }
  55. }
  56. arrS = append(arrS, searchKeys...)
  57. if len(arrS) > 10 {
  58. arrS = arrS[len(arrS)-10:]
  59. }
  60. }
  61. return arrS
  62. }
  63. // 和PC端保持一致
  64. func FilterKey(k string) string {
  65. k = strings.TrimSpace(k)
  66. //k = filterReg3.ReplaceAllString(k, "")
  67. //k = filterReg2.ReplaceAllString(k, "")
  68. //k = filterReg1.ReplaceAllString(k, "")
  69. //k = filterReg.ReplaceAllString(k, "")
  70. k = filterReg_4.ReplaceAllString(k, " ")
  71. return k
  72. }
  73. // InterceptSearchKW 超过keywordsLimit个字,截断
  74. // 返回截取后的字符串和截取掉中的前3个字
  75. // b_word:截取后的关键词;a_word:截取后 后面三个字;s_word:已截取 处理过的关键词
  76. func InterceptSearchKW(word string, keywordsLimit int, isFilter bool) (bWord, aWord, sWord string) {
  77. if isFilter {
  78. word = FilterKey(word)
  79. }
  80. word = MatchSpace.ReplaceAllString(strings.TrimSpace(word), " ")
  81. words := []rune(word)
  82. if len(words) > keywordsLimit {
  83. bWord = string(words[:keywordsLimit])
  84. bWord = strings.TrimSpace(bWord)
  85. if len(words) > keywordsLimit+3 {
  86. aWord = string(words[keywordsLimit : keywordsLimit+3])
  87. } else {
  88. aWord = string(words[keywordsLimit:])
  89. }
  90. } else {
  91. bWord = word
  92. }
  93. aWord = strings.TrimSpace(aWord)
  94. sWord = MatchSpace.ReplaceAllString(bWord, IC.C.JYKeyMark)
  95. return
  96. }
  97. func HttpEs(ques, analyzer, esAddress string) (res string) {
  98. return strings.ReplaceAll(es.Analyze(ques, "bidding", analyzer), "+", IC.C.JYKeyMark)
  99. }
  100. const (
  101. RedisName = "other"
  102. RedisNameNew = "newother"
  103. SearchPageSize = 50 //招标搜索分页--每页显示数量
  104. //招标搜索分页--最大页数
  105. SearchMaxPageNum = 10 //免费用户500条记录
  106. SearchMaxPageNum_PAYED = 100 //付费用户5000条记录
  107. )
  108. // MakeCollection 是否收藏
  109. func MakeCollection(userId string, list []*bxcore.SearchList) {
  110. if userId == "" {
  111. return
  112. }
  113. if list == nil || len(list) == 0 {
  114. return
  115. }
  116. param := []interface{}{userId}
  117. var wh []string
  118. for _, v := range list {
  119. //log.Println(v.Title, "---v.id---:", v.Id)
  120. array := ME.DecodeArticleId2ByCheck(v.Id)
  121. if len(array) == 1 && array[0] != "" {
  122. param = append(param, array[0])
  123. wh = append(wh, "?")
  124. }
  125. }
  126. if len(wh) > 0 {
  127. result := IC.MainMysql.SelectBySql(`select bid from bdcollection where userid=? and bid in (`+strings.Join(wh, ",")+`)`, param...)
  128. bidMap := map[string]bool{}
  129. if result != nil {
  130. for _, v := range *result {
  131. bidMap[ME.EncodeArticleId2ByCheck(MC.ObjToString(v["bid"]))] = true
  132. }
  133. }
  134. for _, v := range list {
  135. if bidMap[v.Id] {
  136. v.IsCollected = true
  137. }
  138. }
  139. }
  140. }
  141. // IndustryFormat 行业处理
  142. func IndustryFormat(industry, subScopeClass string) (newIndustry string) {
  143. commonSubstring := func(v string) (value string) {
  144. bcs := strings.Split(v, "_")
  145. if len(bcs) == 1 {
  146. value = bcs[0]
  147. } else if len(bcs) == 2 {
  148. value = bcs[0]
  149. if strings.TrimSpace(value) == "" {
  150. value = bcs[0]
  151. }
  152. }
  153. return
  154. }
  155. bct := strings.Split(subScopeClass, ",")
  156. if bct == nil || len(bct) == 0 {
  157. return
  158. }
  159. //搜索条件中没有行业的话,取查询结果中第一个行业
  160. if industry == "" {
  161. newIndustry = commonSubstring(bct[0])
  162. } else { //搜索条件中有行业的话,取行业中和搜索条件相对应的第一个
  163. industryArr := strings.Split(industry, ",")
  164. L:
  165. for _, bc := range bct {
  166. for _, is := range industryArr {
  167. if bc == is {
  168. newIndustry = strings.TrimSpace(commonSubstring(bc))
  169. break L
  170. }
  171. }
  172. }
  173. }
  174. return
  175. }
  176. // SearchListFormat 格式化数据
  177. func SearchListFormat(userid, subInformation, propertyForm, industry string, repl *[]map[string]interface{}, b bool, bidField string, words []string) (list []*bxcore.SearchList) {
  178. for _, v := range *repl {
  179. var searchList = &bxcore.SearchList{}
  180. //正文
  181. if highlight, ok := v["highlight"].(map[string][]string); ok {
  182. if b {
  183. detail := ""
  184. for _, val := range highlight["detail"] {
  185. detail += ClearHtml.ReplaceAllString(val, "")
  186. }
  187. searchList.Detail = detail
  188. }
  189. if highlight["filetext"] != nil && len(highlight["filetext"]) > 0 {
  190. var sWords []string
  191. for _, v1 := range highlight["filetext"] {
  192. for _, w := range words {
  193. if strings.Contains(v1, w) && w != "" {
  194. sWords = append(sWords, w)
  195. }
  196. }
  197. }
  198. searchList.FiletextSearch = true
  199. if len(sWords) > 0 {
  200. searchList.FsWord = sWords
  201. }
  202. }
  203. }
  204. searchList.Id = ME.EncodeArticleId2ByCheck(MC.ObjToString(v["_id"])) //ME.EncodeArticleId2ByCheck(MC.ObjToString(v["_id"])) //加密信息id
  205. searchList.Area = MC.ObjToString(v["area"]) //地区
  206. searchList.AreaUrl = IC.LabelMap[searchList.Area].Url //地区分类链接
  207. searchList.BuyerClass = MC.ObjToString(v["buyerclass"]) //采购单位类型
  208. searchList.City = MC.ObjToString(v["city"]) //城市
  209. searchList.Industry = IndustryFormat(industry, strings.Trim(MC.ObjToString(v["s_subscopeclass"]), ",")) //行业
  210. searchList.IndustryUrl = IC.LabelMap[searchList.Industry].Url //行业分类地址
  211. searchList.PublishTime = MC.Int64All(v["publishtime"]) //发布时间
  212. searchList.FileExists, _ = v["isValidFile"].(bool) //是否有附件
  213. searchList.Subtype = MC.ObjToString(v["subtype"]) //信息类型
  214. searchList.SubtypeUrl = IC.LabelMap[searchList.Subtype].Url //信息类型分类链接
  215. searchList.Title = MC.ObjToString(v["title"]) //标题
  216. searchList.ProjectName = MC.ObjToString(v["projectname"]) //项目名称
  217. searchList.ProjectCode = MC.ObjToString(v["projectcode"]) //项目代码
  218. if budget, ok := v["budget"].(float64); ok && budget > 0 { //预算
  219. searchList.Budget = int64(budget)
  220. }
  221. if bidAmount, ok := v["bidamount"].(float64); ok && bidAmount > 0 { //中标金额
  222. searchList.BidAmount = int64(bidAmount)
  223. }
  224. searchList.Buyer = MC.ObjToString(v["buyer"]) //采购单位
  225. searchList.BuyerTel = MC.ObjToString(v["buyertel"]) //采购单位联系方式
  226. searchList.BuyerPerson = MC.ObjToString(v["buyerperson"]) //采购单位联系人
  227. searchList.Agency = MC.ObjToString(v["agency"]) //代理机构
  228. searchList.AgencyTel = MC.ObjToString(v["agencytel"]) //代理机构联系电话
  229. searchList.AgencyPerson = MC.ObjToString(v["agencyperson"]) //代理机构联系人
  230. searchList.BidOpenTime = MC.Int64All(v["bidopentime"]) //开标时间
  231. searchList.BidEndTime = MC.Int64All(v["bidendtime"]) //发布时间
  232. searchList.SignEndTime = MC.Int64All(v["signendtime"]) //投标截止日期
  233. searchList.Site = MC.ObjToString(v["site"]) //网站来源名称
  234. searchList.SpiderCode = MC.ObjToString(v["spidercode"]) //网站来源代码
  235. searchList.Winner = MC.ObjToString(v["winner"]) //中标企业
  236. winnerList := MC.ObjToString(v["s_winner"]) //中标企业名称集合
  237. if winnerList != "" && len(strings.Split(winnerList, ",")) > 0 {
  238. for wk, wv := range strings.Split(winnerList, ",") {
  239. var (
  240. winnerId = ""
  241. )
  242. if v["entidlist"] != nil {
  243. if entIdList := MC.ObjArrToStringArr(v["entidlist"].([]interface{})); len(entIdList) > wk { //中标企业id集合
  244. winnerId = entIdList[wk]
  245. }
  246. }
  247. log.Println("-----------SearchWinner------:", IC.C.SearchWinner)
  248. if IC.C.SearchWinner.Switch && winnerId == "" {
  249. continue
  250. }
  251. if entity.RegWinner.MatchString(wv) {
  252. continue
  253. }
  254. searchList.WinnerInfo = append(searchList.WinnerInfo, &bxcore.WinnerInfo{
  255. Winner: wv, //中标企业 需要单独处理
  256. WinnerTel: MC.ObjToString(v["winnertel"]), //中标企业联系电话
  257. WinnerPerson: MC.ObjToString(v["winnerperson"]), //中标企业联系人
  258. WinnerId: MC.If(winnerId != "" && len([]rune(winnerId)) > 12, ME.EncodeArticleId2ByCheck(winnerId), "").(string), //中标企业加密id 存在winnerId 异常的情况
  259. })
  260. }
  261. }
  262. searchList.ProjectInfo = &bxcore.PInfo{} //拟建项目信息
  263. if v["projectinfo"] != nil {
  264. pInfo := MC.ObjToMap(v["projectinfo"])
  265. searchList.ProjectInfo.ApproveCode = MC.ObjToString((*pInfo)["approvecode"])
  266. searchList.ProjectInfo.ApproveContent = MC.ObjToString((*pInfo)["approvecontent"])
  267. searchList.ProjectInfo.ApproveDept = MC.ObjToString((*pInfo)["approvedept"])
  268. searchList.ProjectInfo.ApproveStatus = MC.ObjToString((*pInfo)["approvestatus"])
  269. searchList.ProjectInfo.ProjectType = MC.ObjToString((*pInfo)["projecttype"])
  270. searchList.ProjectInfo.ApproveNumber = MC.ObjToString((*pInfo)["approvenumber"])
  271. searchList.ProjectInfo.ApproveTime = MC.ObjToString((*pInfo)["approvetime"])
  272. }
  273. if bidField == "BIProperty" {
  274. searchList.FileExists = false
  275. searchList.Bi = &bxcore.BI{}
  276. //物业数据处理
  277. if v["tag_topinformation"] != nil {
  278. searchList.Bi.TagTopInformation = gconv.SliceStr(v["tag_topinformation"])
  279. }
  280. if v["tag_subinformation"] != nil {
  281. subinformationArr := gconv.SliceStr(v["tag_subinformation"])
  282. if len(subinformationArr) > 0 {
  283. if len(subInformation) > 0 {
  284. fool := false
  285. for _, s1 := range subinformationArr {
  286. for _, s2 := range strings.Split(subInformation, ",") {
  287. if s2 == s1 {
  288. searchList.Bi.TagSubInformation = strings.Split(s1, "_")[1]
  289. fool = true
  290. break
  291. }
  292. }
  293. if fool {
  294. break
  295. }
  296. }
  297. } else {
  298. searchList.Bi.TagSubInformation = strings.Split(subinformationArr[0], "_")[1]
  299. }
  300. }
  301. }
  302. //是否有附件
  303. if v["tag_set"] != nil {
  304. tag := gconv.Map(v["tag_set"])
  305. if tag != nil && tag["wuye"] != nil {
  306. wuye := gconv.Map(tag["wuye"])
  307. if wuye != nil {
  308. isFile := gconv.Int64(wuye["isfile"])
  309. propertyFormStr := gconv.String(wuye["property_form"])
  310. if isFile == 63 {
  311. searchList.FileExists = true
  312. }
  313. if propertyFormStr != "" {
  314. if propertyForm != "" {
  315. fool := false
  316. for _, s1 := range strings.Split(propertyFormStr, ",") {
  317. for _, s2 := range strings.Split(propertyForm, ",") {
  318. if s2 == s1 {
  319. searchList.Bi.PropertyForm = s2
  320. fool = true
  321. break
  322. }
  323. }
  324. if fool {
  325. break
  326. }
  327. }
  328. } else {
  329. searchList.Bi.PropertyForm = strings.Split(propertyFormStr, ",")[0]
  330. }
  331. }
  332. }
  333. }
  334. }
  335. }
  336. list = append(list, searchList)
  337. }
  338. return
  339. }
  340. // IsOptimize 付费用户搜索优化
  341. // 需求来源:付费用户 默认查询 五年内数据,数据查询耗时,
  342. // 付费用户 且开关打开,针对前两页数据,满足关键词(< 7个字),查询时间范围一年以上,缩短查询时间
  343. func IsOptimize(cc config.Config, in *bxcore.SearchReq) bool {
  344. if cc.PaySearchLimit.Switch && in.UserType != "fType" {
  345. //首页----字数(<7)
  346. if in.PageNum <= cc.PaySearchLimit.PageNum && len([]rune(in.KeyWords)) < cc.PaySearchLimit.WordSize {
  347. //时间超过一年----
  348. if pTime := GetPublishTime(cc.PaySearchLimit.Year, cc.PaySearchLimit.Month, in.PublishTime); pTime != "" {
  349. in.PublishTime = pTime
  350. return true
  351. }
  352. }
  353. }
  354. return false
  355. }
  356. // GetPublishTime 查询时间调整
  357. func GetPublishTime(y, m int, publishTime string) string {
  358. //发布时间
  359. timeArray := strings.Split(publishTime, "-")
  360. if len(timeArray) == 2 {
  361. startTime, err1 := strconv.ParseInt(timeArray[0], 10, 64)
  362. endTime, err2 := strconv.ParseInt(timeArray[1], 10, 64)
  363. if err1 == nil && err2 == nil {
  364. if endTime == 0 {
  365. endTime = time.Now().Unix()
  366. }
  367. //重新计算数据查询 开始时间
  368. pTime := time.Unix(endTime, 0).AddDate(y, m, 0).Unix()
  369. //从新定义搜索时间跨度
  370. if endTime-startTime > pTime {
  371. return fmt.Sprintf("%d-%d", pTime, endTime)
  372. }
  373. }
  374. }
  375. return ""
  376. }
  377. // GetQueryItems 免费 标题(title) 正文(content) 老用户【中标企业(winner)】
  378. // 付费用户 全部(all)、标题(title) 正文(content) 会员: 采购单位(buyer) 中标企业(winner) 招标代理机构(agency) 附件(file)
  379. // 项目名称projectname和标的物purchasing(ppa)
  380. func GetQueryItems(selectType string, limitOldTime, registerData int64, isPay bool) (items []string) {
  381. if isPay {
  382. for _, t := range strings.Split(selectType, ",") {
  383. if t == "content" {
  384. items = append(items, "detail")
  385. } else if t == "buyer" {
  386. items = append(items, "buyer.mbuyer")
  387. } else if t == "winner" {
  388. items = append(items, "s_winner.mwinner")
  389. } else if t == "agency" {
  390. items = append(items, "agency.magency")
  391. } else if t == "title" {
  392. items = append(items, "title")
  393. } else if t == "ppa" {
  394. items = append(items, []string{"purchasing", "projectname.pname"}...)
  395. } else if t == "file" { //dev4.7.8 标讯优化:搜索范围附件-》全部用户可用
  396. items = append(items, "filetext")
  397. }
  398. }
  399. return
  400. }
  401. //老用户 使用付费功能
  402. isOldUser := registerData != 0 && registerData < limitOldTime
  403. for _, t := range strings.Split(selectType, ",") {
  404. if t == "winner" && isOldUser {
  405. items = append(items, "s_winner.mwinner")
  406. } else if t == "title" {
  407. items = append(items, "title")
  408. } else if t == "content" {
  409. items = append(items, "detail")
  410. } else if t == "file" { //dev4.7.8 标讯优化:搜索范围附件-》全部用户可用
  411. items = append(items, "filetext")
  412. }
  413. }
  414. return
  415. }
  416. // 采购单位、中标企业、代理机构
  417. var GetMatchArrSql = func(field string, val ...string) (sql string) {
  418. if len(val) == 0 {
  419. return
  420. }
  421. var (
  422. arr []string
  423. i int
  424. )
  425. for _, s := range val {
  426. if s == "" {
  427. continue
  428. }
  429. if len([]rune(s)) > 30 {
  430. s = string([]rune(s)[:30])
  431. }
  432. i++
  433. arr = append(arr, fmt.Sprintf(`{"match_phrase": {"%s": "%s"}}`, field, s))
  434. if i > 4 {
  435. break
  436. }
  437. }
  438. if len(arr) == 0 {
  439. return ""
  440. }
  441. return fmt.Sprintf(`{"bool": {"should": [%s],"minimum_should_match": 1}}`, strings.Join(arr, ","))
  442. }