queryStruct.go 12 KB

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