Jelajahi Sumber

feature(*): 消息提醒功能调整:
1、督办任务:任务接收、定期反馈、任务超期。每晚8点定时提醒
2、客户管理:销售接收分配的公海客户,随时提醒
3、招标信息:新增招标信息每晚8点、未跟进的招标信息,每周日晚上6点
4、支持人员反馈,随时;未填写销售人员反馈的支持工单,提醒销售完成,晚上8点

lk 2 tahun lalu
induk
melakukan
cd41b86a0f

+ 3 - 0
opms_parent/app/dao/work/internal/work_order.go

@@ -76,6 +76,7 @@ type workOrderColumns struct {
 	FeedbackSaleDist       string // 销售反馈-客户经销商反馈 (售前讲解)
 	FeedbackSaleNext       string // 销售反馈-下一步计划 (售前讲解 技术文件)
 	FeedbackSaleUser       string // 销售反馈-用户反馈 (技术文件)
+	FeedbackSaleOrder      string // 销售反馈-工单反馈(售后运维工单)
 }
 
 var (
@@ -131,6 +132,7 @@ var (
 			FeedbackSaleDist:       "feedback_sale_dist",
 			FeedbackSaleNext:       "feedback_sale_next",
 			FeedbackSaleUser:       "feedback_sale_user",
+			FeedbackSaleOrder:      "feedback_sale_order",
 		},
 	}
 )
@@ -188,6 +190,7 @@ func NewWorkOrderDao(tenant string) WorkOrderDao {
 			FeedbackSaleDist:       "feedback_sale_dist",
 			FeedbackSaleNext:       "feedback_sale_next",
 			FeedbackSaleUser:       "feedback_sale_user",
+			FeedbackSaleOrder:      "feedback_sale_order",
 		},
 	}
 	return dao

+ 1 - 0
opms_parent/app/model/work/internal/work_order.go

@@ -56,4 +56,5 @@ type WorkOrder struct {
 	FeedbackSaleDist       string      `orm:"feedback_sale_dist"       json:"feedbackSaleDist"`       // 销售反馈-客户经销商反馈 (售前讲解)
 	FeedbackSaleNext       string      `orm:"feedback_sale_next"       json:"feedbackSaleNext"`       // 销售反馈-下一步计划 (售前讲解 技术文件)
 	FeedbackSaleUser       string      `orm:"feedback_sale_user"       json:"feedbackSaleUser"`       // 销售反馈-用户反馈 (技术文件)
+	FeedbackSaleOrder      string      `orm:"feedback_sale_order"      json:"feedbackSaleOrder"`      // 销售反馈-工单反馈(售后运维工单)
 }

+ 1 - 0
opms_parent/app/model/work/work_order.go

