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点 spec3 := "0 0 20 * * ?" // 每晚八点 // 新增招标信息每晚8点 if err := c.AddJob(spec1, NewBidCron{}); err != nil { glog.Error(err) } // 未跟进的招标信息,每周日晚上6点 if err := c.AddJob(spec2, NotFollowBidCron{}); err != nil { glog.Error(err) } // 超过2个月没有创建项目以及没有申请闭环的招投标进入已超期 if err := c.AddJob(spec3, OverdueBidCron{}); err != nil { glog.Error(err) } c.Start() } // NewBidCron 新增招标信息 type NewBidCron struct { } // NotFollowBidCron 未跟进的招标信息 type NotFollowBidCron struct { } // OverdueBidCron 招标信息超期 type OverdueBidCron 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 (c OverdueBidCron) Run() { tenant := g.Config().GetString("micro_srv.tenant") if tenant == "" { glog.Error("租户码未设置,请前往配置") return } date := gtime.Now().AddDate(0, -2, 0).Format("Y-m-d 00:00:00") // 从项目发布日期起,超过2个月没有创建项目以及没有申请闭环的招投标进入已超期 // 状态:(10跟进中,20待审批,30已闭环、40已超期) bidInfos, err := g.DB(tenant).Model("cust_customer_bid_record a").LeftJoin("proj_business b", "b.bid_id=a.id").Where(fmt.Sprintf("b.id IS NULL AND a.status='10' AND a.created_time<'%v'", date)).Group("a.id").Fields("a.*").All() if err != nil { glog.Error(err) return } if len(bidInfos) > 0 { ids := "" for _, bid := range bidInfos { if ids == "" { ids = gconv.String(bid["id"]) } else { ids += "," + gconv.String(bid["id"]) } } _, err = g.DB(tenant).Model("cust_customer_bid_record").Update("status='40'", fmt.Sprintf("id IN (%v)", ids)) 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 }