|
@@ -1,227 +1,24 @@
|
|
|
-/**
|
|
|
- *招标,中标公告的推送消息,在兴趣词分析上,存在一定的难度
|
|
|
- *现在采用DFA算法分析,兴趣词会构造Map结构,前期应该不会出问题。
|
|
|
- *后期需要改进(用户量达到5千以上)
|
|
|
- *可行策略:每日产生的新数据有限,大概不超过5000条,可以把标题全加到内存
|
|
|
- * 用户兴趣词分批次加入,每次加入3000个左右的用户的兴趣词,过滤
|
|
|
- * 今日产生的招标、中标数据,并完成推送,第二批载入后3000个用户的数据
|
|
|
- */
|
|
|
package tender
|
|
|
|
|
|
import (
|
|
|
- "container/list"
|
|
|
- "fmt"
|
|
|
- "log"
|
|
|
- "math/rand"
|
|
|
"qfw/push"
|
|
|
- "qfw/push/dfa"
|
|
|
+ "qfw/push/dopush"
|
|
|
"qfw/util"
|
|
|
- "qfw/util/mongodb"
|
|
|
- qrpc "qfw/util/rpc"
|
|
|
- "runtime"
|
|
|
- "strconv"
|
|
|
- "strings"
|
|
|
"time"
|
|
|
)
|
|
|
|
|
|
type TenderPushJob struct {
|
|
|
- dfa *dfa.DFA
|
|
|
}
|
|
|
|
|
|
-//构造用户兴趣词库
|
|
|
-func (b *TenderPushJob) createUserInterestWord() {
|
|
|
- b.dfa = &dfa.DFA{}
|
|
|
- words := make([]string, 0)
|
|
|
- for k, _ := range push.Cache {
|
|
|
- words = append(words, k)
|
|
|
- }
|
|
|
- b.dfa.AddWord(words...)
|
|
|
-}
|
|
|
-
|
|
|
-var TITLEA string = "[招标信息]" //tender
|
|
|
var MaxPushSize int
|
|
|
|
|
|
-//遍历查到的
|
|
|
-func (b *TenderPushJob) eachAllBidInfo() {
|
|
|
- defer func() {
|
|
|
- if r := recover(); r != nil {
|
|
|
- fmt.Println("推送开始[E]", r)
|
|
|
- }
|
|
|
- }()
|
|
|
- session := mongodb.GetMgoConn()
|
|
|
- defer mongodb.DestoryMongoConn(session)
|
|
|
- startTime := push.PushConfig["tenderStartTime"].(string)
|
|
|
- st, _ := time.ParseInLocation(util.Date_Full_Layout, startTime, time.Local)
|
|
|
- q := `{"comeintime":{"$gt":` + fmt.Sprintf("%d", st.Unix()) + `},"type":"tender"}`
|
|
|
- n, _ := session.DB("qfw").C("bidding").Find(mongodb.ObjToOth(q)).Count()
|
|
|
- log.Println(q, "查询推送信息:", n, ",时间", startTime)
|
|
|
- if n == 0 {
|
|
|
- //没有查询到结果
|
|
|
- return
|
|
|
- }
|
|
|
- query := session.DB("qfw").C("bidding").Find(mongodb.ObjToOth(q)).Sort("-publishtime").Iter()
|
|
|
- userMap := &map[*push.MemberInterest]*list.List{}
|
|
|
- for tmp := new(map[string]interface{}); query.Next(tmp); {
|
|
|
- title := util.ObjToString((*tmp)["title"])
|
|
|
- if title != "" {
|
|
|
- //返回匹配到的词组
|
|
|
- res := b.dfa.Analy(title)
|
|
|
- if len(res) > 0 {
|
|
|
- province := (*tmp)["area"].(string)
|
|
|
- provinceVal := push.GetChoiceCode(province)
|
|
|
- for _, v := range res {
|
|
|
- //根据关键词返回用户指针
|
|
|
- tw := push.Cache[v]
|
|
|
- if tw != nil {
|
|
|
- //遍历用户加入到此条信息上
|
|
|
- for _, v2 := range *tw {
|
|
|
- if v2.Province == "A" || v2.ProvinceVal&provinceVal > 0 {
|
|
|
- s := (*userMap)[v2]
|
|
|
- if s == nil {
|
|
|
- s = list.New()
|
|
|
- (*userMap)[v2] = s
|
|
|
- }
|
|
|
- s.PushBack(tmp)
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- tmp = new(map[string]interface{})
|
|
|
- }
|
|
|
- now := time.Now()
|
|
|
- nowtime := util.FormatDate(&now, util.Date_Full_Layout)
|
|
|
- //更改开始时间
|
|
|
- push.PushConfig["tenderStartTime"] = nowtime
|
|
|
- //date := util.FormatDate(&now, util.Date_Short_Layout)
|
|
|
- for k, v := range *(userMap) {
|
|
|
- kk := *k
|
|
|
- vv := *v
|
|
|
-
|
|
|
- time.Sleep(50 * time.Millisecond)
|
|
|
- go send(&kk, &vv, now, nowtime)
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-func send(k *push.MemberInterest, v *list.List, now time.Time, nowtime string) {
|
|
|
- defer func() {
|
|
|
- if r := recover(); r != nil {
|
|
|
- log.Println("[E]", r)
|
|
|
- for skip := 1; ; skip++ {
|
|
|
- _, file, line, ok := runtime.Caller(skip)
|
|
|
- if !ok {
|
|
|
- break
|
|
|
- }
|
|
|
- go log.Printf("%v,%v\n", file, line)
|
|
|
- }
|
|
|
- }
|
|
|
- }()
|
|
|
-
|
|
|
- //wxstr := ""
|
|
|
- str := fmt.Sprintf("<div>根据您设置的关键词(%s),给您推送以下信息:</div>", strings.Join(k.Interest, ";"))
|
|
|
- //发送内容组合
|
|
|
- i := 0
|
|
|
- lastInfoDate := int64(0)
|
|
|
- firstTitle := ""
|
|
|
- publishTimes := map[string]interface{}{}
|
|
|
- for ks := v.Front(); ks != nil; ks = ks.Next() {
|
|
|
- k2 := *(ks.Value.(*map[string]interface{}))
|
|
|
- i++
|
|
|
- if i == 1 {
|
|
|
- firstTitle = strings.Replace(k2["title"].(string), "\n", "", -1)
|
|
|
- lastInfoDate = k2["publishtime"].(int64)
|
|
|
- }
|
|
|
- str += "<div class='tslist'><span class='xh'>" + fmt.Sprintf("%d", i) + ".</span><a class='bt' target='_blank' href='" + k2["href"].(string) + "'>" + strings.Replace(k2["title"].(string), "\n", "", -1) + "</a></div>"
|
|
|
- publishTimes[strconv.Itoa(i)] = k2["publishtime"]
|
|
|
- if i >= MaxPushSize {
|
|
|
- //限制最大信息条数
|
|
|
- break
|
|
|
- }
|
|
|
-
|
|
|
- }
|
|
|
- TITLE := TITLEA + fmt.Sprintf("%d条,关键词(%s)", i, strings.Join(k.Interest, ";"))
|
|
|
- WXTitle := fmt.Sprintf("%s《%s》%s", func() string {
|
|
|
- minute := now.Unix() - lastInfoDate
|
|
|
- if minute > -1 && minute < 61 {
|
|
|
- return fmt.Sprintf("%d秒前发布的", minute)
|
|
|
- } else {
|
|
|
- minute = minute / 60
|
|
|
- if minute > 121 {
|
|
|
- return ""
|
|
|
- } else {
|
|
|
- if minute < 1 {
|
|
|
- minute = 1
|
|
|
- }
|
|
|
- return fmt.Sprintf("%d分钟前发布的", minute)
|
|
|
- }
|
|
|
- }
|
|
|
- }(), firstTitle, func() string {
|
|
|
- if i == 1 {
|
|
|
- return ""
|
|
|
- } else {
|
|
|
- return "等" + strconv.Itoa(i) + "条"
|
|
|
- }
|
|
|
- }())
|
|
|
-
|
|
|
- //3、发送微信
|
|
|
- if len(k.Openid) > 0 {
|
|
|
- sendWeixin(k, TITLE, str, nowtime, "", now, "", WXTitle, publishTimes)
|
|
|
- }
|
|
|
-
|
|
|
-}
|
|
|
-
|
|
|
-//推送微信
|
|
|
-func sendWeixin(k *push.MemberInterest, TITLE, str, nowtime, wxstr string, now time.Time, msgid, WXTitle string, publishTimes map[string]interface{}) {
|
|
|
- defer func() {
|
|
|
- if r := recover(); r != nil {
|
|
|
- fmt.Println("发送微信[E]", r)
|
|
|
- }
|
|
|
- }()
|
|
|
- time.Sleep(time.Millisecond * 100)
|
|
|
- //详情存库
|
|
|
- wxpush := map[string]interface{}{
|
|
|
- "s_m_openid": k.Openid,
|
|
|
- "l_date": now.Unix(),
|
|
|
- "s_words": k.Interest,
|
|
|
- "s_email": k.Email,
|
|
|
- "s_uid": k.Id,
|
|
|
- "s_province": k.Province,
|
|
|
- "a_interest": k.Interest,
|
|
|
- "s_content": str,
|
|
|
- "s_type": "tender",
|
|
|
- "a_publishtime": publishTimes,
|
|
|
- "i_size": len(publishTimes),
|
|
|
- }
|
|
|
- wid := mongodb.Save("wxpush", &wxpush)
|
|
|
- wxDate := ""
|
|
|
- if k.InterestDate > 0 {
|
|
|
- mt1 := interface{}(k.InterestDate)
|
|
|
- wxDate = util.FormatDateWithObj(&mt1, util.Date_Full_Layout)
|
|
|
- } else {
|
|
|
- r := rand.New(rand.NewSource(time.Now().UnixNano()))
|
|
|
- n1 := r.Int63n(9)
|
|
|
- n2 := r.Int63n(7200)
|
|
|
- wxDate = time.Now().Local().Add(time.Duration(-n2) * time.Second).Add(time.Duration(-n1*24) * time.Hour).Format(util.Date_Full_Layout)
|
|
|
- }
|
|
|
-
|
|
|
- wxstr = "\n点击下方“详情”查看详细信息。\n以上招标信息,是剑鱼根据关键字“" + strings.Join(k.Interest, ";") + "”奋力查找并推送,如不合您心意,请猛戳企明星菜单“会员服务—剑鱼”进行修改。"
|
|
|
- push.SendWinXin(&qrpc.NotifyMsg{
|
|
|
- Openid: k.Openid,
|
|
|
- Title: push.PushConfig["tenderTitle"].(string),
|
|
|
- Remark: wxstr,
|
|
|
- Detail: WXTitle,
|
|
|
- Date: wxDate,
|
|
|
- Service: "剑鱼君",
|
|
|
- Url: push.PushConfig["bidViewDomain"].(string) + "/wxpush/bid/" + k.Openid + "/" + wid + "/aa"})
|
|
|
-
|
|
|
-}
|
|
|
-
|
|
|
-//执行日常招标中标公告的消息推送
|
|
|
+//执行日常招标的消息推送
|
|
|
func (b *TenderPushJob) Execute() bool {
|
|
|
- log.Println("开始执行招标任务:", push.PushConfig["tenderStartTime"])
|
|
|
- push.InitCache("tender")
|
|
|
- b.createUserInterestWord()
|
|
|
- b.eachAllBidInfo()
|
|
|
- return false
|
|
|
+ pj := dopush.Pjob{
|
|
|
+ MaxPushSize: MaxPushSize,
|
|
|
+ Stype: "tender",
|
|
|
+ StypeName: "招标",
|
|
|
+ }
|
|
|
+ st, _ := time.ParseInLocation(util.Date_Full_Layout, push.PushConfig["tenderStartTime"].(string), time.Local)
|
|
|
+ return pj.DoPush("", push.PushConfig["tenderStartTime"].(string), 1, st.Unix())
|
|
|
}
|