@@ -112,6 +112,7 @@ type FeedbackSaleReq struct {
 	FeedbackSaleDist    string      `json:"feedbackSaleDist"`             // 销售反馈-客户经销商反馈 (售前讲解)
 	FeedbackSaleNext    string      `json:"feedbackSaleNext"`             // 销售反馈-下一步计划 (售前讲解 技术文件)
 	FeedbackSaleUser    string      `json:"feedbackSaleUser"`             // 销售反馈-用户反馈 (技术文件)
+	FeedbackSaleOrder   string      `json:"feedbackSaleOrder"`            // 销售反馈-工单反馈(售后运维工单)
 }
 
 type FeedbackTrailReq struct {

+ 1 - 1
opms_parent/app/service/contract/ctr_contract_cron.go

@@ -20,7 +20,7 @@ func init() {
 	// 定时任务
 	c := cron.New()
 	spec1 := "0 0 20 * * 0" // 每周日晚八点
-	spec2 := "0 0 20 1 * *" // 每月1号晚八点
+	spec2 := "0 0 20 1 * ?" // 每月1号晚八点
 
 	if err := c.AddJob(spec1, CollectionCron{}); err != nil {
 		glog.Error(err)

+ 306 - 0
opms_parent/app/service/cust/cust_cron.go

@@ -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
+}

+ 20 - 0
opms_parent/app/service/cust/cust_customer.go

@@ -938,10 +938,16 @@ func (s *CustomerService) AssignCustomer(ctx context.Context, req *model.AssignC
 		return myerrors.TipsError("无可分配客户")
 	}
 
+	customerNames := ""
 	for _, v := range data {
 		if v.SalesId != 0 {
 			return myerrors.TipsError(fmt.Sprintf("客户名称[%s]已被领取或分配", v.CustName))
 		}
+		if customerNames == "" {
+			customerNames = v.CustName
+		} else {
+			customerNames += "、" + v.CustName
+		}
 	}
 
 	err = s.ChangeCustBelong(req.Ids, req.SalesId, req.SalesName)
@@ -953,6 +959,20 @@ func (s *CustomerService) AssignCustomer(ctx context.Context, req *model.AssignC
 	if err != nil {
 		return err
 	}
+
+	// 消息提醒
+	msg := g.MapStrStr{
+		"msgTitle":    "公海客户分配提醒",
+		"msgContent":  fmt.Sprintf("客户%v已分配给您,请前往查看", customerNames),
+		"msgType":     "20",
+		"recvUserIds": gconv.String(req.SalesId),
+		"msgStatus":   "10",
+		"sendType":    "30",
+	}
+	if err := service.CreateSystemMessage(msg); err != nil {
+		g.Log().Error("公海客户分配提醒异常:", err)
+	}
+
 	return s.CreateDynamics("分配客户", req, req.Ids...)
 }
 

+ 2 - 2
opms_parent/app/service/plat/plat_task.go

@@ -364,8 +364,8 @@ func (s *taskService) AddTaskApproval(flow *workflowModel.PlatWorkflow, msg *mes
 			if err != nil {
 				return err
 			}
-			// 发送消息通知
-			go taskNotifyMessage(task.MainUserId, task.OwnerUserId, fmt.Sprintf("%v督办任务需要处理", task.TaskTitle))
+			//// 发送消息通知
+			//go taskNotifyMessage(task.MainUserId, task.OwnerUserId, fmt.Sprintf("%v督办任务需要处理", task.TaskTitle))
 			// 流程日志
 			err = CreateTaskLog(s, nil, task.Id, s.GetCxtUserId(), s.GetCxtUserName(), "督办审批", "通过", "")
 		} else if msg.Result == "refuse" {

+ 76 - 35
opms_parent/app/service/plat/plat_task_cron.go

@@ -18,7 +18,7 @@ import (
 func init() {
 	// 定时任务
 	c := cron.New()
-	spec := "1 */10 * * * ?" // 每天10分钟执行一次
+	spec := "0 0 20 * * ?" // 每晚8点定时提醒
 
 	if err := c.AddJob(spec, taskCron{}); err != nil {
 		glog.Error(err)
@@ -60,19 +60,40 @@ func (c taskCron) Run() {
 	}
 	// 查询数据,启用定时任务
 	var tasks []*model.PlatTask
+	var handles []*model.PlatTaskHandle
+	needReceiveMap := make(map[int]int)
 	err = g.DB(tenant).Model("plat_task").Where("task_status='10' OR task_status='20'").Scan(&tasks)
 	if err != nil && err != sql.ErrNoRows {
 		glog.Error(err)
 		return
 	}
+	err = g.DB(tenant).Model("plat_task_handle").Where("task_status='10' AND step='10'").Scan(&handles)
+	if err != nil && err != sql.ErrNoRows {
+		glog.Error(err)
+		return
+	}
+	for _, handle := range handles {
+		needReceiveMap[handle.TaskId] = 1
+	}
+
+	receiveData := make(map[int]string)
+	feedbackData := make(map[int]string)
+	beforeOverdueData := make(map[int]string)
+	afterOverdueData := make(map[int]string)
+
 	// 当前时间
 	now := gtime.Now()
+	nowStr := gtime.Now().Format("Y-m-d")
 	// 生成提醒数据
 	for _, task := range tasks {
-		TaskTitle := task.TaskTitle + "(须接受督办)"
-		if task.IsOverdue == "20" {
-			TaskTitle = task.TaskTitle + "(超期)"
+		// 任务接收提醒
+		if task.ReceiveDate == nil {
+			if _, ok := needReceiveMap[task.Id]; ok {
+				generateData(&receiveData, task.MainUserId, task.OwnerUserId, task.TaskTitle)
+			}
 		}
+
+		// 定期反馈提醒
 		// 固定日期提醒
 		if task.ReminderRule != "" {
 			rules := strings.Split(task.ReminderRule, " ")
@@ -80,12 +101,7 @@ func (c taskCron) Run() {
 				glog.Error(fmt.Sprintf("%v督办Id为%v提醒规则格式不正确", task.TaskTitle, task.Id))
 			} else {
 				if rules[3] == "*" { // 每天提醒
-					// 校验当前时间
-					remindTime := gtime.NewFromStr(fmt.Sprintf("%v %v:%v:%v", now.Format("Y-m-d"), rules[2], rules[1], rules[0]))
-					// 10分钟一次定时循环,两者相差在10分钟之内(纳秒转换1e9)
-					if (now.UnixNano()-remindTime.UnixNano())/(1*60*1e9) <= 10 && now.UnixNano() > remindTime.UnixNano() {
-						taskNotifyMessage(task.MainUserId, task.OwnerUserId, TaskTitle+"督办需要处理,请前往执行")
-					}
+					generateData(&feedbackData, task.MainUserId, task.OwnerUserId, task.TaskTitle)
 				} else if rules[3] == "?" { // 每周提醒
 					// 校验周选项是否匹配
 					weekDays := strings.Split(rules[4], ",")
@@ -97,12 +113,7 @@ func (c taskCron) Run() {
 						}
 					}
 					if isMatch {
-						// 校验当前时间
-						remindTime := gtime.NewFromStr(fmt.Sprintf("%v %v:%v:%v", now.Format("Y-m-d"), rules[2], rules[1], rules[0]))
-						// 10分钟一次定时循环,两者相差在10分钟之内(纳秒转换1e9)
-						if (now.UnixNano()-remindTime.UnixNano())/(1*60*1e9) <= 10 && now.UnixNano() > remindTime.UnixNano() {
-							taskNotifyMessage(task.MainUserId, task.OwnerUserId, TaskTitle+"督办需要处理,请前往执行")
-						}
+						generateData(&feedbackData, task.MainUserId, task.OwnerUserId, task.TaskTitle)
 					}
 				} else { // 每月提醒
 					monthDays := strings.Split(rules[3], ",")
@@ -114,36 +125,38 @@ func (c taskCron) Run() {
 						}
 					}
 					if isMatch {
-						// 校验当前时间
-						remindTime := gtime.NewFromStr(fmt.Sprintf("%v %v:%v:%v", now.Format("Y-m-d"), rules[2], rules[1], rules[0]))
-						// 10分钟一次定时循环,两者相差在10分钟之内(纳秒转换1e9)
-						if (now.UnixNano()-remindTime.UnixNano())/(1*60*1e9) <= 10 && now.UnixNano() > remindTime.UnixNano() {
-							taskNotifyMessage(task.MainUserId, task.OwnerUserId, TaskTitle+"督办需要处理,请前往执行")
-						}
+						generateData(&feedbackData, task.MainUserId, task.OwnerUserId, task.TaskTitle)
 					}
 				}
 			}
 		}
-		// 超期提醒,差10分(定时任务每10分钟执行一次)一天之时进行提醒
+
+		// 超期提醒
 		if task.TaskEndDate != nil {
 			// 超期前提醒
 			beforeDate := task.TaskEndDate.AddDate(0, 0, before)
-			if beforeDate.After(now) {
-				// 10分钟一次定时循环,两者相差在10分钟之内(纳秒转换1e9)
-				if (beforeDate.UnixNano()-now.UnixNano())/(1*60*1e9) <= 10 {
-					taskNotifyMessage(task.MainUserId, task.OwnerUserId, TaskTitle+"督办即将超期,请前往执行")
-				}
+			if beforeDate.Format("Y-m-d") == nowStr {
+				generateData(&beforeOverdueData, task.MainUserId, task.OwnerUserId, task.TaskTitle)
 			}
 			// 超期后提醒
 			afterDate := task.TaskEndDate.AddDate(0, 0, -after)
-			if now.After(afterDate) {
-				// 10分钟一次定时循环,两者相差在10分钟之内(纳秒转换1e9)
-				if (now.UnixNano()-afterDate.UnixNano())/(1*60*1e9) <= 10 {
-					taskNotifyMessage(task.MainUserId, task.OwnerUserId, TaskTitle+"督办已超期,请确认")
-				}
+			if afterDate.Format("Y-m-d") == nowStr {
+				generateData(&afterOverdueData, task.MainUserId, task.OwnerUserId, task.TaskTitle)
 			}
 		}
 	}
+	for key, val := range receiveData {
+		notifyMessage(gconv.String(key), val+"督办未接收,请前往处理")
+	}
+	for key, val := range feedbackData {
+		notifyMessage(gconv.String(key), val+"督办需要处理,请前往处理")
+	}
+	for key, val := range beforeOverdueData {
+		notifyMessage(gconv.String(key), val+"督办即将超期,请前往处理")
+	}
+	for key, val := range afterOverdueData {
+		notifyMessage(gconv.String(key), val+"督办已超期,请确认")
+	}
 }
 
 // 督办人任务的消息通知
@@ -169,15 +182,43 @@ func taskNotifyMessage(mainId int, ownerIds, message string) {
 	notifyMessage(ids, message)
 }
 
+func generateData(data *map[int]string, mainId int, ownerIds, info string) {
+	isCon := false
+	ownerIdArray := strings.Split(ownerIds, ",")
+	for _, id := range ownerIdArray {
+		if id == gconv.String(mainId) {
+			isCon = true
+			break
+		}
+	}
+	if !isCon {
+		if _, ok := (*data)[mainId]; !ok {
+			(*data)[mainId] = info
+		} else {
+			(*data)[mainId] += "," + info
+		}
+	}
+	for _, id := range ownerIdArray {
+		if id == "" {
+			continue
+		}
+		if _, ok := (*data)[gconv.Int(id)]; !ok {
+			(*data)[gconv.Int(id)] = info
+		} else {
+			(*data)[gconv.Int(id)] += "," + info
+		}
+	}
+}
+
 // notifyMessage 发送消息通知
 func notifyMessage(ids, message string) {
 	msg := g.MapStrStr{
 		"msgTitle":    "督办任务提醒",
-		"msgContent":  fmt.Sprintf("<p>%v</p>", message),
+		"msgContent":  fmt.Sprintf("%v", message),
 		"msgType":     "20",
 		"recvUserIds": ids,
 		"msgStatus":   "10",
-		"sendType":    "10",
+		"sendType":    "30",
 	}
 	if err := service.CreateSystemMessage(msg); err != nil {
 		glog.Error("消息提醒异常:", err)

+ 2 - 2
opms_parent/app/service/work/deliver_order_progress.go

@@ -507,7 +507,7 @@ func (s DeliverOrderProgressService) InspectGoods(ctx context.Context, req *work
 func (s DeliverOrderProgressService) ConfirmArrival(ctx context.Context, req *work.ConfirmArrivalReq) error {
 	data := fmt.Sprintf("deliver_status='50'")
 	if req.Remark != "" {
-		data = fmt.Sprintf("deliver_status='50',remark='%v'", req.Remark)
+		data = fmt.Sprintf("deliver_status='50',progress_status='30',rea_end_date='%v',remark='%v'", gtime.Now().Format("Y-m-d H:i:s"), req.Remark)
 	}
 	_, err := s.Dao.Update(data, fmt.Sprintf("id='%v'", req.Id))
 	return err
@@ -613,7 +613,7 @@ func (s DeliverOrderProgressService) CompleteInstall(ctx context.Context, req *w
 		}
 		// 更新发货任务单状态
 		if progressIds != "" {
-			_, err = tx.Update("deliver_order_imp_progress", fmt.Sprintf("deliver_status='60',progress_status='30',rea_end_date='%v'", gtime.Now().Format("Y-m-d H:i:s")), fmt.Sprintf("id IN (%v)", progressIds))
+			_, err = tx.Update("deliver_order_imp_progress", fmt.Sprintf("deliver_status='60'"), fmt.Sprintf("id IN (%v)", progressIds))
 			if err != nil {
 				return err
 			}

+ 14 - 8
opms_parent/app/service/work/work_order.go

@@ -446,7 +446,7 @@ func (s *OrderService) WorkOrderNotify(flow *workflowModel.PlatWorkflow, msg *me
 	}
 
 	if data[s.Dao.C.OrderStatus] == "30" {
-		s.WorkOrderSendMsg(workOrder, "支持工单审批通过提醒", fmt.Sprintf("<p>工单:%s 已审批通过</p>", workOrder.Name))
+		s.WorkOrderSendMsg(workOrder, "支持工单审批通过提醒", fmt.Sprintf("<p>工单:%s 已审批通过</p>", workOrder.Name), "10")
 	}
 
 	// 项目修改
@@ -457,7 +457,7 @@ func (s *OrderService) WorkOrderNotify(flow *workflowModel.PlatWorkflow, msg *me
 	return err
 }
 
-func (s *OrderService) WorkOrderSendMsg(workOrder *model.WorkOrder, title, content string) {
+func (s *OrderService) WorkOrderSendMsg(workOrder *model.WorkOrder, title, content, sendType string) {
 	recvUserIds := []int{}
 	if workOrder.AssignUserId != 0 {
 		recvUserIds = append(recvUserIds, workOrder.AssignUserId)
@@ -479,7 +479,7 @@ func (s *OrderService) WorkOrderSendMsg(workOrder *model.WorkOrder, title, conte
 		"msgType":     "20",
 		"recvUserIds": strings.Join(recvUserIdString, ","),
 		"msgStatus":   "10",
-		"sendType":    "10",
+		"sendType":    sendType,
 	}
 	fmt.Println(title, strings.Join(recvUserIdString, ","))
 	if err := service.CreateSystemMessage(msg); err != nil {
@@ -590,7 +590,7 @@ func (s *OrderService) FeedbackSupport(ctx context.Context, req *model.FeedbackS
 	if ent == nil {
 		return myerrors.TipsError("工单不存在")
 	}
-	if ent.OrderStatus != "30" || !(ent.OrderTypeName == "售前讲解支持" || ent.OrderTypeName == "技术文件支持") {
+	if ent.OrderStatus != "30" || !(ent.OrderTypeName == "售前讲解支持" || ent.OrderTypeName == "技术文件支持" || ent.OrderTypeName == "售后运维工单") {
 		return myerrors.TipsError("当前工单不可进行支持反馈")
 	}
 	if req.FeedbackSupportTime == nil {
@@ -619,6 +619,11 @@ func (s *OrderService) FeedbackSupport(ctx context.Context, req *model.FeedbackS
 			"feedback_support_name":    s.CxtUser.NickName,
 			"feedback_support_content": req.FeedbackSupportContent,
 		})
+
+		if err == nil {
+			s.WorkOrderSendMsg(ent, "支持工单总结反馈提醒", fmt.Sprintf("您的%v项目%v已有反馈,请尽快查看。", ent.NboName, ent.OrderTypeName), "30")
+		}
+
 		return err
 	})
 }
@@ -631,7 +636,7 @@ func (s *OrderService) FeedbackSale(ctx context.Context, req *model.FeedbackSale
 	if ent == nil {
 		return myerrors.TipsError("工单不存在")
 	}
-	if ent.OrderStatus != "30" || !(ent.OrderTypeName == "售前讲解支持" || ent.OrderTypeName == "技术文件支持") {
+	if ent.OrderStatus != "30" || !(ent.OrderTypeName == "售前讲解支持" || ent.OrderTypeName == "技术文件支持" || ent.OrderTypeName == "售后运维工单") {
 		return myerrors.TipsError("当前工单不可进行销售反馈")
 	}
 	if req.FeedbackSaleTime == nil {
@@ -654,6 +659,7 @@ func (s *OrderService) FeedbackSale(ctx context.Context, req *model.FeedbackSale
 			"feedback_sale_dist":    req.FeedbackSaleDist,
 			"feedback_sale_next":    req.FeedbackSaleNext,
 			"feedback_sale_user":    req.FeedbackSaleUser,
+			"feedback_sale_order":   req.FeedbackSaleOrder,
 			"updated_by":            s.CxtUser.Id,
 			"updated_name":          s.CxtUser.NickName,
 			"updated_time":          gtime.Now(),
@@ -797,7 +803,7 @@ func (s *OrderService) TimeUpdateExpect(ctx context.Context, req *model.TimeUpda
 		return err
 	})
 	if txerr == nil {
-		s.WorkOrderSendMsg(ent, "支持工单改期提醒", fmt.Sprintf("<p>工单:%s 期望完成时间已修改为 %s</p>", ent.Name, req.ExpectTime))
+		s.WorkOrderSendMsg(ent, "支持工单改期提醒", fmt.Sprintf("<p>工单:%s 期望完成时间已修改为 %s</p>", ent.Name, req.ExpectTime), "10")
 	}
 	return txerr
 }
@@ -831,7 +837,7 @@ func (s *OrderService) TimeUpdateSupport(ctx context.Context, req *model.TimeUpd
 		return err
 	})
 	if txerr == nil {
-		s.WorkOrderSendMsg(ent, "支持工单改期提醒", fmt.Sprintf("<p>工单:%s 支持时间已修改为 %s</p>", ent.Name, req.SupportTime))
+		s.WorkOrderSendMsg(ent, "支持工单改期提醒", fmt.Sprintf("<p>工单:%s 支持时间已修改为 %s</p>", ent.Name, req.SupportTime), "10")
 	}
 	return txerr
 }
@@ -882,7 +888,7 @@ func (s *OrderService) TimeUpdateTrial(ctx context.Context, req *model.TimeUpdat
 		return err
 	})
 	if txerr == nil {
-		s.WorkOrderSendMsg(ent, fmt.Sprintf("支持工单%s提醒", dynamic), fmt.Sprintf("<p>工单:%s 试用已修改为 %s-%s</p>", ent.Name, ent.TrialTimeStart, ent.TrialTimeEnd))
+		s.WorkOrderSendMsg(ent, fmt.Sprintf("支持工单%s提醒", dynamic), fmt.Sprintf("<p>工单:%s 试用已修改为 %s-%s</p>", ent.Name, ent.TrialTimeStart, ent.TrialTimeEnd), "10")
 	}
 	return txerr
 }

+ 70 - 0
opms_parent/app/service/work/work_order_cron.go

@@ -0,0 +1,70 @@
+package work
+
+import (
+	"dashoo.cn/micro/app/model/work"
+	"dashoo.cn/micro/app/service"
+	"github.com/gogf/gf/frame/g"
+	"github.com/gogf/gf/os/glog"
+	"github.com/gogf/gf/util/gconv"
+	"github.com/robfig/cron"
+)
+
+// 初始化,创建每10分钟执行一次的定时任务
+func init() {
+	// 定时任务
+	c := cron.New()
+	spec := "0 0 20 * * ?" // 每晚八点
+
+	// 新增招标信息每晚8点
+	if err := c.AddJob(spec, WorkOrderCron{}); err != nil {
+		glog.Error(err)
+	}
+
+	c.Start()
+}
+
+// WorkOrderCron 支持工单未填
+type WorkOrderCron struct {
+}
+
+// Run 支持工单未填写反馈提醒
+func (c WorkOrderCron) Run() {
+	tenant := g.Config().GetString("micro_srv.tenant")
+	if tenant == "" {
+		glog.Error("交接任务单定时任务租户码未设置,请前往配置")
+		return
+	}
+
+	var orders []*work.WorkOrder
+	err := g.DB(tenant).Model("work_order").Where("feedback_support_time IS NOT NULL AND feedback_sale_time IS NULL").Scan(&orders)
+	if err != nil {
+		glog.Error(err)
+		return
+	}
+	data := make(map[int]string)
+	for _, order := range orders {
+		if _, ok := data[order.SaleId]; !ok {
+			data[order.SaleId] = order.Name
+			continue
+		}
+		data[order.SaleId] += "," + order.Name
+	}
+	for key, val := range data {
+		notifyMessageDing("支持工单未填写反馈提醒", gconv.String(key), val+"未填写反馈,请前往填写")
+	}
+}
+
+// notifyMessageDing 发送消息通知
+func notifyMessageDing(title, ids, message string) {
+	msg := g.MapStrStr{
+		"msgTitle":    title,
+		"msgContent":  message,
+		"msgType":     "20", // 消息
+		"recvUserIds": ids,
+		"msgStatus":   "10",
+		"sendType":    "30",
+	}
+	if err := service.CreateSystemMessage(msg); err != nil {
+		glog.Error("消息提醒异常:", err)
+	}
+}