search.go 16 KB

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