|
@@ -3,24 +3,31 @@ package front
|
|
|
import (
|
|
|
qu "app.yhyue.com/moapp/jybase/common"
|
|
|
"app.yhyue.com/moapp/jybase/encrypt"
|
|
|
+ elastic "app.yhyue.com/moapp/jybase/es"
|
|
|
"app.yhyue.com/moapp/jybase/redis"
|
|
|
+ "app.yhyue.com/moapp/jypkg/common/src/qfw/util/bidsearch"
|
|
|
"app.yhyue.com/moapp/jypkg/public"
|
|
|
+ "context"
|
|
|
+ "encoding/json"
|
|
|
"fmt"
|
|
|
"github.com/pkg/errors"
|
|
|
"jy/src/jfw/modules/app/src/app/jyutil"
|
|
|
"log"
|
|
|
+ "reflect"
|
|
|
"strconv"
|
|
|
"strings"
|
|
|
"sync"
|
|
|
+ "time"
|
|
|
)
|
|
|
|
|
|
// 最后与tags合并后 删除
|
|
|
var (
|
|
|
- cacheTime = 1200 // * 60 * 60
|
|
|
+ cacheTime = 12 * 60 //* 60
|
|
|
mobileHref = "/jyapp/tags/%s/%s"
|
|
|
infoTypeRedisKey = "mobile_seo_infoType_home"
|
|
|
areaRedisKey = "mobile_seo_area_home"
|
|
|
hotAreaRedisKey = "mobile_seo_hot_area"
|
|
|
+ biddingListKey = "mobile_seo_list_%s_%s_%d"
|
|
|
industryRedisKey = "mobile_seo_industry"
|
|
|
industryLessRedisKey = "mobile_seo_industry_less"
|
|
|
buyerListRedisKey = "mobile_seo_buyer_list_%d"
|
|
@@ -28,9 +35,8 @@ var (
|
|
|
buyerListLimit = 5000
|
|
|
winnerListLimit = 5000
|
|
|
listPageSize = 25
|
|
|
-
|
|
|
- hotArea = "北京,山东,陕西,河南,广东"
|
|
|
- label = map[string]string{
|
|
|
+ hotArea = "北京,山东,陕西,河南,广东"
|
|
|
+ label = map[string]string{
|
|
|
"area": "area",
|
|
|
"infoType": "it",
|
|
|
"industry": "indu",
|
|
@@ -143,9 +149,16 @@ var (
|
|
|
adCode = "mobile_seo_ad"
|
|
|
areaLock = sync.Mutex{}
|
|
|
infoTypeLock = sync.Mutex{}
|
|
|
+ infoListLock = sync.Mutex{}
|
|
|
total = 5000
|
|
|
+ reqLimitInit *reqLimit
|
|
|
)
|
|
|
|
|
|
+type reqLimit struct {
|
|
|
+ doPool chan struct{}
|
|
|
+ waitPool chan struct{}
|
|
|
+}
|
|
|
+
|
|
|
// 获取广告位
|
|
|
func (tg *Tags) GetAdInfo() (adInfo []jyutil.AdInfo) {
|
|
|
obj := redis.Get("seoCache", "ad_"+adCode)
|
|
@@ -182,8 +195,8 @@ func (tg *Tags) GetInfoType() (list []map[string]interface{}, topMap map[string]
|
|
|
if data != nil && len(data) > 0 {
|
|
|
infoTypeLock.Lock()
|
|
|
defer infoTypeLock.Unlock()
|
|
|
+ topMap = map[string]*AreaInfo{}
|
|
|
for _, v := range data {
|
|
|
- topMap = map[string]*AreaInfo{}
|
|
|
list = append(list, map[string]interface{}{
|
|
|
"name": qu.ObjToString(v["name"]),
|
|
|
"url:": fmt.Sprintf(mobileHref, label["infoType"], combine(2, qu.Int64All(v["id"]), 1, 0)),
|
|
@@ -230,8 +243,9 @@ func combine(num int, pid, id, pageNum int64) string {
|
|
|
}
|
|
|
|
|
|
type AreaInfo struct {
|
|
|
- name string
|
|
|
- url string
|
|
|
+ name string
|
|
|
+ url string
|
|
|
+ nickname string
|
|
|
}
|
|
|
|
|
|
// 获取所有地区信息
|
|
@@ -297,17 +311,6 @@ func (tg *Tags) GetAllArea() (areaList []map[string][]*AreaInfo, areaToCode map[
|
|
|
return
|
|
|
}
|
|
|
|
|
|
-type BiddingInfo struct {
|
|
|
- Area string
|
|
|
- City string
|
|
|
- TopType string
|
|
|
- Industry string
|
|
|
- Keys string
|
|
|
- IsBuyer bool
|
|
|
- IsWinner bool
|
|
|
- Total int
|
|
|
-}
|
|
|
-
|
|
|
// GetIndustry 获取行业和行业下标的物
|
|
|
func GetIndustry(industryHref string) (interface{}, interface{}) {
|
|
|
rediskey := fmt.Sprintf(industryRedisKey)
|
|
@@ -374,7 +377,7 @@ func IsInArr(arr []string, s string) bool {
|
|
|
}
|
|
|
|
|
|
// 获取行业分类下的标签
|
|
|
-func GetInsturyKeywords(idstr string) (name, class_1, class_2 string) {
|
|
|
+func GetIndustryKeywords(idstr string) (name, class_1, class_2 string) {
|
|
|
id, _ := strconv.Atoi(idstr)
|
|
|
data := public.BaseMysql.SelectBySql(`select id,class_1,class_2,name from seo_words.seo_industry where id =?`, id)
|
|
|
if data != nil && len(*data) > 0 {
|
|
@@ -382,10 +385,286 @@ func GetInsturyKeywords(idstr string) (name, class_1, class_2 string) {
|
|
|
class_1 = qu.ObjToString(v["class_1"])
|
|
|
name = qu.ObjToString(v["name"])
|
|
|
class_2 = qu.ObjToString(v["class_2"])
|
|
|
- return name, class_1, class_2
|
|
|
+ return
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return
|
|
|
+}
|
|
|
+
|
|
|
+type BiddingInfo struct {
|
|
|
+ Area string
|
|
|
+ City string
|
|
|
+ TopType string
|
|
|
+ Industry string
|
|
|
+ Keys string
|
|
|
+ IsBuyer bool
|
|
|
+ IsWinner bool
|
|
|
+ Total int
|
|
|
+ PageNum int
|
|
|
+ PageSize int
|
|
|
+}
|
|
|
+
|
|
|
+var (
|
|
|
+ biddingType = []*AreaInfo{
|
|
|
+ {
|
|
|
+ name: "招标预告",
|
|
|
+ url: fmt.Sprintf(mobileHref, label["infoType"], combine(2, 2, 1, 0)),
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "招标公告",
|
|
|
+ url: fmt.Sprintf(mobileHref, label["infoType"], combine(2, 3, 1, 0)),
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "招标结果",
|
|
|
+ url: fmt.Sprintf(mobileHref, label["infoType"], combine(2, 4, 1, 0)),
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "招标信用信息",
|
|
|
+ url: fmt.Sprintf(mobileHref, label["infoType"], combine(2, 5, 1, 0)),
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "拟建项目",
|
|
|
+ nickname: "拟建",
|
|
|
+ url: fmt.Sprintf(mobileHref, label["infoType"], combine(2, 1, 1, 0)),
|
|
|
+ },
|
|
|
+ }
|
|
|
+)
|
|
|
+
|
|
|
+func (b *BiddingInfo) GetSearchSql() string {
|
|
|
+ var (
|
|
|
+ field = `"_id","title","publishtime","toptype","subtype","area","s_subscopeclass","buyerclass","budget","bidamount","isValidFile","spidercode","site"`
|
|
|
+ query = `{"query":{"bool":{"filter":[%s],"should": [%s],"minimum_should_match": %d}},"_source":[%s],"sort":[{"publishtime":"desc"}],"size":%d}`
|
|
|
+ keywords = `{"multi_match": {"query": "%s","type": "phrase", "fields": ["title","purchasing"]}}`
|
|
|
+ filterBool = `{"bool":{"must":[%s],"should":[%s],"minimum_should_match": %d}}`
|
|
|
+ filterMusts, filterShould, should, filter []string
|
|
|
+ filterRange = `{"range":{"publishtime":{"gte":%d,"lte":%d}}}`
|
|
|
+ now = time.Now()
|
|
|
+ )
|
|
|
+ filter = append(filter, fmt.Sprintf(filterRange, now.AddDate(-5, 0, 0).Unix(), now.Unix()))
|
|
|
+ if b.Area != "" || b.City != "" {
|
|
|
+ if b.Area != "" {
|
|
|
+ filterShould = append(filterShould, fmt.Sprintf(`{"terms":{"area":["%s"]}}`, b.Area))
|
|
|
+ }
|
|
|
+ if b.City != "" {
|
|
|
+ filterShould = append(filterShould, fmt.Sprintf(`{"terms":{"city":["%s"]}}`, b.City))
|
|
|
+ }
|
|
|
+ }
|
|
|
+ //信息类型
|
|
|
+ if b.TopType != "" {
|
|
|
+ filterMusts = append(filterMusts, fmt.Sprintf(`{"terms":{"toptype":["%s"]}}`, b.TopType))
|
|
|
+ }
|
|
|
+ //行业
|
|
|
+ if b.Industry != "" {
|
|
|
+ filterMusts = append(filterMusts, fmt.Sprintf(`{"terms":{"s_subscopeclass":["%s"]}}`, b.Industry))
|
|
|
+ }
|
|
|
+ if len(filterMusts) > 0 || len(filterShould) > 0 {
|
|
|
+ shouldMatch := 0
|
|
|
+ if len(filterShould) > 0 {
|
|
|
+ shouldMatch = 1
|
|
|
+ }
|
|
|
+ filter = append(filter, fmt.Sprintf(filterBool, strings.Join(filterMusts, ","), strings.Join(filterShould, ","), shouldMatch))
|
|
|
+ }
|
|
|
+ //标的物
|
|
|
+ if b.Keys != "" {
|
|
|
+ should = append(should, fmt.Sprintf(keywords, b.Keys))
|
|
|
+ }
|
|
|
+ queryShouldMatch := 0
|
|
|
+ if len(should) > 0 {
|
|
|
+ queryShouldMatch = 1
|
|
|
+ }
|
|
|
+ qStr := fmt.Sprintf(query, strings.Join(filter, ","), strings.Join(should, ","), queryShouldMatch, field, b.Total)
|
|
|
+ return qStr
|
|
|
+}
|
|
|
+
|
|
|
+type BiddingInfoRes struct {
|
|
|
+ Name string
|
|
|
+ Url string
|
|
|
+ List []*BiddingList
|
|
|
+}
|
|
|
+type BiddingList struct {
|
|
|
+ Title string
|
|
|
+ Id string
|
|
|
+ Area string
|
|
|
+ Industry string
|
|
|
+ Subtype string
|
|
|
+ FileExists bool
|
|
|
+ Site string
|
|
|
+ Price string
|
|
|
+}
|
|
|
+
|
|
|
+func (b *BiddingInfo) GetBiddingInfo(types, name string, currentLocation []*AreaInfo) (birt []*BiddingInfoRes) {
|
|
|
+ infoListLock.Lock()
|
|
|
+ defer infoListLock.Unlock()
|
|
|
+ if n := strings.Split(name, "_"); len(n) > 0 {
|
|
|
+ name = strings.Join(n[:len(n)-1], "_")
|
|
|
+ }
|
|
|
+ redisKey := fmt.Sprintf(biddingListKey, types, name, b.PageNum)
|
|
|
+ if b, err := redis.GetBytes("seoCache", redisKey); err == nil && len(*b) > 0 {
|
|
|
+ if err = json.Unmarshal(*b, &birt); err != nil {
|
|
|
+ log.Fatal(err)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if len(birt) == 0 {
|
|
|
+ if flag := reqLimitInit.Limit(context.Background()); flag == 1 {
|
|
|
+ defer reqLimitInit.Release()
|
|
|
+ } else {
|
|
|
+ if flag == -2 {
|
|
|
+ log.Println("等待队列已满")
|
|
|
+ } else if flag == -1 {
|
|
|
+ log.Println("等待超时")
|
|
|
+ }
|
|
|
+ return nil
|
|
|
+ }
|
|
|
+ if b.Total == b.PageSize {
|
|
|
+ for _, bidType := range biddingType {
|
|
|
+ if bidsearch.TopType[bidType.name] != "" {
|
|
|
+ b.TopType = bidsearch.TopType[bidType.name]
|
|
|
+ } else {
|
|
|
+ b.TopType = bidType.nickname
|
|
|
+ }
|
|
|
+ biddingInfoRes := &BiddingInfoRes{
|
|
|
+ Name: bidType.name,
|
|
|
+ Url: bidType.url,
|
|
|
+ }
|
|
|
+ _, data := elastic.GetWithCount("bidding", "bidding", "", b.GetSearchSql())
|
|
|
+ if data != nil {
|
|
|
+ biddingInfoRes.List = b.DataFormat(*data)
|
|
|
+ birt = append(birt, biddingInfoRes)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ b, err := json.Marshal(birt)
|
|
|
+ if err == nil && len(b) > 0 {
|
|
|
+ if err := redis.PutBytes("seoCache", redisKey, &b, cacheTime); err != nil {
|
|
|
+ log.Fatal(err)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ if len(currentLocation) > 0 {
|
|
|
+ count, data := elastic.GetWithCount("bidding", "bidding", "", b.GetSearchSql())
|
|
|
+ if count > 0 && data != nil {
|
|
|
+ if b.Total > int(count) {
|
|
|
+ b.Total = int(count)
|
|
|
+ }
|
|
|
+ formatData := b.DataFormat(*data)
|
|
|
+ for i := 0; i < (b.Total+b.PageSize-1)/b.PageSize; i++ {
|
|
|
+ var (
|
|
|
+ bir []*BiddingInfoRes
|
|
|
+ start = i * b.PageSize
|
|
|
+ end = (i + 1) * b.PageSize
|
|
|
+ )
|
|
|
+ if end > int(count) {
|
|
|
+ end = int(count)
|
|
|
+ }
|
|
|
+ bir = []*BiddingInfoRes{
|
|
|
+ {
|
|
|
+ Name: currentLocation[0].name,
|
|
|
+ Url: "", //currentLocation[0].url
|
|
|
+ List: formatData[start:end],
|
|
|
+ },
|
|
|
+ }
|
|
|
+ if i == b.PageNum-1 {
|
|
|
+ birt = bir
|
|
|
+ }
|
|
|
+ if len(bir) == 1 {
|
|
|
+ b, err := json.Marshal(bir)
|
|
|
+ if err == nil && len(b) > 0 {
|
|
|
+ if err = redis.PutBytes("seoCache", fmt.Sprintf(biddingListKey, types, name, i+1), &b, cacheTime); err != nil {
|
|
|
+ log.Fatal(err)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return
|
|
|
+}
|
|
|
+
|
|
|
+func (b *BiddingInfo) DataFormat(data []map[string]interface{}) (bList []*BiddingList) {
|
|
|
+ if len(data) > 0 {
|
|
|
+ for _, v := range data {
|
|
|
+ bl := &BiddingList{
|
|
|
+ Title: qu.ObjToString(v["title"]),
|
|
|
+ Id: encrypt.CommonEncodeArticle("content", qu.ObjToString(v["_id"])),
|
|
|
+ Area: qu.ObjToString(v["area"]),
|
|
|
+ Subtype: qu.ObjToString(v["subtype"]),
|
|
|
+ Site: qu.ObjToString(v["site"]),
|
|
|
+ }
|
|
|
+ if subs := qu.InterfaceToStr(v["s_subscopeclass"]); subs != "" {
|
|
|
+ bl.Industry = strings.Split(strings.Split(subs, ",")[0], "_")[0]
|
|
|
+ }
|
|
|
+ if isValidFile, _ := v["isValidFile"].(bool); isValidFile {
|
|
|
+ bl.FileExists = true
|
|
|
+ }
|
|
|
+ if v["budget"] != nil {
|
|
|
+ bl.Price = ConversionMoney(v["budget"])
|
|
|
+ } else if v["bidamount"] != nil {
|
|
|
+ bl.Price = ConversionMoney(v["bidamount"])
|
|
|
+ }
|
|
|
+ bList = append(bList, bl)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return
|
|
|
+}
|
|
|
+
|
|
|
+// 金额转化 金额:0-万元以下单位为元 ,万元以上至亿元以下单位为万元 ,亿元以上单位为亿元。保留 小数点后 2 位,不进行四舍五入。
|
|
|
+func ConversionMoney(i_money interface{}) string {
|
|
|
+ m := ""
|
|
|
+ if reflect.TypeOf(i_money).Name() == "float64" {
|
|
|
+ m = strconv.FormatFloat(qu.Float64All(i_money), 'f', -1, 64)
|
|
|
+ } else {
|
|
|
+ m = qu.ObjToString(i_money)
|
|
|
+ }
|
|
|
+ if m == "" {
|
|
|
+ return m
|
|
|
+ }
|
|
|
+ m_arr := strings.Split(m, ".")
|
|
|
+ m_1 := m_arr[0]
|
|
|
+ len_m1 := len([]rune(m_1))
|
|
|
+ if len_m1 >= 9 {
|
|
|
+ m = m_1[0:len_m1-8] + "." + m_1[len_m1-8:len_m1-6] + "亿元"
|
|
|
+ } else if len_m1 >= 5 {
|
|
|
+ m = m_1[0:len_m1-4] + "." + m_1[len_m1-4:len_m1-2] + "万元"
|
|
|
+ } else {
|
|
|
+ if len(m_arr) == 1 {
|
|
|
+ return m + ".00元"
|
|
|
+ }
|
|
|
+ m_2 := m_arr[1]
|
|
|
+ if len([]rune(m_2)) > 1 {
|
|
|
+ m_2 = m_2[0:2]
|
|
|
+ } else {
|
|
|
+ m_2 = m_2[0:1] + "0"
|
|
|
+ }
|
|
|
+ m = m_1 + "." + m_2 + "元"
|
|
|
+ }
|
|
|
+ return m
|
|
|
+}
|
|
|
+
|
|
|
+// -2 等待池已满
|
|
|
+// -1 超时
|
|
|
+// 1:可以执行查询
|
|
|
+func (r *reqLimit) Limit(ctx context.Context) int {
|
|
|
+ ctx, cancel := context.WithTimeout(ctx, 3*time.Second)
|
|
|
+ defer cancel()
|
|
|
+ select {
|
|
|
+ case <-r.waitPool:
|
|
|
+ defer func() {
|
|
|
+ r.waitPool <- struct{}{}
|
|
|
+ }()
|
|
|
+ select {
|
|
|
+ case <-r.doPool:
|
|
|
+ return 1
|
|
|
+ case <-ctx.Done(): //超时
|
|
|
+ return -1
|
|
|
}
|
|
|
+ default:
|
|
|
+ return -2
|
|
|
}
|
|
|
- return "", "", ""
|
|
|
+}
|
|
|
+
|
|
|
+func (r *reqLimit) Release() {
|
|
|
+ r.doPool <- struct{}{}
|
|
|
}
|
|
|
|
|
|
// PutListCache
|