Bladeren bron

feat:xiugai

wangchuanjin 2 maanden geleden
commit
55b1b32890
6 gewijzigde bestanden met toevoegingen van 482 en 0 verwijderingen
  1. 35 0
      config.yaml
  2. BIN
      fish.png
  3. 428 0
      main.go
  4. 18 0
      main_test.go
  5. 1 0
      task.json
  6. BIN
      thumb.png

+ 35 - 0
config.yaml

@@ -0,0 +1,35 @@
+mediaId: "ojRhKlgIAz6_v1g-w4DRKi60-WFW716AvBoYVa-6OuakSOd2up4jr-IVWiaSlMQe"
+mediaUrl: "http://mmbiz.qpic.cn/mmbiz_png/vww3GXJpnUU0DlqPLibbQysWXlyJne2YJzcaF5pDozG4lTyGicd026OuHLS1ZVJRb95iaibpL8qabJaV7zU6icGurXA/0?wx_fmt=png"
+wxTokenRpc: "172.17.162.29:1166"
+appid: "wxd6ed3bb13dd2282d"
+timeTask: "11:00"
+areas:
+  - 广东
+  - 上海
+mongodb:
+  main:
+    address: "172.17.4.86:27080"
+    size: 5
+    dbName: "jyqykhfw"
+    collection: "f_sourceinfo_2025Sjcs"
+    replSet: ""
+  bidding:
+    address: "172.20.45.128:27080"
+    size: 5
+    dbName: "qfw"
+    replSet: ""
+    userName: "JS2Z_Rbid_ProG"
+    password: "JS2z@S1e3aR5Ch"
+template:
+  zhaoProjectSize: 10
+  zhongProjectSize: 5
+  caiProjectSize: 10
+  purchasingSize: 2
+  winnerSize: 2
+  captureAppend: "<br>……文中所列为采购项目的部分内容,如需完整版,可留言或添加微信咨询!"
+  zbgs:
+    content: "近期%s在%s领域展现出巨大的市场价值,%s等多家企业接连中标。<br>本文将详细介绍项目概况、中标候选信息以及项目建设内容。"
+  zcgs:
+    content: "随着科技的不断进步,数字化建设已成为推动社会经济发展的重要力量。近日,%s相继发布了涵盖%s等多个领域,总预算金额高达%.2f的采购项目,以下是各项目的详细信息:"
+  yxsjgs:
+    content: "%s正积极推进多项信息化建设项目,来提升公共服务和管理效率。本文将介绍近期发布的信息化项目采购意向公告。"

BIN
fish.png


+ 428 - 0
main.go

