queryStruct.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468
  1. package service
  2. import (
  3. "app.yhyue.com/moapp/jybase/encrypt"
  4. "context"
  5. "fmt"
  6. "github.com/gogf/gf/v2/container/gvar"
  7. "github.com/gogf/gf/v2/frame/g"
  8. "github.com/gogf/gf/v2/util/gconv"
  9. "jyseo/internal/consts"
  10. "jyseo/utility"
  11. "strings"
  12. )
  13. type (
  14. SeoBiddingQuery struct {
  15. keys string
  16. area string
  17. city string
  18. district string
  19. topType string
  20. subType string
  21. topClass string
  22. subClass string
  23. industry string
  24. SIGN int
  25. Similarity string
  26. Other map[string]interface{}
  27. }
  28. InfoList struct {
  29. Id string
  30. Title string
  31. Url string
  32. Area string
  33. Industry string
  34. Subtype string
  35. Price string
  36. Site string
  37. Detail string
  38. Keyword string
  39. FileExists bool
  40. PublishTime int64
  41. ExpandMap map[string]interface{}
  42. }
  43. ListResp struct {
  44. Total int
  45. List []*InfoList
  46. }
  47. )
  48. func NewBiddingQuery() *SeoBiddingQuery {
  49. return &SeoBiddingQuery{}
  50. }
  51. // EquipArea 装备地区查询
  52. func (query *SeoBiddingQuery) EquipArea(areaNode *AreaNode) *SeoBiddingQuery {
  53. if areaNode != nil {
  54. switch areaNode.Type {
  55. case 1, 3:
  56. query.area = areaNode.Name
  57. case 2:
  58. query.city = areaNode.Name
  59. case 4:
  60. // 区县会重名,所以带上上级名字
  61. if areaNode.PNode != nil {
  62. if areaNode.PNode.Type == 3 {
  63. query.area = areaNode.PNode.Name
  64. } else {
  65. query.city = areaNode.PNode.Name
  66. }
  67. }
  68. query.district = areaNode.Name
  69. }
  70. }
  71. return query
  72. }
  73. func (query *SeoBiddingQuery) EquipSType(sTypeNode *STypeNode) *SeoBiddingQuery {
  74. if sTypeNode.PNode != nil {
  75. query.subType = sTypeNode.Name
  76. } else {
  77. query.topType = sTypeNode.Name
  78. }
  79. return query
  80. }
  81. func (query *SeoBiddingQuery) EquipIndustryNode(node *IndustryNode) *SeoBiddingQuery {
  82. if node != nil {
  83. query.industry = node.Name
  84. }
  85. return query
  86. }
  87. func (query *SeoBiddingQuery) EquipSIGN(sign int) *SeoBiddingQuery {
  88. if sign > 0 {
  89. query.SIGN = sign
  90. }
  91. return query
  92. }
  93. func (query *SeoBiddingQuery) EquipKeyWord(keyword string) *SeoBiddingQuery {
  94. query.keys = keyword
  95. return query
  96. }
  97. // EquipEsSimilarity es查询相似度
  98. func (query *SeoBiddingQuery) EquipEsSimilarity(similarity string) *SeoBiddingQuery {
  99. query.Similarity = similarity
  100. return query
  101. }
  102. func (query *SeoBiddingQuery) EquipIndustry(topClass, subClass string) *SeoBiddingQuery {
  103. if topClass != "" {
  104. query.topClass = topClass
  105. }
  106. if subClass != "" {
  107. query.subClass = subClass
  108. }
  109. return query
  110. }
  111. func (query *SeoBiddingQuery) EquipOtherMap(m map[string]interface{}) *SeoBiddingQuery {
  112. if m != nil && len(m) > 0 {
  113. query.Other = m
  114. }
  115. return query
  116. }
  117. // defaultDataFormat 默认格式化数据
  118. func (query *SeoBiddingQuery) defaultDataFormat(data []map[string]interface{}) (bList []*InfoList) {
  119. if len(data) > 0 {
  120. titleReduction := map[string]bool{}
  121. for _, v := range data {
  122. bl := &InfoList{
  123. Id: gconv.String(v["bid_id"]),
  124. Title: gconv.String(v["title"]),
  125. Url: fmt.Sprintf("/nologin/content/%s.html", encrypt.EncodeArticleId2ByCheck(gconv.String(v["bid_id"]))),
  126. Area: gconv.String(v["area"]),
  127. Subtype: gconv.String(v["subtype"]),
  128. Detail: gconv.String(v["desc"]),
  129. Keyword: gconv.String(v["keyword"]),
  130. }
  131. //重复标题加数字后缀
  132. if titleReduction[bl.Title] {
  133. continue
  134. }
  135. titleReduction[bl.Title] = true
  136. bl.PublishTime = gconv.Int64(v["publish_time"])
  137. if gconv.String(v["area"]) == "剑鱼信息发布平台" {
  138. bl.Site = "用户发布"
  139. }
  140. if industry := gconv.String(v["industry"]); industry != "" {
  141. bl.Industry = industry
  142. }
  143. if isValidFile, _ := v["isValidFile"].(bool); isValidFile {
  144. bl.FileExists = true
  145. }
  146. if budget := gconv.Float64(v["budget"]); budget > 0 {
  147. bl.Price = utility.ConversionMoney(v["budget"])
  148. } else if bidamount := gconv.Float64(v["bidamount"]); bidamount > 0 {
  149. bl.Price = utility.ConversionMoney(v["bidamount"])
  150. }
  151. bList = append(bList, bl)
  152. }
  153. }
  154. return
  155. }
  156. // getBidListCacheKey 获取列表缓存
  157. func (query *SeoBiddingQuery) getDataPageListCacheKey(pageNum, pageSize, maxTotal int, flag string) string {
  158. return fmt.Sprintf("JySeoBidDataPageList_%s_%d_%d_%d_%s_%s_%s_%s_%s_%s_%s_%s_%s_%d", flag, pageNum, pageSize, maxTotal, query.keys, query.area, query.city, query.district, query.topType, query.subType, query.topClass, query.subClass, query.industry, query.SIGN)
  159. }
  160. // GetDataPageList 翻页列表页查询
  161. // 一次性查询全部信息,生成每页cache
  162. func (query *SeoBiddingQuery) GetDataPageList(ctx context.Context, pageNum, pageSize, maxTotal int, flag string, getData func(context.Context, int, *SeoBiddingQuery) []map[string]interface{}, formatFunc ...func([]map[string]interface{}) []*InfoList) (res *ListResp, err error) {
  163. var vars *gvar.Var
  164. res = &ListResp{}
  165. if pageSize == 0 {
  166. pageSize = consts.SettingPageSize
  167. }
  168. vars, err = g.Redis().Get(ctx, query.getDataPageListCacheKey(pageNum, pageSize, maxTotal, flag))
  169. if err != nil || vars.IsNil() {
  170. //并发限制
  171. if ok := JySeoQueryListLimit.Limit(); !ok {
  172. err = fmt.Errorf("请求超时")
  173. return
  174. }
  175. defer JySeoQueryListLimit.Reset()
  176. data := getData(ctx, maxTotal, query)
  177. formatExec := query.defaultDataFormat
  178. if len(formatFunc) > 0 && formatFunc[0] != nil {
  179. formatExec = formatFunc[0]
  180. }
  181. formatData := formatExec(data)
  182. count := len(formatData)
  183. if count > maxTotal {
  184. count = maxTotal
  185. }
  186. if data != nil && len(data) > 0 {
  187. totalPage := count / pageSize
  188. if count%pageSize != 0 {
  189. totalPage++
  190. }
  191. for i := 1; i <= gconv.Int(totalPage); i++ {
  192. start, end := (i-1)*pageSize, (i)*pageSize
  193. if end > count {
  194. end = count
  195. }
  196. pageTmp := &ListResp{
  197. Total: totalPage,
  198. List: formatData[start:end],
  199. }
  200. if i == pageNum {
  201. res = pageTmp
  202. }
  203. if err := g.Redis().SetEX(ctx, query.getDataPageListCacheKey(i, pageSize, maxTotal, flag), pageTmp, consts.GetListCacheTime(ctx, flag)); err != nil {
  204. g.Log().Errorf(ctx, "第%d页数据 存储redis err:%v", err)
  205. }
  206. }
  207. }
  208. } else {
  209. err = vars.Struct(res)
  210. }
  211. return
  212. }
  213. // getTabDataCacheKey 获取列表缓存
  214. func (query *SeoBiddingQuery) getOnceDataCacheKey(total int, flag string) string {
  215. return fmt.Sprintf("JySeoBidDataOnceList_%s_%d_%s_%s_%s_%s_%s_%s_%s_%s_%s_%d_%v", flag, total, query.keys, query.area, query.city, query.district, query.topType, query.subType, query.topClass, query.subClass, query.industry, query.SIGN, query.Other)
  216. }
  217. // GetOnceData 获取数据
  218. func (query *SeoBiddingQuery) GetOnceData(ctx context.Context, total int, flag string, getData func(context.Context, int, *SeoBiddingQuery) []map[string]interface{}, formatFunc ...func([]map[string]interface{}) []*InfoList) (res []*InfoList, err error) {
  219. var vars *gvar.Var
  220. cacheKey := query.getOnceDataCacheKey(total, flag)
  221. vars, err = g.Redis().Get(ctx, cacheKey)
  222. if err != nil || vars.IsNil() {
  223. //并发限制
  224. if ok := JySeoQueryTabLimit.Limit(); !ok {
  225. err = fmt.Errorf("请求超时")
  226. return
  227. }
  228. defer JySeoQueryTabLimit.Reset()
  229. data := getData(ctx, total*2, query)
  230. if data != nil && len(data) > 0 {
  231. formatExec := query.defaultDataFormat
  232. if len(formatFunc) > 0 && formatFunc[0] != nil {
  233. formatExec = formatFunc[0]
  234. }
  235. res = formatExec(data)
  236. }
  237. if len(res) > total {
  238. res = res[:total]
  239. }
  240. fmt.Println(cacheKey, consts.GetListCacheTime(ctx, flag))
  241. if res != nil && len(res) > 0 {
  242. if err := g.Redis().SetEX(ctx, cacheKey, res, consts.GetListCacheTime(ctx, flag)); err != nil {
  243. g.Log().Errorf(ctx, "GetOnceData 存储redis err:%v", err)
  244. }
  245. }
  246. } else {
  247. err = vars.Struct(&res)
  248. }
  249. return
  250. }
  251. func FillingBiddingBaseFields(ctx context.Context, res []map[string]interface{}, filterNj ...bool) []map[string]interface{} {
  252. var batchSize = g.Cfg().MustGet(context.Background(), "listPageSetting.batchSize", 200).Int()
  253. var (
  254. queryIdBatch = [][]string{}
  255. tmpArr = make([]string, 0, batchSize)
  256. total = len(res)
  257. index = 0
  258. )
  259. for _, m := range res {
  260. bidId := gconv.String(m["bid_id"])
  261. if bidId == "" {
  262. continue
  263. }
  264. tmpArr = append(tmpArr, bidId)
  265. if len(tmpArr) == batchSize {
  266. queryIdBatch = append(queryIdBatch, tmpArr)
  267. tmpArr = make([]string, 0, batchSize)
  268. }
  269. index++
  270. if index == total {
  271. queryIdBatch = append(queryIdBatch, tmpArr)
  272. }
  273. }
  274. var (
  275. bidMap = map[string]map[string]interface{}{}
  276. execSql = `SELECT * FROM new_bidList WHERE bid_id IN ('%s')`
  277. )
  278. if len(filterNj) > 0 {
  279. execSql = `SELECT * FROM new_bidList WHERE toptype !='拟建' AND toptype !='采购意向' AND bid_id IN ('%s')`
  280. }
  281. for _, ids := range queryIdBatch {
  282. bidRes, _ := g.DB().Query(ctx, fmt.Sprintf(execSql, strings.Join(ids, "','")))
  283. if bidRes.IsEmpty() {
  284. continue
  285. }
  286. for _, m := range bidRes.List() {
  287. if bidIdStr := gconv.String(m["bid_id"]); bidIdStr != "" {
  288. bidMap[bidIdStr] = m
  289. }
  290. }
  291. }
  292. var finalArr []map[string]interface{}
  293. for i := 0; i < len(res); i++ {
  294. if tBid := gconv.String(res[i]["bid_id"]); tBid != "" && bidMap[tBid] != nil && len(bidMap[tBid]) > 0 {
  295. for k, v := range bidMap[tBid] {
  296. res[i][k] = v
  297. }
  298. finalArr = append(finalArr, res[i])
  299. }
  300. }
  301. return finalArr
  302. }
  303. func FillingEsBiddingBaseFields(res []map[string]interface{}, key string) []map[string]interface{} {
  304. bidMap := map[string]map[string]interface{}{}
  305. for _, m := range res {
  306. if bidIdStr := gconv.String(m["_id"]); bidIdStr != "" {
  307. mv := make(map[string]interface{})
  308. mv["keyword"] = key
  309. if len([]rune(gconv.String(m["detail"]))) > 100 {
  310. mv["desc"] = string([]rune(gconv.String(m["detail"]))[:100])
  311. } else {
  312. mv["desc"] = gconv.String(m["detail"])
  313. }
  314. mv["bid_id"] = bidIdStr
  315. mv["title"] = gconv.String(m["title"])
  316. mv["area"] = gconv.String(m["area"])
  317. mv["city"] = gconv.String(m["city"])
  318. mv["district"] = gconv.String(m["district"])
  319. mv["toptype"] = gconv.String(m["toptype"])
  320. mv["subtype"] = gconv.String(m["subtype"])
  321. mv["industry"] = ""
  322. if subs := gconv.String(m["s_subscopeclass"]); subs != "" {
  323. classArr := strings.Split(strings.Split(subs, ",")[0], "_")
  324. if len(classArr) == 2 {
  325. mv["industry"] = classArr[0]
  326. }
  327. }
  328. mv["bidamount"] = gconv.Float64(m["bidamount"])
  329. mv["budget"] = gconv.Float64(m["budget"])
  330. mv["size"] = gconv.String(m["size"])
  331. mv["isValidFile"] = gconv.Bool(m["isValidFile"])
  332. mv["publish_time"] = gconv.Int64(m["publishtime"])
  333. bidMap[bidIdStr] = mv
  334. }
  335. }
  336. var finalArr []map[string]interface{}
  337. for i := 0; i < len(res); i++ {
  338. if tBid := gconv.String(res[i]["_id"]); tBid != "" && bidMap[tBid] != nil && len(bidMap[tBid]) > 0 {
  339. for k, v := range bidMap[tBid] {
  340. res[i][k] = v
  341. }
  342. finalArr = append(finalArr, res[i])
  343. }
  344. }
  345. return finalArr
  346. }
  347. func GetEsQuery(maxTotal int, query *SeoBiddingQuery) string {
  348. var (
  349. musts []string
  350. should []string
  351. )
  352. qu := `{
  353. "query": {
  354. "bool": {
  355. "filter": [
  356. %s
  357. ],
  358. %s
  359. "must_not": [
  360. {
  361. "terms": {
  362. "toptype": [
  363. "拟建",
  364. "采购意向"
  365. ]
  366. }
  367. }
  368. ]
  369. }
  370. },
  371. "_source": [
  372. "title",
  373. "publishtime",
  374. "_id",
  375. "area",
  376. "city",
  377. "district",
  378. "toptype",
  379. "subtype",
  380. "s_subscopeclass",
  381. "bidamount",
  382. "budget",
  383. "site",
  384. "detail",
  385. "isValidFile"
  386. ],
  387. "sort": {
  388. "publishtime": "desc"
  389. },
  390. "from": 0,
  391. "size": %d
  392. }`
  393. if query.keys != "" {
  394. if query.Similarity != "" {
  395. musts = append(musts, fmt.Sprintf(`{"multi_match": {"query": "%s","minimum_should_match": "%s","fields": ["title"]}}`, query.keys, query.Similarity))
  396. } else {
  397. if strings.Index(query.keys, ",") > -1 {
  398. for _, s := range strings.Split(query.keys, ",") {
  399. k := strings.TrimSpace(s)
  400. if k != "" {
  401. should = append(should, fmt.Sprintf(`{"multi_match": {"query": "%s","type": "phrase","fields": ["title"]}}`, s))
  402. }
  403. }
  404. } else {
  405. should = append(should, fmt.Sprintf(`{"multi_match": {"query": "%s","type": "phrase","fields": ["title"]}}`, query.keys))
  406. }
  407. }
  408. }
  409. if query.district != "" {
  410. musts = append(musts, fmt.Sprintf(`{"term": {"district": "%s" }}`, query.district))
  411. }
  412. if query.city != "" {
  413. musts = append(musts, fmt.Sprintf(`{"term": {"city": "%s" }}`, query.city))
  414. }
  415. if query.area != "" {
  416. musts = append(musts, fmt.Sprintf(`{"term": {"area": "%s" }}`, query.area))
  417. }
  418. if query.topType != "" {
  419. if val, _ := consts.TopTypeMap[query.topType]; val != "" {
  420. musts = append(musts, fmt.Sprintf(`{"term": {"topType": "%s" }}`, val))
  421. } else {
  422. musts = append(musts, fmt.Sprintf(`{"term": {"topType": "%s" }}`, query.topType))
  423. }
  424. }
  425. if query.topClass != "" {
  426. musts = append(musts, fmt.Sprintf(`{"term": {"s_topscopeclass": "%s" }}`, query.topClass))
  427. }
  428. if query.subClass != "" && query.topClass != "" {
  429. musts = append(musts, fmt.Sprintf(`{"term": {"s_subscopeclass": "%s" }}`, fmt.Sprintf("%s_%s", query.topClass, query.subClass)))
  430. }
  431. var shouldStr string
  432. if len(should) > 0 {
  433. shouldStr = fmt.Sprintf(`"should":[%s],"minimum_should_match": 1,`, strings.Join(should, ","))
  434. }
  435. return fmt.Sprintf(qu, strings.Join(musts, ","), shouldStr, maxTotal)
  436. }