search.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301
  1. package util
  2. import (
  3. "fmt"
  4. IC "jyBXCore/rpc/init"
  5. "jyBXCore/rpc/internal/config"
  6. "jyBXCore/rpc/type/bxcore"
  7. "regexp"
  8. "strconv"
  9. "strings"
  10. "time"
  11. MC "app.yhyue.com/moapp/jybase/common"
  12. ME "app.yhyue.com/moapp/jybase/encrypt"
  13. "app.yhyue.com/moapp/jybase/es"
  14. "github.com/zeromicro/go-zero/core/logx"
  15. )
  16. var (
  17. ClearHtml = regexp.MustCompile("<[^>]*>")
  18. MatchSpace = regexp.MustCompile("\\s+")
  19. filterReg3 = regexp.MustCompile("(项目|公告|公示)$")
  20. filterReg2 = regexp.MustCompile("^[)\\)>》】\\]}}〕,,;;::'\"“”。.\\??、/+=\\_—*&……\\^%$¥@!!`~·(\\(<《【\\[{{〔]+$")
  21. filterReg1 = regexp.MustCompile("^([0-9]{1,3}|[零一二三四五六七八九十]{1,2}|联系人?|电话|地址|编号|采购|政府采购|成交|更正|招标|中标|变更|结果)$")
  22. filterReg = regexp.MustCompile("^[的人号时元万公告项目地址电话邮编日期联系招标中结果成交项目项目采购采购项目政府采购公告更正公告]+$")
  23. //PhoneReg = regexp.MustCompile("^[1][3-9][0-9]{9}$")
  24. //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})(\\]?)$")
  25. )
  26. // SearchHistory 格式化 关键词搜索历史记录
  27. func SearchHistory(history, searchValue, additionalWords string) (arrS []string) {
  28. //主关键词
  29. var searchKeys = strings.Split(searchValue, IC.C.JYKeyMark)
  30. //附加词
  31. if additionalWords != "" {
  32. for _, aws := range strings.Split(additionalWords, ",") {
  33. for _, as := range strings.Split(aws, IC.C.JYKeyMark) {
  34. searchKeys = append(searchKeys, as)
  35. }
  36. }
  37. }
  38. //关键词 和 附加词 合并,作为新的关键词历史搜索记录
  39. if len(searchKeys) > 0 {
  40. arrS = strings.Split(history, ",")
  41. //新增历史记录
  42. if history == "" {
  43. arrS = make([]string, 0)
  44. }
  45. for _, sv := range searchKeys {
  46. for k, v := range arrS {
  47. if v == strings.TrimSpace(sv) {
  48. arrS = append(arrS[:k], arrS[k+1:]...)
  49. break
  50. }
  51. }
  52. }
  53. arrS = append(arrS, searchKeys...)
  54. if len(arrS) > 10 {
  55. arrS = arrS[len(arrS)-10:]
  56. }
  57. }
  58. return arrS
  59. }
  60. func FilterKey(k string) string {
  61. k = strings.TrimSpace(k)
  62. k = filterReg3.ReplaceAllString(k, "")
  63. k = filterReg2.ReplaceAllString(k, "")
  64. k = filterReg1.ReplaceAllString(k, "")
  65. k = filterReg.ReplaceAllString(k, "")
  66. return k
  67. }
  68. // InterceptSearchKW 超过keywordsLimit个字,截断
  69. // 返回截取后的字符串和截取掉中的前3个字
  70. // b_word:截取后的关键词;a_word:截取后 后面三个字;s_word:已截取 处理过的关键词
  71. func InterceptSearchKW(word string, keywordsLimit int, isFilter bool) (bWord, aWord, sWord string) {
  72. if isFilter {
  73. word = FilterKey(word)
  74. }
  75. word = MatchSpace.ReplaceAllString(strings.TrimSpace(word), " ")
  76. words := []rune(word)
  77. if len(words) > keywordsLimit {
  78. bWord = string(words[:keywordsLimit])
  79. bWord = strings.TrimSpace(bWord)
  80. if len(words) > keywordsLimit+3 {
  81. aWord = string(words[keywordsLimit : keywordsLimit+3])
  82. } else {
  83. aWord = string(words[keywordsLimit:])
  84. }
  85. } else {
  86. bWord = word
  87. }
  88. aWord = strings.TrimSpace(aWord)
  89. sWord = MatchSpace.ReplaceAllString(bWord, IC.C.JYKeyMark)
  90. return
  91. }
  92. func HttpEs(ques, analyzer, esAddress string) (res string) {
  93. return strings.ReplaceAll(es.Analyze(ques, "bidding", analyzer), "+", IC.C.JYKeyMark)
  94. }
  95. const (
  96. RedisName = "other"
  97. RedisNameNew = "newother"
  98. SearchPageSize = 50 //招标搜索分页--每页显示数量
  99. //招标搜索分页--最大页数
  100. SearchMaxPageNum = 10 //免费用户500条记录
  101. SearchMaxPageNum_PAYED = 100 //付费用户5000条记录
  102. )
  103. // MakeCollection 是否收藏
  104. func MakeCollection(userId string, list []*bxcore.SearchList) {
  105. if userId == "" {
  106. return
  107. }
  108. if list == nil || len(list) == 0 {
  109. return
  110. }
  111. param := []interface{}{userId}
  112. var wh []string
  113. for _, v := range list {
  114. logx.Info(v.Title, "---v.id---:", v.Id)
  115. array := ME.DecodeArticleId2ByCheck(v.Id)
  116. if len(array) == 1 && array[0] != "" {
  117. param = append(param, array[0])
  118. wh = append(wh, "?")
  119. }
  120. }
  121. if len(wh) > 0 {
  122. result := IC.MainMysql.SelectBySql(`select bid from bdcollection where userid=? and bid in (`+strings.Join(wh, ",")+`)`, param...)
  123. bidMap := map[string]bool{}
  124. if result != nil {
  125. for _, v := range *result {
  126. bidMap[ME.EncodeArticleId2ByCheck(MC.ObjToString(v["bid"]))] = true
  127. }
  128. }
  129. for _, v := range list {
  130. if bidMap[v.Id] {
  131. v.IsCollected = true
  132. }
  133. }
  134. }
  135. }
  136. // IndustryFormat 行业处理
  137. func IndustryFormat(industry, subScopeClass string) (newIndustry string) {
  138. commonSubstring := func(v string) (value string) {
  139. bcs := strings.Split(v, "_")
  140. if len(bcs) == 1 {
  141. value = bcs[0]
  142. } else if len(bcs) == 2 {
  143. value = bcs[0]
  144. if strings.TrimSpace(value) == "" {
  145. value = bcs[0]
  146. }
  147. }
  148. return
  149. }
  150. bct := strings.Split(subScopeClass, ",")
  151. if bct == nil || len(bct) == 0 {
  152. return
  153. }
  154. //搜索条件中没有行业的话,取查询结果中第一个行业
  155. if industry == "" {
  156. newIndustry = commonSubstring(bct[0])
  157. } else { //搜索条件中有行业的话,取行业中和搜索条件相对应的第一个
  158. industryArr := strings.Split(industry, ",")
  159. L:
  160. for _, bc := range bct {
  161. for _, is := range industryArr {
  162. if bc == is {
  163. newIndustry = strings.TrimSpace(commonSubstring(bc))
  164. break L
  165. }
  166. }
  167. }
  168. }
  169. return
  170. }
  171. // SearchListFormat 格式化数据
  172. func SearchListFormat(userid, industry string, repl *[]map[string]interface{}, b bool) (list []*bxcore.SearchList) {
  173. for _, v := range *repl {
  174. var searchList = &bxcore.SearchList{}
  175. //正文
  176. if b {
  177. //正文匹配检索关键词
  178. highlight, _ := v["highlight"].(map[string][]string)
  179. detail := ""
  180. for _, val := range highlight["detail"] {
  181. detail += ClearHtml.ReplaceAllString(val, "")
  182. }
  183. searchList.Detail = detail
  184. }
  185. searchList.Id = ME.EncodeArticleId2ByCheck(MC.ObjToString(v["_id"])) //ME.EncodeArticleId2ByCheck(MC.ObjToString(v["_id"])) //加密信息id
  186. searchList.Area = MC.ObjToString(v["area"]) //地区
  187. searchList.AreaUrl = IC.LabelMap[searchList.Area].Url //地区分类链接
  188. searchList.BuyerClass = MC.ObjToString(v["buyerclass"]) //采购单位类型
  189. searchList.City = MC.ObjToString(v["city"]) //城市
  190. searchList.Industry = IndustryFormat(industry, strings.Trim(MC.ObjToString(v["s_subscopeclass"]), ",")) //行业
  191. searchList.IndustryUrl = IC.LabelMap[searchList.Industry].Url //行业分类地址
  192. searchList.PublishTime = MC.Int64All(v["publishtime"]) //发布时间
  193. searchList.FileExists, _ = v["isValidFile"].(bool) //是否有附件
  194. searchList.Subtype = MC.ObjToString(v["subtype"]) //信息类型
  195. searchList.SubtypeUrl = IC.LabelMap[searchList.Subtype].Url //信息类型分类链接
  196. searchList.Title = MC.ObjToString(v["title"]) //标题
  197. searchList.ProjectName = MC.ObjToString(v["projectname"]) //项目名称
  198. searchList.ProjectCode = MC.ObjToString(v["projectcode"]) //项目代码
  199. if budget, ok := v["budget"].(float64); ok && budget > 0 { //预算
  200. searchList.Budget = int64(budget)
  201. }
  202. if bidAmount, ok := v["bidamount"].(float64); ok && bidAmount > 0 { //中标金额
  203. searchList.BidAmount = int64(bidAmount)
  204. }
  205. searchList.Buyer = MC.ObjToString(v["buyer"]) //采购单位
  206. searchList.BuyerTel = MC.ObjToString(v["buyertel"]) //采购单位联系方式
  207. searchList.BuyerPerson = MC.ObjToString(v["buyerperson"]) //采购单位联系人
  208. searchList.Agency = MC.ObjToString(v["agency"]) //代理机构
  209. searchList.AgencyTel = MC.ObjToString(v["agencytel"]) //代理机构联系电话
  210. searchList.AgencyPerson = MC.ObjToString(v["agencyperson"]) //代理机构联系人
  211. searchList.BidOpenTime = MC.Int64All(v["bidopentime"]) //开标时间
  212. searchList.BidEndTime = MC.Int64All(v["bidendtime"]) //发布时间
  213. searchList.SignEndTime = MC.Int64All(v["signendtime"]) //投标截止日期
  214. searchList.Site = MC.ObjToString(v["site"]) //网站来源名称
  215. searchList.SpiderCode = MC.ObjToString(v["spidercode"]) //网站来源代码
  216. searchList.Winner = MC.ObjToString(v["winner"]) //中标企业
  217. winnerList := MC.ObjToString(v["s_winner"]) //中标企业名称集合
  218. if winnerList != "" && len(strings.Split(winnerList, ",")) > 0 {
  219. for wk, wv := range strings.Split(winnerList, ",") {
  220. var (
  221. winnerId = ""
  222. )
  223. if v["entidlist"] != nil {
  224. if entIdList := MC.ObjArrToStringArr(v["entidlist"].([]interface{})); len(entIdList) > wk { //中标企业id集合
  225. winnerId = entIdList[wk]
  226. }
  227. }
  228. searchList.WinnerInfo = append(searchList.WinnerInfo, &bxcore.WinnerInfo{
  229. Winner: wv, //中标企业 需要单独处理
  230. WinnerTel: MC.ObjToString(v["winnertel"]), //中标企业联系电话
  231. WinnerPerson: MC.ObjToString(v["winnerperson"]), //中标企业联系人
  232. WinnerId: MC.If(winnerId != "" && len([]rune(winnerId)) > 12, ME.EncodeArticleId2ByCheck(winnerId), "").(string), //中标企业加密id 存在winnerId 异常的情况
  233. })
  234. }
  235. }
  236. searchList.ProjectInfo = &bxcore.PInfo{} //拟建项目信息
  237. if v["projectinfo"] != nil {
  238. pInfo := MC.ObjToMap(v["projectinfo"])
  239. searchList.ProjectInfo.ApproveCode = MC.ObjToString((*pInfo)["approvecode"])
  240. searchList.ProjectInfo.ApproveContent = MC.ObjToString((*pInfo)["approvecontent"])
  241. searchList.ProjectInfo.ApproveDept = MC.ObjToString((*pInfo)["approvedept"])
  242. searchList.ProjectInfo.ApproveStatus = MC.ObjToString((*pInfo)["approvestatus"])
  243. searchList.ProjectInfo.ProjectType = MC.ObjToString((*pInfo)["projecttype"])
  244. searchList.ProjectInfo.ApproveNumber = MC.ObjToString((*pInfo)["approvenumber"])
  245. searchList.ProjectInfo.ApproveTime = MC.ObjToString((*pInfo)["approvetime"])
  246. }
  247. list = append(list, searchList)
  248. }
  249. return
  250. }
  251. // IsOptimize 付费用户搜索优化
  252. // 需求来源:付费用户 默认查询 五年内数据,数据查询耗时,
  253. // 付费用户 且开关打开,针对前两页数据,满足关键词(< 7个字),查询时间范围一年以上,缩短查询时间
  254. func IsOptimize(cc config.Config, in *bxcore.SearchReq) bool {
  255. if cc.PaySearchLimit.Switch && in.UserType != "fType" {
  256. //首页----字数(<7)
  257. if in.PageNum <= cc.PaySearchLimit.PageNum && len([]rune(in.KeyWords)) < cc.PaySearchLimit.WordSize {
  258. //时间超过一年----
  259. if pTime := GetPublishTime(cc.PaySearchLimit.Year, cc.PaySearchLimit.Month, in.PublishTime); pTime != "" {
  260. in.PublishTime = pTime
  261. return true
  262. }
  263. }
  264. }
  265. return false
  266. }
  267. // GetPublishTime 查询时间调整
  268. func GetPublishTime(y, m int, publishTime string) string {
  269. //发布时间
  270. timeArray := strings.Split(publishTime, "-")
  271. if len(timeArray) == 2 {
  272. startTime, err1 := strconv.ParseInt(timeArray[0], 10, 64)
  273. endTime, err2 := strconv.ParseInt(timeArray[1], 10, 64)
  274. if err1 == nil && err2 == nil {
  275. if endTime == 0 {
  276. endTime = time.Now().Unix()
  277. }
  278. //重新计算数据查询 开始时间
  279. pTime := time.Unix(endTime, 0).AddDate(y, m, 0).Unix()
  280. //从新定义搜索时间跨度
  281. if endTime-startTime > pTime {
  282. return fmt.Sprintf("%d-%d", pTime, endTime)
  283. }
  284. }
  285. }
  286. return ""
  287. }