@@ -0,0 +1,428 @@
+package main
+
+import (
+	. "app.yhyue.com/moapp/jybase/common"
+	. "app.yhyue.com/moapp/jybase/mongodb"
+	"context"
+	"flag"
+	"fmt"
+	"github.com/gogf/gf/v2/container/garray"
+	"github.com/gogf/gf/v2/container/gmap"
+	"github.com/gogf/gf/v2/container/gset"
+	"github.com/gogf/gf/v2/frame/g"
+	"github.com/gogf/gf/v2/os/gctx"
+	"github.com/gogf/gf/v2/os/gtime"
+	"github.com/gogf/gf/v2/text/gstr"
+	"github.com/gogf/gf/v2/util/gconv"
+	"golang.org/x/net/html"
+	"net/rpc"
+	"regexp"
+	"strings"
+	"sync"
+	"time"
+)
+
+var (
+	TaskConfig *taskConfig
+	mgo        *MongodbSim
+	mgoBid     *MongodbSim
+)
+
+type taskConfig struct {
+	LastTime int64
+}
+
+type BidDataItem struct {
+	Zbgs   *BidDataObj //中标公示
+	Zcgs   *BidDataObj //招采公示
+	Yxsjgs *BidDataObj //意向商机公示
+
+}
+
+type BidDataObj struct {
+	Winners        *gset.Set
+	Purchasinglist *gset.Set
+	BidDataDetails *garray.SortedArray
+}
+
+type BidDataDetail struct {
+	ProjectName  string  //项目名称
+	ProjectCode  string  //项目编号
+	Area         string  //项目地区
+	Buyer        string  //招采单位
+	Budget       string  //预算金额
+	Amount       float64 //金额
+	Winner       string  //中标单位
+	Bidamount    string  //中标金额
+	Detail       string  //建设内容
+	Bidendtime   string  //投标截止时间
+	PurchaseTime string  //预计采购时间
+	Projectscope string  //需求概况 projectscope
+}
+
+type Article struct {
+	Title   string
+	Content string
+}
+
+func newBidDataObj() *BidDataObj {
+	return &BidDataObj{Winners: gset.New(true), Purchasinglist: gset.New(true), BidDataDetails: garray.NewSortedArray(func(a, b interface{}) int {
+		if a.(*BidDataDetail).Amount > b.(*BidDataDetail).Amount {
+			return -1
+		} else if a.(*BidDataDetail).Amount < b.(*BidDataDetail).Amount {
+			return 1
+		}
+		return 0
+	}, true)}
+}
+
+func init() {
+	ctx := gctx.New()
+	mgo = &MongodbSim{
+		MongodbAddr: g.Config().MustGet(ctx, "mongodb.main.address").String(),
+		Size:        g.Config().MustGet(ctx, "mongodb.main.size").Int(),
+		DbName:      g.Config().MustGet(ctx, "mongodb.main.dbName").String(),
+		UserName:    g.Config().MustGet(ctx, "mongodb.main.userName").String(),
+		Password:    g.Config().MustGet(ctx, "mongodb.main.password").String(),
+	}
+	mgo.InitPool()
+	mgoBid = &MongodbSim{
+		MongodbAddr: g.Config().MustGet(ctx, "mongodb.bidding.address").String(),
+		Size:        g.Config().MustGet(ctx, "mongodb.bidding.size").Int(),
+		DbName:      g.Config().MustGet(ctx, "mongodb.bidding.dbName").String(),
+		UserName:    g.Config().MustGet(ctx, "mongodb.bidding.userName").String(),
+		Password:    g.Config().MustGet(ctx, "mongodb.bidding.password").String(),
+	}
+	mgoBid.InitPool()
+	ReadConfig("./task.json", &TaskConfig)
+}
+
+func main() {
+	model := flag.Int("m", 0, "1:上传素材 2:非定时任务")
+	startTime := flag.Int64("s", 0, "开始时间")
+	endTime := flag.Int64("e", 0, "结束时间")
+	flag.Parse()
+	//addDraft(0, 1736289976) //f_sourceinfo_2025Jslt_msx
+	//addDraft(1740731400, 1840731400) //f_sourceinfo_2025Sjcs 江苏 西藏
+	//return
+	if *model == 1 {
+		addMaterial()
+	} else if *model == 2 {
+		addDraft(*startTime, *endTime)
+	} else {
+		SimpleCrontab(true, g.Config().MustGet(gctx.New(), "timeTask").String(), func() {
+			et := time.Now().Unix()
+			addDraft(TaskConfig.LastTime, et)
+			TaskConfig.LastTime = et
+			WriteSysConfig("./task.json", *TaskConfig)
+		})
+		<-chan bool(nil)
+	}
+}
+
+func addMaterial() {
+	ctx := gctx.New()
+	var path = "./thumb.png"
+	result, err := g.Client().Post(ctx, "https://api.weixin.qq.com/cgi-bin/material/add_material?access_token="+getAccessToken(ctx)+"&type=image", "media=@file:"+path)
+	if err != nil {
+		g.Log().Error(ctx, err)
+		return
+	}
+	defer result.Close()
+	g.Log().Info(ctx, "新增永久素材请求返回结果", result.ReadAllString())
+}
+
+// 新增草稿箱
+func addDraft(startTime, endTime int64) {
+	ctx := gctx.New()
+	g.Log().Info(ctx, "开始新增草稿箱任务。。。")
+	sess := mgo.GetMgoConn()
+	defer mgo.DestoryMongoConn(sess)
+	it := sess.DB(g.Config().MustGet(ctx, "mongodb.main.dbName").String()).C(g.Config().MustGet(ctx, "mongodb.main.collection").String()).Find(map[string]interface{}{
+		"i_updatetime": map[string]interface{}{
+			"$gte": startTime,
+			"$lt":  endTime,
+		},
+	}).Select(map[string]interface{}{"id": 1, "v_baseinfo": 1}).Iter()
+	all := gmap.New(true)
+	pool := make(chan bool, 5)
+	wait := &sync.WaitGroup{}
+	index := 0
+	areas := map[string]bool{}
+	for _, v := range g.Config().MustGet(ctx, "areas").Strings() {
+		areas[v] = true
+	}
+	for mt := make(map[string]interface{}); it.Next(&mt); {
+		pool <- true
+		wait.Add(1)
+		go func(mn map[string]interface{}) {
+			defer func() {
+				<-pool
+				wait.Done()
+			}()
+			v_baseinfo, _ := mn["v_baseinfo"].(map[string]interface{})
+			area, _ := v_baseinfo["area"].(string)
+			toptype, _ := v_baseinfo["toptype"].(string)
+			subtype, _ := v_baseinfo["subtype"].(string)
+			if !areas[area] || (toptype != "招标" && toptype != "采购意向" && subtype != "中标" && subtype != "成交") {
+				return
+			}
+			var bdi *BidDataItem
+			if all.Get(area) == nil {
+				bdi = &BidDataItem{Zbgs: newBidDataObj(), Zcgs: newBidDataObj(), Yxsjgs: newBidDataObj()}
+				all.Set(area, bdi)
+			} else {
+				bdi = all.Get(area).(*BidDataItem)
+			}
+			var bdo *BidDataObj
+			if toptype == "招标" {
+				bdo = bdi.Zcgs
+			} else if toptype == "结果" {
+				bdo = bdi.Zbgs
+			} else {
+				bdo = bdi.Yxsjgs
+			}
+			purchasinglist, _ := v_baseinfo["purchasinglist"].([]interface{})
+			bidendtime := gconv.Int64(v_baseinfo["bidendtime"])
+			procurementlist, _ := v_baseinfo["procurementlist"].([]interface{})
+			if id := gconv.String(mn["id"]); id != "" && (purchasinglist == nil || len(purchasinglist) == 0 || v_baseinfo["bidendtime"] == nil || procurementlist == nil || len(procurementlist) == 0) {
+				bidding, ok := mgoBid.FindById("bidding", id, `{"purchasinglist":1,"bidendtime":1,"procurementlist":1}`)
+				if ok && bidding != nil && len(*bidding) > 0 {
+					if purchasinglist == nil {
+						purchasinglist, _ = (*bidding)["purchasinglist"].([]interface{})
+					}
+					if bidendtime == 0 {
+						bidendtime = gconv.Int64((*bidding)["bidendtime"])
+					}
+					if procurementlist == nil {
+						procurementlist, _ = (*bidding)["procurementlist"].([]interface{})
+					}
+				}
+			}
+			//
+			if len(purchasinglist) > 0 {
+				firstOne, _ := purchasinglist[0].(map[string]interface{})
+				itemname := gconv.String(firstOne["itemname"])
+				if itemname != "" {
+					bdo.Purchasinglist.Add(itemname)
+				}
+			}
+			//
+			purchaseTime := ""
+			if len(procurementlist) > 0 {
+				firstOne, _ := procurementlist[0].(map[string]interface{})
+				purchaseTime = gconv.String(firstOne["expurasingtime"])
+			}
+			//
+			winner, _ := v_baseinfo["winner"].(string)
+			bdo.Winners.Add(winner)
+			//
+			detail := filterHtml(gconv.String(v_baseinfo["detail"]))
+			newDetail := gstr.SubStrRune(detail, 0, 300)
+			if detail != newDetail {
+				newDetail += g.Config().MustGet(ctx, "template.captureAppend").String()
+			}
+			bdd := &BidDataDetail{
+				ProjectName:  gconv.String(v_baseinfo["projectname"]),
+				ProjectCode:  gconv.String(v_baseinfo["projectcode"]),
+				Area:         area,
+				Buyer:        gconv.String(v_baseinfo["buyer"]),
+				Budget:       gconv.String(v_baseinfo["budget"]),
+				Winner:       winner,
+				Bidamount:    gconv.String(v_baseinfo["bidamount"]),
+				Detail:       newDetail,
+				PurchaseTime: purchaseTime,
+				Projectscope: gconv.String(v_baseinfo["projectscope"]),
+			}
+			if v_baseinfo["bidamount"] != nil {
+				bdd.Amount = gconv.Float64(bdd.Bidamount)
+			} else {
+				bdd.Amount = gconv.Float64(bdd.Budget)
+			}
+			if bdd.ProjectName == "" {
+				bdd.ProjectName = gconv.String(v_baseinfo["title"])
+			}
+			if bidendtime > 0 {
+				bdd.Bidendtime = gtime.New(bidendtime).Layout(time.DateTime)
+			}
+			bdo.BidDataDetails.Add(bdd)
+		}(mt)
+		index++
+		if index%500 == 0 {
+			g.Log().Info(ctx, "加载清洗数据。。。", index)
+		}
+		mt = make(map[string]interface{})
+	}
+	wait.Wait()
+	g.Log().Info(ctx, "清洗数据加载结束。。。", index)
+	articles := []*Article{}
+	ymd := gtime.Now().Format("Ymd")
+	purchasingSize := g.Config().MustGet(ctx, "template.purchasingSize").Int()
+	winnerSize := g.Config().MustGet(ctx, "template.winnerSize").Int()
+	all.Iterator(func(k, v interface{}) bool {
+		kk := k.(string)
+		vv := v.(*BidDataItem)
+		//
+		if !vv.Zbgs.BidDataDetails.IsEmpty() {
+			vv.Zbgs.BidDataDetails.Sort()
+			content := fmt.Sprintf(g.Config().MustGet(ctx, "template.zbgs.content").String(), kk, garray.NewArrayFrom(vv.Zbgs.Purchasinglist.Pops(purchasingSize)).Join("、"), garray.NewArrayFrom(vv.Zbgs.Winners.Pops(winnerSize)).Join("、"))
+			for k, v := range vv.Zbgs.BidDataDetails.PopLefts(g.Config().MustGet(ctx, "template.zhongProjectSize").Int()) {
+				vv := v.(*BidDataDetail)
+				content += fmt.Sprintf("<br>项目%s:%s", NumToChinese(k+1), vv.ProjectName)
+				if vv.Buyer != "" {
+					content += fmt.Sprintf("<br>招采单位:%s", vv.Buyer)
+				}
+				if vv.Winner != "" {
+					content += fmt.Sprintf("<br>中标单位:%s", vv.Winner)
+				}
+				if vv.Bidamount != "" && vv.Bidamount != "0" {
+					content += fmt.Sprintf("<br>中标金额:%s", vv.Bidamount)
+				}
+				if vv.Detail != "" {
+					content += fmt.Sprintf("<br>建设内容:%s", vv.Detail)
+				}
+				content += "<br>"
+			}
+			articles = append(articles, &Article{
+				Title:   fmt.Sprintf("%s_%s_中标标讯", ymd, kk),
+				Content: content,
+			})
+		}
+		//
+		if !vv.Zcgs.BidDataDetails.IsEmpty() {
+			vv.Zcgs.BidDataDetails.Sort()
+			content := ""
+			var allBudget float64
+			for k, v := range vv.Zcgs.BidDataDetails.PopLefts(g.Config().MustGet(ctx, "template.zhaoProjectSize").Int()) {
+				vv := v.(*BidDataDetail)
+				allBudget += gconv.Float64(vv.Budget)
+				content += fmt.Sprintf("<br>项目%s:%s", NumToChinese(k+1), vv.ProjectName)
+				if vv.ProjectCode != "" {
+					content += fmt.Sprintf("<br>项目编号:%s", vv.ProjectCode)
+				}
+				if vv.Buyer != "" {
+					content += fmt.Sprintf("<br>采购单位:%s", vv.Buyer)
+				}
+				if vv.Area != "" {
+					content += fmt.Sprintf("<br>项目地区:%s", vv.Area)
+				}
+				if vv.Budget != "" && vv.Budget != "0" {
+					content += fmt.Sprintf("<br>预算金额:%s", vv.Budget)
+				}
+				if vv.Bidendtime != "" {
+					content += fmt.Sprintf("<br>投标截止时间:%s", vv.Bidendtime)
+				}
+				if vv.Detail != "" {
+					content += fmt.Sprintf("<br>项目建设内容:%s", vv.Detail)
+				}
+				content += "<br>"
+			}
+			content = fmt.Sprintf(g.Config().MustGet(ctx, "template.zcgs.content").String(), kk, garray.NewArrayFrom(vv.Zcgs.Purchasinglist.Pops(purchasingSize)).Join("、"), allBudget) + content
+			articles = append(articles, &Article{
+				Title:   fmt.Sprintf("%s_%s_招标标讯", ymd, kk),
+				Content: content,
+			})
+		}
+		//
+		if !vv.Yxsjgs.BidDataDetails.IsEmpty() {
+			vv.Yxsjgs.BidDataDetails.Sort()
+			content := ""
+			for k, v := range vv.Yxsjgs.BidDataDetails.PopLefts(g.Config().MustGet(ctx, "template.caiProjectSize").Int()) {
+				vv := v.(*BidDataDetail)
+				content += fmt.Sprintf("<br>项目%s:%s", NumToChinese(k+1), vv.ProjectName)
+				content += fmt.Sprintf("<br>项目地区:%s", vv.Area)
+				if vv.Buyer != "" {
+					content += fmt.Sprintf("<br>采购单位:%s", vv.Buyer)
+				}
+				if vv.Budget != "" && vv.Budget != "0" {
+					content += fmt.Sprintf("<br>预算金额:%s", vv.Budget)
+				}
+				if vv.PurchaseTime != "" {
+					content += fmt.Sprintf("<br>预计采购时间:%s", vv.PurchaseTime)
+				}
+				if vv.Detail != "" {
+					content += fmt.Sprintf("<br>需求概况:%s", vv.Detail)
+				}
+				content += "<br>"
+			}
+			content = g.Config().MustGet(ctx, "template.yxsjgs.content").String() + content
+			articles = append(articles, &Article{
+				Title:   fmt.Sprintf("%s_%s_采购意向", ymd, kk),
+				Content: content,
+			})
+		}
+		return true
+	})
+	//
+	if len(articles) > 0 {
+		list := g.List{}
+		for _, v := range articles {
+			list = append(list, g.Map{
+				"article_type": "news",
+				"title":        v.Title,
+				//"author":AUTHOR,
+				//"digest":DIGEST,
+				"content": v.Content,
+				//"content_source_url":CONTENT_SOURCE_URL,
+				"thumb_media_id": g.Config().MustGet(ctx, "mediaId").String(),
+				//"need_open_comment":0,
+				//"only_fans_can_comment":0,
+				//"pic_crop_235_1":X1_Y1_X2_Y2,
+				//"pic_crop_1_1":X1_Y1_X2_Y2
+			})
+		}
+		g.Log().Info(ctx, "提交草稿数据", len(articles), "条", gconv.String(list))
+		result, err := g.Client().ContentType("application/json").Post(ctx, "https://api.weixin.qq.com/cgi-bin/draft/add?access_token="+getAccessToken(ctx), g.Map{"articles": list})
+		if err != nil {
+			g.Log().Error(ctx, "提交草稿出错", err)
+			return
+		}
+		defer result.Close()
+		g.Log().Info(ctx, "新增草稿箱请求返回结果", result.ReadAllString())
+	} else {
+		g.Log().Info(ctx, "没有需要提交草稿的数据")
+	}
+	g.Log().Info(ctx, "新增草稿箱任务结束。。。")
+}
+
+func getAccessToken(ctx context.Context) string {
+	code := g.Config().MustGet(ctx, "appid").String()
+	var repl string
+	client, err := rpc.DialHTTP("tcp", g.Config().MustGet(ctx, "wxTokenRpc").String())
+	if err != nil {
+		g.Log().Error(ctx, code, err)
+		return repl
+	}
+	defer client.Close()
+	err = client.Call("WxTokenRpc.GetAccessToken", code, &repl)
+	if err != nil {
+		g.Log().Error(ctx, code, err)
+		return repl
+	}
+	if repl == "" {
+		g.Log().Error(ctx, code, "未获取到accessToken")
+	} else {
+		g.Log().Info(ctx, code, "获取到accessToken", repl)
+	}
+	return repl
+}
+func filterHtml(text string) string {
+	text = strings.ReplaceAll(text, `\t`, "")
+	text = strings.ReplaceAll(text, `\n`, "")
+	doc, err := html.Parse(strings.NewReader(text))
+	if err != nil {
+		return regexp.MustCompile(`<[^>]+>`).ReplaceAllString(text, "")
+	}
+	var textBuilder strings.Builder
+	var traverse func(*html.Node)
+	traverse = func(n *html.Node) {
+		if n.Type == html.TextNode {
+			textBuilder.WriteString(n.Data)
+		}
+		for c := n.FirstChild; c != nil; c = c.NextSibling {
+			traverse(c)
+		}
+	}
+	traverse(doc)
+	return strings.TrimSpace(textBuilder.String())
+}

+ 18 - 0
main_test.go

@@ -0,0 +1,18 @@
+package main
+
+import (
+	"github.com/gogf/gf/v2/os/gctx"
+	"log"
+	"testing"
+)
+
+func TestGetAccessToken(t *testing.T) {
+	log.Println(getAccessToken(gctx.New()))
+}
+func TestAddMaterial(t *testing.T) {
+	addMaterial()
+}
+
+func TestAddDraft(t *testing.T) {
+	addDraft(0, 0)
+}

+ 1 - 0
task.json

@@ -0,0 +1 @@
+{"LastTime":1740731400}

BIN
thumb.png