|
|
@@ -0,0 +1,306 @@
|
|
|
+package cust
|
|
|
+
|
|
|
+import (
|
|
|
+ "dashoo.cn/micro/app/service"
|
|
|
+ "dashoo.cn/opms_libary/plugin/dingtalk"
|
|
|
+ "dashoo.cn/opms_libary/plugin/dingtalk/message/corpconversation"
|
|
|
+ "fmt"
|
|
|
+ "github.com/360EntSecGroup-Skylar/excelize"
|
|
|
+ "github.com/gogf/gf/frame/g"
|
|
|
+ "github.com/gogf/gf/os/glog"
|
|
|
+ "github.com/gogf/gf/os/gtime"
|
|
|
+ "github.com/gogf/gf/util/gconv"
|
|
|
+ "github.com/robfig/cron"
|
|
|
+ "os"
|
|
|
+)
|
|
|
+
|
|
|
+// 初始化,创建每10分钟执行一次的定时任务
|
|
|
+func init() {
|
|
|
+ // 定时任务
|
|
|
+ c := cron.New()
|
|
|
+ spec1 := "0 0 20 * * ?" // 每晚八点
|
|
|
+ spec2 := "0 0 18 * * 0" // 每周日晚6点
|
|
|
+
|
|
|
+ // 新增招标信息每晚8点
|
|
|
+ if err := c.AddJob(spec1, NewBidCron{}); err != nil {
|
|
|
+ glog.Error(err)
|
|
|
+ }
|
|
|
+ // 未跟进的招标信息,每周日晚上6点
|
|
|
+ if err := c.AddJob(spec2, NotFollowBidCron{}); err != nil {
|
|
|
+ glog.Error(err)
|
|
|
+ }
|
|
|
+
|
|
|
+ c.Start()
|
|
|
+}
|
|
|
+
|
|
|
+// NewBidCron 新增招标信息
|
|
|
+type NewBidCron struct {
|
|
|
+}
|
|
|
+
|
|
|
+// NotFollowBidCron 未跟进的招标信息
|
|
|
+type NotFollowBidCron struct {
|
|
|
+}
|
|
|
+
|
|
|
+// Run 新增招标信息提醒
|
|
|
+func (c NewBidCron) Run() {
|
|
|
+ tenant := g.Config().GetString("micro_srv.tenant")
|
|
|
+ if tenant == "" {
|
|
|
+ glog.Error("交接任务单定时任务租户码未设置,请前往配置")
|
|
|
+ return
|
|
|
+ }
|
|
|
+ // 当前时间
|
|
|
+ date := gtime.Now()
|
|
|
+ bidInfos, err := g.DB(tenant).Model("cust_customer_bid_record a").InnerJoin("cust_customer b", "a.cust_id=b.id").Where(fmt.Sprintf("a.created_time LIKE '%v%%'", date.Format("Y-m-d"))).Fields("a.*,b.sales_id").All()
|
|
|
+ if err != nil {
|
|
|
+ glog.Error(err)
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ data := make(map[int][]map[string]string)
|
|
|
+
|
|
|
+ for _, contract := range bidInfos {
|
|
|
+ id := gconv.Int(contract["sales_id"])
|
|
|
+ if _, ok := data[id]; !ok {
|
|
|
+ data[id] = make([]map[string]string, 0)
|
|
|
+ }
|
|
|
+ d := map[string]string{
|
|
|
+ "cuct_name": gconv.String(contract["cuct_name"]),
|
|
|
+ "product_name": gconv.String(contract["product_name"]),
|
|
|
+ "published_time": contract["published_time"].GTime().Format("Y-m-d"),
|
|
|
+ "title": gconv.String(contract["title"]),
|
|
|
+ "budget": fmt.Sprintf("%.2f", contract["budget"].Float64()),
|
|
|
+ }
|
|
|
+ data[id] = append(data[id], d)
|
|
|
+ }
|
|
|
+
|
|
|
+ if len(data) > 0 {
|
|
|
+ for id, collections := range data {
|
|
|
+ f := excelize.NewFile()
|
|
|
+ // Create a new sheet.
|
|
|
+ sheet := f.NewSheet("Sheet1")
|
|
|
+
|
|
|
+ // 表头
|
|
|
+ f.SetCellValue("Sheet1", Div(1)+"1", "客户名称")
|
|
|
+ f.SetCellValue("Sheet1", Div(2)+"1", "招标产品名称")
|
|
|
+ f.SetCellValue("Sheet1", Div(3)+"1", "发布招标日期")
|
|
|
+ f.SetCellValue("Sheet1", Div(4)+"1", "招标信息标题")
|
|
|
+ f.SetCellValue("Sheet1", Div(4)+"1", "项目预算")
|
|
|
+
|
|
|
+ for index, collection := range collections {
|
|
|
+ f.SetCellValue("Sheet1", Div(1)+gconv.String(index+2), collection["cuct_name"])
|
|
|
+ f.SetCellValue("Sheet1", Div(2)+gconv.String(index+2), collection["product_name"])
|
|
|
+ f.SetCellValue("Sheet1", Div(3)+gconv.String(index+2), collection["published_time"])
|
|
|
+ f.SetCellValue("Sheet1", Div(4)+gconv.String(index+2), collection["title"])
|
|
|
+ f.SetCellValue("Sheet1", Div(4)+gconv.String(index+2), collection["budget"])
|
|
|
+ }
|
|
|
+
|
|
|
+ // Set active sheet of the workbook.
|
|
|
+ f.SetActiveSheet(sheet)
|
|
|
+ // Save xlsx file by the given path.
|
|
|
+ file := "./temp/新增招标信息.xlsx"
|
|
|
+ _, err = createPath("./temp")
|
|
|
+ if err != nil {
|
|
|
+ glog.Error(err)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ if err = f.SaveAs(file); err != nil {
|
|
|
+ glog.Error(err)
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ fileId, err := uploadFile(file)
|
|
|
+ if err != nil {
|
|
|
+ glog.Error(err)
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ notifyMessageFile("新增招标信息提醒", gconv.String(id), fileId)
|
|
|
+ // 删除已存在的文件
|
|
|
+ err = os.Remove(file)
|
|
|
+ if err != nil {
|
|
|
+ glog.Error(err)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// Run 未跟进的招标信息提醒
|
|
|
+func (c NotFollowBidCron) Run() {
|
|
|
+ tenant := g.Config().GetString("micro_srv.tenant")
|
|
|
+ if tenant == "" {
|
|
|
+ glog.Error("交接任务单定时任务租户码未设置,请前往配置")
|
|
|
+ return
|
|
|
+ }
|
|
|
+ // 当前时间
|
|
|
+ bidInfos, err := g.DB(tenant).Model("cust_customer_bid_record a").InnerJoin("cust_customer b", "a.cust_id=b.id").LeftJoin("(SELECT * FROM plat_followup WHERE target_type='60') c", "c.target_id=a.id").Where("c.id IS NULL").Group("a.id").Fields("a.*,b.sales_id").All()
|
|
|
+ if err != nil {
|
|
|
+ glog.Error(err)
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ data := make(map[int][]map[string]string)
|
|
|
+
|
|
|
+ for _, contract := range bidInfos {
|
|
|
+ id := gconv.Int(contract["sales_id"])
|
|
|
+ if _, ok := data[id]; !ok {
|
|
|
+ data[id] = make([]map[string]string, 0)
|
|
|
+ }
|
|
|
+ d := map[string]string{
|
|
|
+ "cuct_name": gconv.String(contract["cuct_name"]),
|
|
|
+ "product_name": gconv.String(contract["product_name"]),
|
|
|
+ "published_time": contract["published_time"].GTime().Format("Y-m-d"),
|
|
|
+ "title": gconv.String(contract["title"]),
|
|
|
+ "budget": fmt.Sprintf("%.2f", contract["budget"].Float64()),
|
|
|
+ }
|
|
|
+ data[id] = append(data[id], d)
|
|
|
+ }
|
|
|
+
|
|
|
+ if len(data) > 0 {
|
|
|
+ for id, collections := range data {
|
|
|
+ f := excelize.NewFile()
|
|
|
+ // Create a new sheet.
|
|
|
+ sheet := f.NewSheet("Sheet1")
|
|
|
+
|
|
|
+ // 表头
|
|
|
+ f.SetCellValue("Sheet1", Div(1)+"1", "客户名称")
|
|
|
+ f.SetCellValue("Sheet1", Div(2)+"1", "招标产品名称")
|
|
|
+ f.SetCellValue("Sheet1", Div(3)+"1", "发布招标日期")
|
|
|
+ f.SetCellValue("Sheet1", Div(4)+"1", "招标信息标题")
|
|
|
+ f.SetCellValue("Sheet1", Div(4)+"1", "项目预算")
|
|
|
+
|
|
|
+ for index, collection := range collections {
|
|
|
+ f.SetCellValue("Sheet1", Div(1)+gconv.String(index+2), collection["cuct_name"])
|
|
|
+ f.SetCellValue("Sheet1", Div(2)+gconv.String(index+2), collection["product_name"])
|
|
|
+ f.SetCellValue("Sheet1", Div(3)+gconv.String(index+2), collection["published_time"])
|
|
|
+ f.SetCellValue("Sheet1", Div(4)+gconv.String(index+2), collection["title"])
|
|
|
+ f.SetCellValue("Sheet1", Div(4)+gconv.String(index+2), collection["budget"])
|
|
|
+ }
|
|
|
+
|
|
|
+ // Set active sheet of the workbook.
|
|
|
+ f.SetActiveSheet(sheet)
|
|
|
+ // Save xlsx file by the given path.
|
|
|
+ file := "./temp/未跟进的招标信息.xlsx"
|
|
|
+ _, err = createPath("./temp")
|
|
|
+ if err != nil {
|
|
|
+ glog.Error(err)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ if err = f.SaveAs(file); err != nil {
|
|
|
+ glog.Error(err)
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ fileId, err := uploadFile(file)
|
|
|
+ if err != nil {
|
|
|
+ glog.Error(err)
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ notifyMessageFile("未跟进的招标信息提醒", gconv.String(id), fileId)
|
|
|
+ // 删除已存在的文件
|
|
|
+ err = os.Remove(file)
|
|
|
+ if err != nil {
|
|
|
+ glog.Error(err)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+func uploadFile(file string) (string, error) {
|
|
|
+ var request corpconversation.UploadConversationFileRequest
|
|
|
+ request.Type = "file"
|
|
|
+ request.FileData = "media"
|
|
|
+ request.FilePath = file
|
|
|
+
|
|
|
+ c := dingtalk.Client.GetCorpConversation()
|
|
|
+ resp, err := c.UploadConversationFile(&request)
|
|
|
+ if err != nil {
|
|
|
+ fmt.Println(err)
|
|
|
+ return "", err
|
|
|
+ }
|
|
|
+
|
|
|
+ return resp.MediaId, nil
|
|
|
+}
|
|
|
+
|
|
|
+// notifyMessageFile 发送消息通知
|
|
|
+func notifyMessageFile(title, ids, message string) {
|
|
|
+ msg := g.MapStrStr{
|
|
|
+ "msgTitle": title,
|
|
|
+ "msgContent": message,
|
|
|
+ "msgType": "40", // 文件处理
|
|
|
+ "recvUserIds": ids,
|
|
|
+ "msgStatus": "10",
|
|
|
+ "sendType": "30",
|
|
|
+ }
|
|
|
+ if err := service.CreateSystemMessage(msg); err != nil {
|
|
|
+ glog.Error("消息提醒异常:", err)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 判断文件夹是否存在
|
|
|
+func pathExists(path string) (bool, error) {
|
|
|
+ _, err := os.Stat(path)
|
|
|
+ if err == nil {
|
|
|
+ return true, nil
|
|
|
+ }
|
|
|
+ if os.IsNotExist(err) {
|
|
|
+ return false, nil
|
|
|
+ }
|
|
|
+ return false, err
|
|
|
+}
|
|
|
+
|
|
|
+func createPath(path string) (bool, error) {
|
|
|
+ exist, err := pathExists(path)
|
|
|
+ if err != nil {
|
|
|
+ return false, err
|
|
|
+ }
|
|
|
+
|
|
|
+ if exist {
|
|
|
+ return true, nil
|
|
|
+ } else {
|
|
|
+ // 创建文件夹
|
|
|
+ err := os.MkdirAll(path, os.ModePerm)
|
|
|
+ if err != nil {
|
|
|
+ return false, err
|
|
|
+ } else {
|
|
|
+ return true, err
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// Div 数字转字母
|
|
|
+func Div(Num int) string {
|
|
|
+ var (
|
|
|
+ Str string = ""
|
|
|
+ k int
|
|
|
+ temp []int //保存转化后每一位数据的值,然后通过索引的方式匹配A-Z
|
|
|
+ )
|
|
|
+ //用来匹配的字符A-Z
|
|
|
+ Slice := []string{"", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O",
|
|
|
+ "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"}
|
|
|
+
|
|
|
+ if Num > 26 { //数据大于26需要进行拆分
|
|
|
+ for {
|
|
|
+ k = Num % 26 //从个位开始拆分,如果求余为0,说明末尾为26,也就是Z,如果是转化为26进制数,则末尾是可以为0的,这里必须为A-Z中的一个
|
|
|
+ if k == 0 {
|
|
|
+ temp = append(temp, 26)
|
|
|
+ k = 26
|
|
|
+ } else {
|
|
|
+ temp = append(temp, k)
|
|
|
+ }
|
|
|
+ Num = (Num - k) / 26 //减去Num最后一位数的值,因为已经记录在temp中
|
|
|
+ if Num <= 26 { //小于等于26直接进行匹配,不需要进行数据拆分
|
|
|
+ temp = append(temp, Num)
|
|
|
+ break
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ return Slice[Num]
|
|
|
+ }
|
|
|
+ for _, value := range temp {
|
|
|
+ Str = Slice[value] + Str //因为数据切分后存储顺序是反的,所以Str要放在后面
|
|
|
+ }
|
|
|
+ return Str
|
|
|
+}
|