buyerListBYEs.go 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450
  1. package model
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. IC "jyBXBuyer/rpc/init"
  6. "jyBXBuyer/rpc/type/bxbuyer"
  7. "log"
  8. "strconv"
  9. "strings"
  10. "sync"
  11. "time"
  12. MC "app.yhyue.com/moapp/jybase/common"
  13. "app.yhyue.com/moapp/jybase/encrypt"
  14. elastic "app.yhyue.com/moapp/jybase/es"
  15. "github.com/zeromicro/go-zero/core/logx"
  16. )
  17. const (
  18. HasContact = 1 // 采购单位是否有联系方式筛选项 1-有联系方式
  19. NoContact = 2 // 无联系方式
  20. )
  21. type BScope struct {
  22. Keyword []string `json:"keyword"`
  23. AdditionalWords []string `json:"additionalWords"`
  24. ExcludedWords []string `json:"excludedWords"`
  25. }
  26. // 获取采购单位查询query
  27. func BuyerListQuery(in *bxbuyer.BuyerListReq) (qstr string, CountQstr string) {
  28. query := `{%s "query":{"bool":{"must":[%s],"must_not": [{"term": {"buyer_name": ""}}],"should":[%s],"minimum_should_match": %d}}}`
  29. //21.1.20 为和画像保持一致 数据组要求 budget 改成 bidamount
  30. query_bool_should := `{"bool":{"should":[%s],"minimum_should_match": 1}}`
  31. bools := []string{}
  32. musts := []string{}
  33. musts_should := []string{}
  34. //省份
  35. if len(in.Province) > 0 {
  36. musts_should = append(musts_should, fmt.Sprintf(`{"terms":{"province":["%s"]}}`, strings.Join(in.Province, "\",\"")))
  37. }
  38. //城市
  39. if len(in.City) > 0 {
  40. musts_should = append(musts_should, fmt.Sprintf(`{"terms":{"city":["%s"]}}`, strings.Join(in.City, "\",\"")))
  41. }
  42. if len(musts_should) > 0 {
  43. musts = append(musts, fmt.Sprintf(query_bool_should, strings.Join(musts_should, ",")))
  44. }
  45. //采购单位名称
  46. if in.BuyerName != "" {
  47. entNameQuery := fmt.Sprintf(`{"multi_match": {"query": "%s","type": "phrase", "fields": ["name"]}}`, in.BuyerName)
  48. musts = append(musts, entNameQuery)
  49. }
  50. //采购单位类型
  51. buyerclass := in.BuyerClass
  52. if len(buyerclass) > 0 {
  53. Buyerclass := `{"terms":{"buyerclass":[`
  54. for k, v := range buyerclass {
  55. if k > 0 {
  56. Buyerclass += `,`
  57. }
  58. Buyerclass += `"` + v + `"`
  59. }
  60. Buyerclass += `]}}`
  61. musts = append(musts, Buyerclass)
  62. }
  63. // 采购单位联系方式 0-不限 1-有联系人 2-无联系人
  64. if in.IsContact != 0 {
  65. isContact := MC.If(in.IsContact == int64(HasContact), true, false)
  66. isContactStr := `{"term":{"is_contact":` + fmt.Sprint(isContact) + `}}`
  67. musts = append(musts, isContactStr)
  68. }
  69. boolsNum := 0
  70. qstr = fmt.Sprintf(query, fmt.Sprintf(`"from":%d,"size": %d,`, (in.PageNum-1)*in.PageSize, in.PageSize), strings.Join(musts, ","), strings.Join(bools, ","), boolsNum)
  71. CountQstr = fmt.Sprintf(query, "", strings.Join(musts, ","), strings.Join(bools, ","), boolsNum)
  72. return
  73. }
  74. // 采购单位补充采购规模、项目数量、招标动态数量
  75. func SupplyDataQuery(buyerList []string) (query string) {
  76. // 查近两年的数据 因为bigmember BuyerMiniPortrait 查的默认是两年
  77. q := `{"size":0,"query": { "bool": { "must": [ {"terms": { "buyer": ["` + strings.Join(buyerList, "\",\"") + `"] }},{"range": {"jgtime": {"gte": %d,"lt": %d} }} ]}}, "aggs": { "buyerBuckets": {"terms": {"field": "buyer"
  78. },"aggs": {"bidAmountCount": {"sum": {"field": "bidamount"}}}}}}`
  79. start, end := getTimeRange()
  80. return fmt.Sprintf(q, start.Unix(), end.Unix())
  81. }
  82. func getTimeRange() (st, et time.Time) {
  83. now := time.Now()
  84. eYear := now.Year()
  85. sYear := now.Year() - 2
  86. //返回默认时间
  87. sTimeStamp := time.Date(sYear, 1, 1, 0, 0, 0, 0, time.Local)
  88. eTimeStamp := time.Date(eYear, now.Month(), now.Day(), now.Hour(), now.Minute(), 0, 0, time.Local)
  89. return sTimeStamp, eTimeStamp
  90. }
  91. const (
  92. P_INDEX = "projectset"
  93. P_TYPE = "projectset"
  94. P_redis_time = 15 * 24 * 60 * 60 //redis存15天
  95. P_redis_key = "buyerListCache_%d_%d" // 按页存缓存
  96. BuyerIndex = "buyer" // 采购单位index
  97. BuyerType = "buyer"
  98. biddingIndex = "bidding"
  99. biddingType = "bidding"
  100. )
  101. // 查询采购单位列表
  102. func GetBuyerList(qstr string, CountQuery string, in *bxbuyer.BuyerListReq, isCache bool) (resp *bxbuyer.BuyerListResp) {
  103. t1 := time.Now()
  104. total := elastic.Count(BuyerIndex, BuyerType, CountQuery) // 总数
  105. fmt.Println("耗时1:", time.Since(t1))
  106. resp = &bxbuyer.BuyerListResp{
  107. Data: &bxbuyer.BuyerData{
  108. Count: total,
  109. List: []*bxbuyer.BuyerList{},
  110. },
  111. }
  112. if total == 0 {
  113. return
  114. }
  115. start := time.Now()
  116. rs := elastic.Get(BuyerIndex, BuyerType, qstr) // 采购单位列表
  117. fmt.Println("采购单位列表 es get查询耗时", time.Since(start))
  118. if rs == nil || len(*rs) == 0 {
  119. return
  120. }
  121. var buyerNames []string
  122. for i := 0; i < len(*rs); i++ {
  123. tmp := &bxbuyer.BuyerList{
  124. Buyer: MC.ObjToString((*rs)[i]["name"]),
  125. Province: MC.ObjToString((*rs)[i]["province"]),
  126. City: MC.ObjToString((*rs)[i]["city"]),
  127. BuyerClass: MC.ObjToString((*rs)[i]["buyerclass"]),
  128. }
  129. buyerNames = append(buyerNames, tmp.Buyer)
  130. resp.Data.List = append(resp.Data.List, tmp)
  131. }
  132. //省份和城市 是否查询已关注信息 是否查询已领取信息
  133. //企业信用库qyxy_std 和es buyer库 查询省份和城市
  134. //客户领取
  135. t2 := time.Now()
  136. isRws := map[string]string{}
  137. if in.IsCheckReceive {
  138. isRws = IsReceived(buyerNames, in.EntUserId)
  139. }
  140. fmt.Println("客户领取耗时:", time.Since(t2))
  141. //客户关注
  142. t3 := time.Now()
  143. isFws := map[string]bool{}
  144. if in.IsCheckFollow {
  145. isFws = IsFollowd(buyerNames, in.UserId)
  146. }
  147. fmt.Println("采购单位关注耗时:", time.Since(t3))
  148. for _, bv := range resp.Data.List {
  149. if in.IsCheckReceive {
  150. if isRws[bv.Buyer] != "" {
  151. bv.IsReceived = true
  152. bv.RecId = isRws[bv.Buyer]
  153. }
  154. if isFws[bv.Buyer] {
  155. bv.IsFollowed = true
  156. }
  157. }
  158. }
  159. fmt.Println("耗时;", time.Since(t1).Seconds(), "秒--", time.Since(t1).Microseconds())
  160. return
  161. }
  162. // 补充字段 项目数量 历史联系人数量 采购单位规模
  163. func SupplyBuyerListData(resp *bxbuyer.BuyerListResp) {
  164. start := time.Now()
  165. // buyerList
  166. buyerList := []string{}
  167. for i := 0; i < len(resp.Data.List); i++ {
  168. buyerList = append(buyerList, resp.Data.List[i].Buyer)
  169. }
  170. t1 := time.Now()
  171. query := SupplyDataQuery(buyerList) // 项目数量、采购规模
  172. // 聚合查
  173. aggs := GetAggs(P_INDEX, P_TYPE, query)
  174. logx.Info("查询语句:", query)
  175. fmt.Println("项目数量采购规模查询耗时:", time.Since(t1))
  176. type BuyerAggStruct struct {
  177. Buckets []struct {
  178. Key string `json:"key,omitempty"`
  179. Doc_count int64 `json:"doc_count,omitempty"`
  180. BidAmountCount struct {
  181. Value float32 `json:"value,omitempty"`
  182. } `json:"bidAmountCount"`
  183. } `json:"buckets"`
  184. }
  185. var buyerBuckets = BuyerAggStruct{}
  186. type supplyDataStruct struct {
  187. ProjectCount int64
  188. BidCount int64
  189. BidAmountCount float32
  190. }
  191. // 处理成map 用于后面格式化数据
  192. buyerMap := map[string]supplyDataStruct{}
  193. if aggs != nil && aggs["buyerBuckets"] != nil {
  194. bs, err := aggs["buyerBuckets"].MarshalJSON()
  195. if err != nil {
  196. resp.ErrCode = -1
  197. resp.ErrMsg = "获取数据异常"
  198. } else {
  199. if len(bs) == 0 {
  200. resp.ErrMsg = "暂无数据"
  201. } else {
  202. err := json.Unmarshal(bs, &buyerBuckets)
  203. logx.Info(err)
  204. if len(buyerBuckets.Buckets) > 0 {
  205. for _, v := range buyerBuckets.Buckets {
  206. buyerMap[v.Key] = supplyDataStruct{
  207. BidAmountCount: v.BidAmountCount.Value,
  208. ProjectCount: v.Doc_count,
  209. }
  210. }
  211. }
  212. }
  213. }
  214. }
  215. ch := make(chan int, 10)
  216. ch2 := make(chan int, 10)
  217. wg := &sync.WaitGroup{}
  218. for i := 0; i < len(resp.Data.List); i++ {
  219. buyer := resp.Data.List[i].Buyer
  220. // 补充字段
  221. if supplyData, ok := buyerMap[buyer]; ok {
  222. resp.Data.List[i].BidAmountCount = supplyData.BidAmountCount
  223. resp.Data.List[i].ProjectCount = supplyData.ProjectCount
  224. }
  225. ch2 <- 1
  226. wg.Add(1)
  227. go func(list *bxbuyer.BuyerList, buyer string) {
  228. defer func() {
  229. <-ch2
  230. wg.Done()
  231. }()
  232. list.ContactCount = GetProjectContactCount(buyer) // 补充联系人字段
  233. }(resp.Data.List[i], buyer)
  234. ch <- 1
  235. wg.Add(1)
  236. go func(list *bxbuyer.BuyerList, buyer string) {
  237. defer func() {
  238. <-ch
  239. wg.Done()
  240. }()
  241. list.BiddingCount = GetNewBiddingCount(buyer)
  242. }(resp.Data.List[i], buyer)
  243. }
  244. wg.Wait()
  245. fmt.Println("SupplyBuyerListData 整体耗时:", time.Since(start))
  246. }
  247. func GetProjectContactCount(buyerName string) int64 {
  248. start := time.Now()
  249. list := []string{}
  250. var musts []string
  251. if buyerName != "" {
  252. musts = append(musts, fmt.Sprintf(`{"term":{"buyer":"%s"}}`, buyerName))
  253. }
  254. if musts == nil || len(musts) == 0 {
  255. return 0
  256. }
  257. searchSql := fmt.Sprintf(`{"query":{"bool":{"must":[%s]}},"_source":["list"],"sort":[{"zbtime":"desc"}],"size":500}`, strings.Join(musts, ","))
  258. projectList := elastic.Get(P_INDEX, P_TYPE, searchSql)
  259. logx.Info("GetProjectContactCount esget 耗时", time.Since(start), searchSql)
  260. if projectList == nil || len(*projectList) == 0 {
  261. return 0
  262. }
  263. //根据联系人和联系方式展示
  264. //多个项目同一个联系人;只展示最新项目
  265. //一个项目多个联系人;拆分展示
  266. repeatContacts := map[string]bool{}
  267. for _, rowData := range *projectList {
  268. mapList, ok := rowData["list"].([]interface{})
  269. if !ok || len(mapList) == 0 {
  270. continue
  271. }
  272. for i := len(mapList) - 1; i >= 0; i-- {
  273. thisMsg, ok := mapList[i].(map[string]interface{})
  274. if !ok || len(thisMsg) == 0 {
  275. continue
  276. }
  277. thisPhone, thisPerson := "", ""
  278. if buyerName != "" {
  279. if thisPhone, _ = thisMsg["buyertel"].(string); thisPhone != "" {
  280. thisPerson, _ = thisMsg["buyerperson"].(string)
  281. }
  282. }
  283. if thisPhone == "" { //联系人为空则不展示 dev4.7.3联系人为空展示此记录
  284. continue
  285. }
  286. //一个项目只选取一条公告联系人
  287. thisAddPerson := false
  288. //名字中多个联系人拆分
  289. for _, name := range strings.Split(thisPerson, ",") {
  290. thisName := strings.TrimSpace(name)
  291. if thisName == "" && thisAddPerson { //联系人为空则不展示
  292. continue
  293. }
  294. thisAddPerson = true
  295. repeatKey := fmt.Sprintf("%s_%s", thisName, thisPhone)
  296. if repeatContacts[repeatKey] {
  297. continue
  298. }
  299. repeatContacts[repeatKey] = true
  300. list = append(list, thisPhone)
  301. }
  302. if thisAddPerson {
  303. break
  304. }
  305. }
  306. }
  307. fmt.Println("GetProjectContactCount 单次耗时:", time.Since(start))
  308. return int64(len(list))
  309. }
  310. func GetNewBiddingCount(buyer string) int64 {
  311. start := time.Now()
  312. if buyer == "" {
  313. return 0
  314. }
  315. var mustQuery []string
  316. st, et := getTimeRange()
  317. mustQuery = append(mustQuery, fmt.Sprintf(`{"range":{"publishtime":{"gte":%d,"lte":%d}}}`, st.Unix(), et.Unix()))
  318. mustQuery = append(mustQuery, fmt.Sprintf(`{"term": {"buyer": "%s"}}`, buyer))
  319. aa := fmt.Sprintf(`{"query":{"bool":{"must":[%s]}}}`, strings.Join(mustQuery, ","))
  320. count := elastic.Count(biddingIndex, biddingType, aa)
  321. fmt.Println("GetNewBiddingCount 单次耗时:", time.Since(start))
  322. return count
  323. }
  324. // 聚合查询
  325. func GetAggs(index, itype, query string) (aggs map[string]json.RawMessage) {
  326. aggs, _, _ = elastic.GetAggs(index, itype, query)
  327. return
  328. }
  329. type buyerInfo struct {
  330. Province string
  331. City string
  332. }
  333. // 潜在客户 获取省份和城市
  334. func GetBuyerInfo(buyerNames []string) (infoMap map[string]buyerInfo) {
  335. var buyerInfoQuery = `{"query": {"bool": {"must": [{"terms": {"%s": [%s]}}],"must_not": [],"should": []}},"from": 0,"size": 50,"sort": []}`
  336. query := fmt.Sprintf(buyerInfoQuery, "buyer_name", `"`+strings.Join(buyerNames, `","`)+`"`)
  337. list := *elastic.Get("buyer", "buyer", query)
  338. if list != nil {
  339. if len(list) > 0 {
  340. infoMap = map[string]buyerInfo{}
  341. for _, v := range list {
  342. infoMap[v["name"].(string)] = buyerInfo{
  343. Province: MC.If(v["province"] != nil, MC.ObjToString(v["province"]), "").(string),
  344. City: MC.If(v["city"] != nil, MC.ObjToString(v["city"]), "").(string),
  345. }
  346. }
  347. }
  348. } else {
  349. logx.Info("采购单位获取地区信息异常")
  350. }
  351. return
  352. }
  353. var fc = "follow_customer" //关注客户表
  354. // 采购单位是否作为客户已被关注
  355. func IsFollowd(buyerNames []string, userId string) (isFws map[string]bool) {
  356. queryMap := map[string]interface{}{
  357. "userId": userId,
  358. "name": map[string]interface{}{
  359. "$in": buyerNames,
  360. },
  361. }
  362. list, ok := IC.Mgo.Find(fc, queryMap, `{"_id":1}`, nil, false, -1, -1)
  363. if ok && len(*list) > 0 {
  364. isFws = map[string]bool{}
  365. for _, lv := range *list {
  366. if MC.ObjToString(lv["name"]) != "" {
  367. isFws[MC.ObjToString(lv["name"])] = true
  368. }
  369. }
  370. } else {
  371. logx.Info("采购单位是否已关注信息异常 or 未查到数据")
  372. }
  373. return
  374. }
  375. var (
  376. Entniche_customer = "entniche_customer"
  377. Entniche_user_customer = "entniche_user_customer"
  378. )
  379. // 领取状态
  380. func IsReceived(buyerNames []string, entUserId string) (isRws map[string]string) {
  381. //新加领取的客户id----保证领取的唯一性
  382. aa := fmt.Sprintf("SELECT ecn.id, ecn.name FROM %s ecn,%s euu WHERE ecn.id = euu.customer_id AND euu.user_id =? AND ecn.`name` IN ('%s') AND (euu.source_type =1 or euu.source_type=4)", Entniche_customer, Entniche_user_customer, strings.Join(buyerNames, "','"))
  383. log.Println(aa)
  384. receInfos := IC.MainMysql.SelectBySql(fmt.Sprintf("SELECT ecn.id, ecn.name FROM %s ecn,%s euu WHERE ecn.id = euu.customer_id AND euu.user_id =? AND ecn.`name` IN ('%s') AND (euu.source_type =1 or euu.source_type=4)", Entniche_customer, Entniche_user_customer, strings.Join(buyerNames, "','")), entUserId)
  385. if receInfos != nil {
  386. if len(*receInfos) > 0 {
  387. isRws = map[string]string{}
  388. for _, rv := range *receInfos {
  389. if MC.ObjToString(rv["name"]) != "" && strconv.Itoa(MC.IntAll(rv["id"])) != "" {
  390. isRws[MC.ObjToString(rv["name"])] = encrypt.SE.Encode2HexByCheck(strconv.Itoa(MC.IntAll(rv["id"])))
  391. }
  392. }
  393. }
  394. } else {
  395. logx.Info("采购单位是否已领取信息异常")
  396. }
  397. return
  398. }
  399. // 是否为空请求
  400. func CheckEmpty(in *bxbuyer.BuyerListReq) bool {
  401. if in.BuyerName == "" && len(in.BuyerClass) == 0 && len(in.Province) == 0 && len(in.City) == 0 && in.IsContact == 0 {
  402. return true
  403. }
  404. return false
  405. }
  406. //缓存数据查询
  407. // 获取采购单位查询query
  408. func BuyerListRedisCacheQuery(pageNum, pageSize int64) (qstr string, cstr string) {
  409. query := `{"from":%d,"size":%d,"query":{"bool":{"must":[%s],"must_not": [{"term": {"buyer_name": ""}}],"should":[],"minimum_should_match": %d}}}`
  410. queryCount := `{"query":{"bool":{"must":[%s],"must_not": [{"term": {"buyer": ""}}],"should":[],"minimum_should_match": %d}}}`
  411. //21.1.20 为和画像保持一致 数据组要求 budget 改成 bidamount
  412. musts := []string{}
  413. boolsNum := 0
  414. entcustomerClass := `{"terms":{"buyer_name":[`
  415. for k, v := range IC.C.DefaultBuyerNames {
  416. if k > 0 {
  417. entcustomerClass += `,`
  418. }
  419. entcustomerClass += `"` + v + `"`
  420. }
  421. entcustomerClass += `]}}`
  422. musts = append(musts, entcustomerClass)
  423. qstr = fmt.Sprintf(query, (pageNum-1)*pageSize, pageSize, strings.Join(musts, ","), boolsNum)
  424. cstr = fmt.Sprintf(queryCount, strings.Join(musts, ","), boolsNum)
  425. logx.Info("qstr:", qstr)
  426. return
  427. }