|
@@ -0,0 +1,867 @@
|
|
|
+package jyutil
|
|
|
+
|
|
|
+import (
|
|
|
+ qu "app.yhyue.com/moapp/jybase/common"
|
|
|
+ "app.yhyue.com/moapp/jybase/date"
|
|
|
+ "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"
|
|
|
+ "jy/src/jfw/modules/app/src/jfw/config"
|
|
|
+ "log"
|
|
|
+ "math/rand"
|
|
|
+ "reflect"
|
|
|
+ "strconv"
|
|
|
+ "strings"
|
|
|
+ "sync"
|
|
|
+ "time"
|
|
|
+)
|
|
|
+
|
|
|
+var (
|
|
|
+ mobileHref = "/list/%s/%s.html"
|
|
|
+ infoTypeRedisKey = "mobile_seo_infoType_home"
|
|
|
+ biddingListKey = "mobile_seo_list_%s_%s_%d"
|
|
|
+ biddingListTotalKey = "mobile_seo_list_total_%s_%s"
|
|
|
+ industryRedisKey = "mobile_seo_industry"
|
|
|
+ buyerListRedisKey = "mobile_seo_buyer_list_%d"
|
|
|
+ winnerListRedisKey = "mobile_seo_winner_list_%d"
|
|
|
+ HotArea = "北京,山东,陕西,河南,广东"
|
|
|
+ AdCode = "mobile_seo_ad"
|
|
|
+ randCount = 600
|
|
|
+ Label = map[string]string{
|
|
|
+ "area": "area",
|
|
|
+ "city": "city",
|
|
|
+ "infoType": "stype",
|
|
|
+ "industry": "industry",
|
|
|
+ "buyer": "buyer",
|
|
|
+ "winner": "winner",
|
|
|
+ "home": "home",
|
|
|
+ }
|
|
|
+ areaLock = &sync.Mutex{}
|
|
|
+ infoTypeLock = &sync.Mutex{}
|
|
|
+ infoListLock = &sync.Mutex{}
|
|
|
+ AreaInfo []map[string][]*SeoInfo
|
|
|
+ ReqLimitInit *ReqLimit
|
|
|
+ RandNum *rand.Rand
|
|
|
+ CodeToInfoType = map[int64]string{
|
|
|
+ 2: "ZBYG",
|
|
|
+ 3: "ZBGG",
|
|
|
+ 4: "ZBJG",
|
|
|
+ 5: "ZBXYXX",
|
|
|
+ 1: "NJXM",
|
|
|
+ }
|
|
|
+ SXSMap = map[string]string{}
|
|
|
+ defaultTitle = "剑鱼标讯官网-招标信息查询_全国招标采购信息订阅推送_专业招标大数据服务平台"
|
|
|
+ defaultKeywords = "招标网站,招标信息查询,剑鱼标讯,招标采购信息,招标大数据平台"
|
|
|
+ defaultDescription = "剑鱼标讯是国内专业的招标大数据服务平台,专注于全国招标采购信息的搜索查询、订阅推送和数据定制化服务。提供涵盖拟在建项目、招标预告、招标公告、中标公告、政府采购、企业工商信息等多种信息类型,帮助企业全方位掌握市场动态变化。招投标大数据平台就用剑鱼标讯。"
|
|
|
+)
|
|
|
+
|
|
|
+type tkd struct {
|
|
|
+ NAME string
|
|
|
+ TITLE string
|
|
|
+ KEYWORDS string
|
|
|
+ DESCRIPTION string
|
|
|
+ PARENT string
|
|
|
+}
|
|
|
+type ReqLimit struct {
|
|
|
+ DoPool chan struct{}
|
|
|
+ WaitPool chan struct{}
|
|
|
+}
|
|
|
+
|
|
|
+// GetAdInfo 获取广告位
|
|
|
+func GetAdInfo(host string) (adInfo []AdInfo) {
|
|
|
+ obj := redis.Get("other", "ad_"+AdCode)
|
|
|
+ if obj != nil {
|
|
|
+ adInfo = Handle(obj.([]interface{}), host)
|
|
|
+ } else {
|
|
|
+ res, ok := public.MQFW.FindOneByField("ad", `{"s_code":"`+AdCode+`"}`, `{"a_son":1}`)
|
|
|
+ if ok && res != nil && (*res)["a_son"] != nil {
|
|
|
+ son := (*res)["a_son"].([]interface{})
|
|
|
+ if len(son) > 0 {
|
|
|
+ redis.Put("seoCache", "ad_"+AdCode, son, qu.IntAll(config.Seoconfig["cacheTime"])+RandNum.Intn(randCount))
|
|
|
+ }
|
|
|
+ adInfo = Handle(son, host)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return
|
|
|
+}
|
|
|
+
|
|
|
+// GetInfoType 获取全部信息类型
|
|
|
+func GetInfoType() (list []map[string]interface{}, topMap map[string]*SeoInfo) {
|
|
|
+ infoTypeLock.Lock()
|
|
|
+ defer infoTypeLock.Unlock()
|
|
|
+ var (
|
|
|
+ data []map[string]interface{}
|
|
|
+ )
|
|
|
+ if l, ok := redis.Get("seoCache", infoTypeRedisKey).([]interface{}); ok && l != nil && len(l) > 0 {
|
|
|
+ data = qu.ObjArrToMapArr(l)
|
|
|
+ } else {
|
|
|
+ data = *public.BaseMysql.SelectBySql(`select id,name,pid from seo_words.seo_stype where name != "采购意向" and pid=0 order by id`)
|
|
|
+ if data != nil && len(data) > 0 {
|
|
|
+ if b := redis.Put("seoCache", infoTypeRedisKey, data, qu.IntAll(config.Seoconfig["cacheTime"])+RandNum.Intn(randCount)); !b {
|
|
|
+ log.Println("缓存保存失败,key:", infoTypeRedisKey)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if data != nil && len(data) > 0 {
|
|
|
+ topMap = map[string]*SeoInfo{}
|
|
|
+ for _, v := range data {
|
|
|
+ topMap[fmt.Sprintf("%d", qu.Int64All(v["id"]))] = &SeoInfo{
|
|
|
+ Name: qu.ObjToString(v["name"]),
|
|
|
+ Url: fmt.Sprintf(mobileHref, Label["infoType"], CodeToInfoType[qu.Int64All(v["id"])]),
|
|
|
+ }
|
|
|
+ if qu.ObjToString(v["name"]) == "拟建项目" {
|
|
|
+ continue
|
|
|
+ }
|
|
|
+ list = append(list, map[string]interface{}{
|
|
|
+ "name": qu.ObjToString(v["name"]),
|
|
|
+ "url": fmt.Sprintf(mobileHref, Label["infoType"], CodeToInfoType[qu.Int64All(v["id"])]),
|
|
|
+ })
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return
|
|
|
+}
|
|
|
+
|
|
|
+// GetHotArea 获取热门地区
|
|
|
+func GetHotArea() {
|
|
|
+ if HotArea != "" {
|
|
|
+ for _, v := range strings.Split(HotArea, ",") {
|
|
|
+ HotAreas = append(HotAreas, &SeoInfo{
|
|
|
+ Name: v,
|
|
|
+ Url: AreaToUrl[v],
|
|
|
+ })
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+func combine(num int, pid, id, pageNum int64) string {
|
|
|
+ switch num {
|
|
|
+ case 2:
|
|
|
+ return fmt.Sprintf("%d_%d", pid, id)
|
|
|
+ case 3:
|
|
|
+ return fmt.Sprintf("%d_%d_%d", pid, id, pageNum)
|
|
|
+ default:
|
|
|
+ return fmt.Sprintf("%d", pid)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+type SeoInfo struct {
|
|
|
+ Name string
|
|
|
+ Url string
|
|
|
+ Nickname string
|
|
|
+}
|
|
|
+
|
|
|
+// GetAllArea 获取所有地区信息
|
|
|
+func GetAllArea() {
|
|
|
+ areaLock.Lock()
|
|
|
+ defer areaLock.Unlock()
|
|
|
+ ///list/area/HB.html
|
|
|
+ if AreaMap != nil && CityMap != nil {
|
|
|
+ for ak, am := range AreaMap {
|
|
|
+ AreaCityToCode[ak] = &SeoInfo{
|
|
|
+ Name: am.NAME,
|
|
|
+ Url: fmt.Sprintf(mobileHref, Label["area"], ak),
|
|
|
+ }
|
|
|
+ AreaToUrl[am.NAME] = fmt.Sprintf(mobileHref, Label["area"], ak)
|
|
|
+ }
|
|
|
+ for ck, cm := range CityMap {
|
|
|
+ AreaCityToCode[ck] = &SeoInfo{
|
|
|
+ Name: cm.NAME,
|
|
|
+ Url: fmt.Sprintf(mobileHref, Label["area"], ck),
|
|
|
+ }
|
|
|
+ if strings.Contains(ck, "_") {
|
|
|
+ cityCode := strings.Split(ck, "_")[0]
|
|
|
+ if cm.PARENT != "" {
|
|
|
+ cityCode = cm.PARENT
|
|
|
+ SXSMap[ck] = cm.PARENT
|
|
|
+ }
|
|
|
+ AreaToCity[cityCode] = append(AreaToCity[cityCode], &SeoInfo{
|
|
|
+ Name: cm.NAME,
|
|
|
+ Url: fmt.Sprintf(mobileHref, Label["city"], ck),
|
|
|
+ })
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if AreaToUrl != nil {
|
|
|
+ L:
|
|
|
+ for _, av := range AreaInfo {
|
|
|
+ for _, avv := range av {
|
|
|
+ for _, an := range avv {
|
|
|
+ if an.Url != "" {
|
|
|
+ break L
|
|
|
+ }
|
|
|
+ an.Url = AreaToUrl[an.Name]
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return
|
|
|
+}
|
|
|
+
|
|
|
+type IndustryStruct struct {
|
|
|
+ Name string
|
|
|
+ Url string
|
|
|
+ List []IndustryStruct
|
|
|
+}
|
|
|
+
|
|
|
+// GetIndustry 获取行业和行业下标的物
|
|
|
+func GetIndustry(industryHref string) *IndustryStruct {
|
|
|
+ saveData := IndustryStruct{
|
|
|
+ Name: "招标导航",
|
|
|
+ Url: "/tags/industry/all.html",
|
|
|
+ }
|
|
|
+ redisKey := fmt.Sprintf(industryRedisKey)
|
|
|
+ if l, err := GetIndustryCache(redisKey); l != nil && (l.List != nil) && len(l.List) > 0 && err == nil {
|
|
|
+ return l
|
|
|
+ } else {
|
|
|
+ data := public.BaseMysql.SelectBySql(`select a.id,a.name,b.id class_id,b.name class_1 from seo_words.seo_industry a inner join seo_words.seo_industry_class b on a.class_1=b.name and a.class_2 !='药品' order by a.class_1`)
|
|
|
+ industryArr := []string{}
|
|
|
+ industryMap := map[string][]IndustryStruct{}
|
|
|
+ if len(*data) > 0 && data != nil {
|
|
|
+ for _, v := range *data {
|
|
|
+ class := qu.ObjToString(v["class_1"])
|
|
|
+ name := qu.ObjToString(v["name"])
|
|
|
+ id := qu.Int64All(v["id"])
|
|
|
+ industryId := qu.Int64All(v["class_id"])
|
|
|
+ if !IsInArr(industryArr, class) {
|
|
|
+ industryArr = append(industryArr, class)
|
|
|
+ }
|
|
|
+ industryMap[class] = append(industryMap[class], IndustryStruct{
|
|
|
+ Name: name,
|
|
|
+ Url: fmt.Sprintf(industryHref, industryId, id),
|
|
|
+ })
|
|
|
+ }
|
|
|
+ }
|
|
|
+ var m []IndustryStruct
|
|
|
+ for _, v := range industryArr {
|
|
|
+ m = append(m, IndustryStruct{
|
|
|
+ Name: v,
|
|
|
+ List: industryMap[v],
|
|
|
+ })
|
|
|
+ }
|
|
|
+ saveData.List = m
|
|
|
+ PutIndustryCache(redisKey, qu.IntAll(config.Seoconfig["cacheTime"]), &saveData)
|
|
|
+ return &saveData
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// IsInArr 判断字符串是否再数组str内
|
|
|
+func IsInArr(arr []string, s string) bool {
|
|
|
+ for _, v := range arr {
|
|
|
+ if v == s {
|
|
|
+ return true
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return false
|
|
|
+}
|
|
|
+
|
|
|
+// GetIndustryKeywords 获取行业分类下的标签
|
|
|
+func GetIndustryKeywords(idstr string) (name, class1, class2 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 {
|
|
|
+ for _, v := range *data {
|
|
|
+ class1 = qu.ObjToString(v["class_1"])
|
|
|
+ name = qu.ObjToString(v["name"])
|
|
|
+ class2 = qu.ObjToString(v["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
|
|
|
+ IsSearch bool
|
|
|
+ IsEffective bool //是否是有效请求
|
|
|
+}
|
|
|
+
|
|
|
+var (
|
|
|
+ biddingType = []*SeoInfo{
|
|
|
+ {
|
|
|
+ Name: "招标预告",
|
|
|
+ Url: fmt.Sprintf(mobileHref, Label["infoType"], CodeToInfoType[2]),
|
|
|
+ },
|
|
|
+ {
|
|
|
+ Name: "招标公告",
|
|
|
+ Url: fmt.Sprintf(mobileHref, Label["infoType"], CodeToInfoType[3]),
|
|
|
+ },
|
|
|
+ {
|
|
|
+ Name: "招标结果",
|
|
|
+ Url: fmt.Sprintf(mobileHref, Label["infoType"], CodeToInfoType[4]),
|
|
|
+ },
|
|
|
+ {
|
|
|
+ Name: "招标信用信息",
|
|
|
+ Url: fmt.Sprintf(mobileHref, Label["infoType"], CodeToInfoType[5]),
|
|
|
+ },
|
|
|
+ {
|
|
|
+ Name: "拟建项目",
|
|
|
+ Url: fmt.Sprintf(mobileHref, Label["infoType"], CodeToInfoType[1]),
|
|
|
+ Nickname: "拟建",
|
|
|
+ },
|
|
|
+ }
|
|
|
+ AreaMap = map[string]tkd{}
|
|
|
+ CityMap = map[string]tkd{}
|
|
|
+ AreaCityToCode = map[string]*SeoInfo{}
|
|
|
+ AreaToCity = map[string][]*SeoInfo{}
|
|
|
+ AreaToUrl = map[string]string{}
|
|
|
+ HotAreas []*SeoInfo
|
|
|
+)
|
|
|
+
|
|
|
+func (b *BiddingInfo) PageFormat(page string) {
|
|
|
+ if page != "" {
|
|
|
+ b.PageNum, _ = strconv.Atoi(page)
|
|
|
+ if b.PageNum <= 0 || b.PageNum > b.Total/b.PageSize {
|
|
|
+ b.IsEffective = false
|
|
|
+ b.PageNum = 1
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+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.City != "" {
|
|
|
+ filterShould = append(filterShould, fmt.Sprintf(`{"terms":{"city":["%s"]}}`, b.City))
|
|
|
+ } else if b.Area != "" {
|
|
|
+ filterShould = append(filterShould, fmt.Sprintf(`{"terms":{"area":["%s"]}}`, b.Area))
|
|
|
+ }
|
|
|
+ }
|
|
|
+ //信息类型
|
|
|
+ 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 InfoRes struct {
|
|
|
+ Name string
|
|
|
+ Url string
|
|
|
+ List []*InfoList
|
|
|
+}
|
|
|
+type InfoList struct {
|
|
|
+ Title string
|
|
|
+ Url string
|
|
|
+ Area string
|
|
|
+ Industry string
|
|
|
+ Subtype string
|
|
|
+ FileExists bool
|
|
|
+ Site string //来自用户发版
|
|
|
+ Price string
|
|
|
+ PublishTime string
|
|
|
+ BuyerClass string //采购单位类型
|
|
|
+ LegalPerson string //企业法人
|
|
|
+ State string //中标企业状态
|
|
|
+ Address string //地址
|
|
|
+}
|
|
|
+
|
|
|
+func (b *BiddingInfo) GetBiddingInfo(types string, codes []string, currentLocation []SeoInfo, isPaging bool) (birt []*InfoRes) {
|
|
|
+ infoListLock.Lock()
|
|
|
+ defer infoListLock.Unlock()
|
|
|
+ name := strings.Join(codes, "")
|
|
|
+ if n := len(codes); n > 1 {
|
|
|
+ name = strings.Join(codes, "_")
|
|
|
+ if isPaging {
|
|
|
+ name = strings.Join(codes[:n-1], "_")
|
|
|
+ }
|
|
|
+ }
|
|
|
+ redisCount := 0
|
|
|
+ redisKey := fmt.Sprintf(biddingListKey, types, name, b.PageNum)
|
|
|
+ redisTotalKey := fmt.Sprintf(biddingListTotalKey, types, name)
|
|
|
+ if b, err := redis.GetBytes("seoCache", redisKey); err == nil && len(*b) > 0 {
|
|
|
+ if err = json.Unmarshal(*b, &birt); err != nil {
|
|
|
+ log.Println(err)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if redisCountByte, err := redis.GetBytes("seoCache", redisTotalKey); err == nil && len(*redisCountByte) > 0 {
|
|
|
+ if err = json.Unmarshal(*redisCountByte, &redisCount); err != nil {
|
|
|
+ log.Println(err)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if len(birt) == 0 || ((b.Total != b.PageSize) && redisCount == 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 {
|
|
|
+ bidsearch.TopType[bidType.Name] = bidType.Nickname
|
|
|
+ b.TopType = bidType.Nickname
|
|
|
+ }
|
|
|
+ biddingInfoRes := &InfoRes{
|
|
|
+ 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)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if b.IsWinner && b.IsBuyer {
|
|
|
+ //birt = append(birt, &InfoRes{
|
|
|
+ // Name: "采购单位",
|
|
|
+ // Url: "/jyapp/tags/buyer/1.html",
|
|
|
+ // List: b.GetEntInfoList(b.PageSize, 0),
|
|
|
+ //})
|
|
|
+ //birt = append(birt, &InfoRes{
|
|
|
+ // Name: "中标企业",
|
|
|
+ // Url: "/jyapp/tags/winner/1.html",
|
|
|
+ // List: b.GetEntInfoList(b.PageSize, 1),
|
|
|
+ //})
|
|
|
+ }
|
|
|
+ b, err := json.Marshal(birt)
|
|
|
+ if err == nil && len(b) > 0 {
|
|
|
+ if err := redis.PutBytes("seoCache", redisKey, &b, qu.IntAll(config.Seoconfig["biddingCacheTime"])+RandNum.Intn(randCount)); err != nil {
|
|
|
+ log.Println(err)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ if len(currentLocation) > 0 {
|
|
|
+ redisCacheTime := qu.IntAll(config.Seoconfig["biddingCacheTime"])
|
|
|
+ start := time.Now()
|
|
|
+ if b.Keys != "" { // 标的物 一天更新一次
|
|
|
+ redisCacheTime = qu.IntAll(config.Seoconfig["cacheTime"])
|
|
|
+ }
|
|
|
+ count, data := elastic.GetWithCount("bidding", "bidding", "", b.GetSearchSql())
|
|
|
+ fmt.Println("GetWithCount", time.Since(start))
|
|
|
+ fmt.Println(" b.GetSearchSql()", 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 []*InfoRes
|
|
|
+ start = i * b.PageSize
|
|
|
+ end = (i + 1) * b.PageSize
|
|
|
+ )
|
|
|
+ if end > int(count) {
|
|
|
+ end = int(count)
|
|
|
+ }
|
|
|
+ bir = []*InfoRes{
|
|
|
+ {
|
|
|
+ Name: fmt.Sprintf("%s%s", currentLocation[0].Name, qu.If(types == "industry" && name != "all", "信息列表", "").(string)),
|
|
|
+ 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, redisCacheTime+RandNum.Intn(randCount)); err != nil {
|
|
|
+ log.Println(err)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ countByte, err := json.Marshal(b.Total)
|
|
|
+ if err == nil && len(countByte) > 0 {
|
|
|
+ if err = redis.PutBytes("seoCache", fmt.Sprintf(biddingListTotalKey, types, name), &countByte, redisCacheTime+RandNum.Intn(randCount)); err != nil {
|
|
|
+ log.Println("存缓存失败:", fmt.Sprintf(biddingListTotalKey, types, name), b.Total)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ if b.Total != b.PageSize {
|
|
|
+ b.Total = redisCount
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return
|
|
|
+}
|
|
|
+
|
|
|
+func (b *BiddingInfo) DataFormat(data []map[string]interface{}) (bList []*InfoList) {
|
|
|
+ if len(data) > 0 {
|
|
|
+ for _, v := range data {
|
|
|
+ bl := &InfoList{
|
|
|
+ Title: qu.ObjToString(v["title"]),
|
|
|
+ Url: fmt.Sprintf("/nologin/content/%s.html", encrypt.CommonEncodeArticle("content", qu.ObjToString(v["_id"]))),
|
|
|
+ Area: qu.ObjToString(v["area"]),
|
|
|
+ Subtype: qu.ObjToString(v["subtype"]),
|
|
|
+ Site: qu.If(qu.ObjToString(v["site"]) == "剑鱼信息发布平台", "用户发布", "").(string),
|
|
|
+ }
|
|
|
+ publishTime := v["publishtime"]
|
|
|
+ bl.PublishTime = date.FormatDateWithObj(&publishTime, date.Date_Short_Layout)
|
|
|
+ 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 {
|
|
|
+ if i_money == nil {
|
|
|
+ return ""
|
|
|
+ }
|
|
|
+ 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
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+func (r *ReqLimit) Release() {
|
|
|
+ r.DoPool <- struct{}{}
|
|
|
+}
|
|
|
+
|
|
|
+// PutListCache
|
|
|
+func PutListCache(redisKey string, redisTimeout int, list []*InfoList) {
|
|
|
+ b, err := json.Marshal(list)
|
|
|
+ if err == nil && len(b) > 0 {
|
|
|
+ if err := redis.PutBytes("seoCache", redisKey, &b, redisTimeout+RandNum.Intn(randCount)); err != nil {
|
|
|
+ log.Printf("1保存缓存 redis 异常 key:%s", redisKey)
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ log.Printf("2保存缓存 redis 异常 key:%s", redisKey)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// GetEntInfoList 采购单位列表/企业列表
|
|
|
+// pageNum:页码
|
|
|
+// identityType :0 采购单位 1 企业
|
|
|
+func (b *BiddingInfo) GetEntInfoList(limit, identityType int) (list []*InfoList) {
|
|
|
+ var (
|
|
|
+ redisKeyNum = ""
|
|
|
+ redisKey = ""
|
|
|
+ pageNum = b.PageNum
|
|
|
+ cachePage int
|
|
|
+ )
|
|
|
+ if b.IsBuyer && b.IsWinner {
|
|
|
+ pageNum = 0
|
|
|
+ cachePage--
|
|
|
+ }
|
|
|
+ var rs *[]map[string]interface{}
|
|
|
+ switch identityType {
|
|
|
+ case 0:
|
|
|
+ redisKeyNum = fmt.Sprintf(buyerListRedisKey, pageNum)
|
|
|
+ redisKey = buyerListRedisKey
|
|
|
+ case 1:
|
|
|
+ redisKeyNum = fmt.Sprintf(winnerListRedisKey, pageNum)
|
|
|
+ redisKey = winnerListRedisKey
|
|
|
+ }
|
|
|
+ //查缓存
|
|
|
+ if data, err := GetListCache(redisKeyNum); err == nil && len(data) > 0 {
|
|
|
+ return data
|
|
|
+ }
|
|
|
+ // 查数据
|
|
|
+ start := time.Now()
|
|
|
+ switch identityType {
|
|
|
+ case 0:
|
|
|
+ rs = selectBuyerInfo(limit)
|
|
|
+ case 1:
|
|
|
+ rs = selectEntInfo(limit)
|
|
|
+ }
|
|
|
+ fmt.Println(identityType, "查询耗时", time.Since(start))
|
|
|
+ if rs == nil || len(*rs) == 0 {
|
|
|
+ return []*InfoList{}
|
|
|
+ }
|
|
|
+ var (
|
|
|
+ cacheList []*InfoList
|
|
|
+ )
|
|
|
+ for i := 0; i < len(*rs); i++ {
|
|
|
+ (*rs)[i]["id"] = encrypt.EncodeArticleId2ByCheck(qu.ObjToString((*rs)[i]["id"]))
|
|
|
+ infoList := &InfoList{Title: qu.ObjToString((*rs)[i]["name"])}
|
|
|
+ switch identityType {
|
|
|
+ case 0:
|
|
|
+ infoList.Url = fmt.Sprintf("/jyapp/big/page/unit_portrayal?entName=%s", qu.ObjToString((*rs)[i]["name"]))
|
|
|
+ infoList.Area = fmt.Sprintf("%s %s", qu.InterfaceToStr((*rs)[i]["province"]), qu.InterfaceToStr((*rs)[i]["city"]))
|
|
|
+ infoList.BuyerClass = qu.InterfaceToStr((*rs)[i]["buyerclass"])
|
|
|
+ case 1:
|
|
|
+ infoList.Url = fmt.Sprintf("/jyapp/big/page/ent_portrait?eId=%s", (*rs)[i]["id"])
|
|
|
+ capital := qu.Int64All((*rs)[i]["capital"])
|
|
|
+ if capital != 0 {
|
|
|
+ infoList.Price = ConversionMoney(float64(capital * 10000))
|
|
|
+ }
|
|
|
+ infoList.LegalPerson = qu.InterfaceToStr((*rs)[i]["legal_person"])
|
|
|
+ infoList.Address = qu.InterfaceToStr((*rs)[i]["address"])
|
|
|
+ if state := qu.InterfaceToStr((*rs)[i]["company_status"]); state != "" {
|
|
|
+ infoList.State = strings.Split(state, "(")[0]
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if i >= (b.PageNum-1)*b.PageSize && i < b.PageNum*b.PageSize {
|
|
|
+ list = append(list, infoList)
|
|
|
+ }
|
|
|
+ cacheList = append(cacheList, infoList)
|
|
|
+ if len(cacheList) == b.PageSize || i == len(*rs)-1 {
|
|
|
+ cachePage++
|
|
|
+ PutListCache(fmt.Sprintf(redisKey, cachePage), qu.IntAll(config.Seoconfig["cacheTime"]), cacheList)
|
|
|
+ cacheList = []*InfoList{}
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return list
|
|
|
+}
|
|
|
+
|
|
|
+func GetListCache(redisKey string) (list []*InfoList, err error) {
|
|
|
+ var b *[]byte
|
|
|
+ b, err = redis.GetBytes("seoCache", redisKey)
|
|
|
+ if err != nil {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ err = json.Unmarshal(*b, &list)
|
|
|
+ if err == nil {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ return
|
|
|
+}
|
|
|
+
|
|
|
+// 获取最新的采购单位
|
|
|
+// limit: 数量
|
|
|
+func selectBuyerInfo(limit int) *[]map[string]interface{} {
|
|
|
+ t := time.Now()
|
|
|
+ defer func(time2 time.Time) {
|
|
|
+ fmt.Println("采购=========")
|
|
|
+ fmt.Println("耗时", time.Since(t))
|
|
|
+ }(t)
|
|
|
+ q := `SELECT c.name,c.company_id AS id,b. NAME AS buyerclass,p.area AS province,ca.city AS city
|
|
|
+ FROM (SELECT dfeb.NAME, dfeb.company_id, dfeb.area_code, dfeb.city_code, dfeb.name_id,dfet.labelvalues
|
|
|
+ FROM dws_f_ent_baseinfo dfeb INNER JOIN dws_f_ent_tags dfet
|
|
|
+ ON (dfeb.identity_type &(1 << 0)) > 0 AND dfeb.name_id = dfet.name_id
|
|
|
+ ORDER BY dfeb.latest_time DESC LIMIT ?) c LEFT JOIN code_buyerclass b ON (b. CODE = c.labelvalues)
|
|
|
+ LEFT JOIN code_area p ON (p. CODE = c.area_code) LEFT JOIN code_area ca ON (ca. CODE = c.city_code)`
|
|
|
+ return public.GlobalCommonMysql.SelectBySql(q, limit)
|
|
|
+}
|
|
|
+
|
|
|
+// 获取最新的中标单位
|
|
|
+// limit: 数量
|
|
|
+func selectEntInfo(limit int) *[]map[string]interface{} {
|
|
|
+ t := time.Now()
|
|
|
+ defer func(time2 time.Time) {
|
|
|
+ fmt.Println("中标=========")
|
|
|
+ fmt.Println("耗时", time.Since(t))
|
|
|
+ }(t)
|
|
|
+ q := ` select c. name, c.company_id as id,c.address,cb.legal_person,cb.company_status,cb.capital from ( SELECT
|
|
|
+ name, company_id,address
|
|
|
+ FROM
|
|
|
+ dws_f_ent_baseinfo
|
|
|
+ WHERE company_id!=''and
|
|
|
+ (identity_type & (1 << 1)) > 0 order by latest_time desc
|
|
|
+ LIMIT ?) c left join company_baseinfo cb on(cb.company_id=c.company_id) `
|
|
|
+ return public.GlobalCommonMysql.SelectBySql(q, limit)
|
|
|
+}
|
|
|
+
|
|
|
+// PutIndustryCache 存缓存
|
|
|
+func PutIndustryCache(redisKey string, redisTimeout int, data *IndustryStruct) {
|
|
|
+ b, err := json.Marshal(data)
|
|
|
+ if err != nil {
|
|
|
+ log.Printf("保存缓存 序列化异常,data:%s,err:%s\n", data, err.Error())
|
|
|
+ return
|
|
|
+ }
|
|
|
+ if err = redis.PutBytes("seoCache", redisKey, &b, redisTimeout+RandNum.Intn(randCount)); err != nil {
|
|
|
+ log.Printf("保存缓存 redis 异常,key:%s,err:%s\n", redisKey, err.Error())
|
|
|
+ }
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+// GetIndustryCache 取缓存
|
|
|
+func GetIndustryCache(redisKey string) (data *IndustryStruct, err error) {
|
|
|
+ redisByte, err := redis.GetBytes("seoCache", redisKey)
|
|
|
+ if err != nil || redisByte == nil || len(*redisByte) == 0 {
|
|
|
+ return data, err
|
|
|
+ }
|
|
|
+ err = json.Unmarshal(*redisByte, &data)
|
|
|
+ if err != nil {
|
|
|
+ log.Println(fmt.Sprintf("读取缓存 序列化异常,err:%s", err.Error()))
|
|
|
+ return data, err
|
|
|
+ }
|
|
|
+ return data, nil
|
|
|
+}
|
|
|
+
|
|
|
+// GetIndustryNav 招标导航 随机展示每个分类下 少于等于5个字的标的物 12个
|
|
|
+func GetIndustryNav(data *IndustryStruct) *IndustryStruct {
|
|
|
+ for i := 0; i < len(data.List); i++ {
|
|
|
+ tmpInduList := data.List[i].List
|
|
|
+ tmpList := []IndustryStruct{}
|
|
|
+ // 处理出每组少于等于5 个字的 标的物名称
|
|
|
+ for j := 0; j < len(tmpInduList); j++ {
|
|
|
+ if len([]rune(tmpInduList[j].Name)) <= 5 {
|
|
|
+ tmpList = append(tmpList, tmpInduList[j])
|
|
|
+ }
|
|
|
+ }
|
|
|
+ var randomList []IndustryStruct
|
|
|
+ if len(tmpList) <= 12 {
|
|
|
+ randomList = tmpList
|
|
|
+ } else {
|
|
|
+ // 随机出12个
|
|
|
+ indexMap := map[int]struct{}{}
|
|
|
+ for len(randomList) < 12 {
|
|
|
+ index := RandNum.Intn(len(tmpList))
|
|
|
+ if _, ok := indexMap[index]; !ok {
|
|
|
+ randomList = append(randomList, tmpList[index])
|
|
|
+ indexMap[index] = struct{}{}
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ data.List[i].List = randomList
|
|
|
+ }
|
|
|
+ return data
|
|
|
+}
|
|
|
+
|
|
|
+// 随机数
|
|
|
+func RandomNumberGenerator() *rand.Rand {
|
|
|
+ s1 := rand.NewSource(time.Now().UnixNano())
|
|
|
+ r1 := rand.New(s1)
|
|
|
+ return r1
|
|
|
+}
|
|
|
+
|
|
|
+type TdkStruct struct {
|
|
|
+ Title string
|
|
|
+ Keywords string
|
|
|
+ Description string
|
|
|
+}
|
|
|
+
|
|
|
+func GetTdk(types, name string) (tdk TdkStruct) {
|
|
|
+ title, keywords, description := defaultTitle, defaultKeywords, defaultDescription
|
|
|
+ switch types {
|
|
|
+ case Label["area"], Label["city"]: // 省份页面
|
|
|
+ title = fmt.Sprintf("%s免费招标信息_%s采购服务平台_%s中标公告-剑鱼标讯", name, name, name)
|
|
|
+ keywords = fmt.Sprintf("%s免费招标信息,%s招标公告,%s中标公告,%s招标采购,%s招标网", name, name, name, name, name)
|
|
|
+ description = fmt.Sprintf("剑鱼标讯%s专栏,提供%s免费招标信息、%s招标公告、%s中标公告和%s招标采购等内容,以及%s招标信息搜索查询、订阅推送和数据定制化服务,每天实时更新%s的招标信息,随时随地免费查看。", name, name, name, name, name, name, name)
|
|
|
+
|
|
|
+ case Label["infoType"]: // 信息类型页面
|
|
|
+ stypemp, _ := config.Seoconfig["stype"].(map[string]interface{})
|
|
|
+ if stypemp != nil {
|
|
|
+ stypemp1 := stypemp[name].(map[string]interface{})
|
|
|
+ if stypemp1 != nil {
|
|
|
+ //= stypemp1["NAME"].(string)
|
|
|
+ title = fmt.Sprintf("剑鱼标讯官网_%s", stypemp1["TITLE"].(string))
|
|
|
+ keywords = stypemp1["KEYWORDS"].(string)
|
|
|
+ description = stypemp1["DESCRIPTION"].(string)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ case Label["industry"]: // 行业首页
|
|
|
+ area_name := fmt.Sprintf("全国%s", name)
|
|
|
+ if name == "" {
|
|
|
+ title = fmt.Sprintf("%s招标采购行业大全-剑鱼标讯", area_name)
|
|
|
+ keywords = fmt.Sprintf("%s招标采购分类,%s招标行业分类,%s招标采购行业,%s招标采购信息", area_name, area_name, area_name, area_name)
|
|
|
+ description = "剑鱼标讯提供全国各行各业的招标采购公告、预告、变更、中标等招标分类信息搜索、查看、发布服务。有详细的招标采购信息、政府采购信息、标书制作、招投标课程、企业合作需求等招标分类信息。借助高流量的主站使信息推广变得更加简单,快速,效率。"
|
|
|
+ break
|
|
|
+ }
|
|
|
+ // 行业标的物
|
|
|
+ title = fmt.Sprintf("%s免费招标信息_%s采购服务平台-剑鱼标讯", area_name, name)
|
|
|
+ keywords = fmt.Sprintf("%s免费招标信息,%s招标公告,%s中标公告,%s招标采购,%s招标网", area_name, area_name, area_name, area_name, area_name)
|
|
|
+ description = fmt.Sprintf("剑鱼标讯%s专栏,是%s招标采购的免费信息平台,免费提供%s招标采购信息、招标公告,中标结果等所有%s相关的招投标信息,寻找优质免费%s招标信息网站,就用剑鱼标讯!", area_name, area_name, area_name, area_name, area_name)
|
|
|
+ }
|
|
|
+ tdk = TdkStruct{
|
|
|
+ Title: title,
|
|
|
+ Keywords: keywords,
|
|
|
+ Description: description,
|
|
|
+ }
|
|
|
+ return
|
|
|
+}
|