Эх сурвалжийг харах

wip:提交协议归档提醒

wkyuer 2 сар өмнө
parent
commit
f3d81b6187

+ 17 - 0
config.yaml

@@ -53,6 +53,8 @@ mongodb:
 
 userCenterUrl: "https://jybx2-webtest.jydev.jianyu360.com"
 
+apiList:
+  buyBalance: "http://172.31.31.205:8889/resources/purchaseUserBalance"
 
 etcd:
   hosts:
@@ -61,6 +63,21 @@ userCenterKey: "usercenter.rpc" #用户中台rpc
 powerCheckCenterKey: "powercheck.rpc" #权益校验中台
 entManageApplication: "entmanageapplication.rpc" #企业管理中台
 
+protocolNotice:
+  cron: "0 */2 * * * ?" #定时任务执行时间
+  salerManager: -1 #销管entUserID
+  dayLimit: 7 #几天提醒
+
+mailConfig:
+  - smtpHost: "smtp.exmail.qq.com"
+    smtpPort: 465
+    pwd: "ue9Rg9Sf4CVtdm5a"
+    user: "public03@topnet.net.cn"
+  - smtpHost: "smtp.exmail.qq.com"
+    smtpPort: 465
+    pwd: "ue9Rg9Sf4CVtdm5a"
+    user: "public03@topnet.net.cn"
+
 examineDiscount: #自动审批折扣率
   VIP订阅:
     - min: 12

+ 65 - 25
internal/jyutil/dept.go

@@ -18,6 +18,7 @@ type (
 	DeptManager struct {
 		deptTree      *DeptStruct
 		idDeptMapping map[int]*DeptStruct
+		userIdMapping map[int]*Saler
 		//deptSalerMapping map[int]
 		salers  []*DeptStruct
 		lock    *sync.RWMutex
@@ -33,9 +34,11 @@ type (
 		Salers   []*Saler      `json:"salers,omitempty"`
 	}
 	Saler struct {
-		Id     int    `json:"id"`
-		Name   string `json:"name"`
-		DeptId int    `json:"deptId"`
+		Id       int    `json:"id"`
+		Name     string `json:"name"`
+		DeptId   int    `json:"deptId"`
+		RoleId   int    `json:"roleId"` //角色Id
+		UserMail string `json:"userMail"`
 	}
 )
 
@@ -112,55 +115,64 @@ func (mge *DeptManager) initAndUpdateDeptManager(ctx context.Context) {
 
 	//获取人员列表
 	salerRes, err := g.DB().Query(ctx, `SELECT
-					d.name as deptName,
-					d.id as deptId,
-					u.id as entUserId,
-					u.name as userName,
-					u.phone as phone
-				FROM
-					jianyu.entniche_user u
-					INNER JOIN jianyu.entniche_department_user e
-					INNER JOIN jianyu.entniche_department d
-					on
-					( u.id = e.user_id AND e.dept_id=d.id  ) 
-				WHERE
-					 u.ent_id = ?`, g.Cfg("global").MustGet(ctx, "powerEntId", 25917).Int64())
+		d.name as deptName,
+		u.mail as userMail,
+		d.id as deptId,
+		u.id as entUserId,
+		u.name as userName,
+		u.phone as phone,
+		eur.role_id,
+		COALESCE(eur.role_id, 0) as roleId  
+	FROM
+		jianyu.entniche_user u
+		INNER JOIN jianyu.entniche_department_user e on u.id = e.user_id
+		INNER JOIN jianyu.entniche_department d on e.dept_id=d.id
+		LEFT JOIN jianyu.entniche_user_role eur ON u.id = eur.user_id
+	WHERE
+		 u.ent_id =?`, g.Cfg("global").MustGet(ctx, "powerEntId", 25917).Int64())
 	if err != nil {
 		g.Log().Errorf(ctx, "获取企业人员异常")
 	}
 	deptIdUser := map[int][]*Saler{}
+	userIdMapping := map[int]*Saler{}
 	for _, m := range salerRes.List() {
 		var (
 			deptId = gconv.Int(m["deptId"])
 		)
 		uMsg := Saler{
-			Id:     gconv.Int(m["entUserId"]),
-			Name:   gconv.String(m["userName"]),
-			DeptId: deptId,
+			Id:       gconv.Int(m["entUserId"]),
+			Name:     gconv.String(m["userName"]),
+			DeptId:   deptId,
+			RoleId:   gconv.Int(m["roleId"]),
+			UserMail: gconv.String(m["userMail"]),
 		}
 		if _, ok := deptIdUser[deptId]; !ok {
 			deptIdUser[deptId] = []*Saler{&uMsg}
 		} else {
 			deptIdUser[deptId] = append(deptIdUser[deptId], &uMsg)
 		}
+		userIdMapping[uMsg.Id] = &uMsg
 	}
 	for deptId, users := range deptIdUser {
 		idDeptMapping[deptId].Salers = users
 	}
-
+	//递归获取部门全名
+	for _, deptStruct := range idDeptMapping {
+		deptStruct.FullName = mge.getDeptFullName(deptStruct.Id)
+	}
 	mge.lock.Lock()
 	defer mge.lock.Unlock()
 	mge.deptTree = deptTree
 	mge.idDeptMapping = idDeptMapping
+	mge.userIdMapping = userIdMapping
 	mge.lastMd5 = md5Str
 	//g.Dump(deptTree)
-	//递归获取部门全名
-	for _, deptStruct := range idDeptMapping {
-		deptStruct.FullName = mge.getDeptFullName(deptStruct.Id)
-	}
+
 }
 
 func (mge *DeptManager) getDeptFullName(deptId int) string {
+	mge.lock.RLock()
+	defer mge.lock.RUnlock()
 	node, ok := mge.idDeptMapping[deptId]
 	if !ok {
 		return ""
@@ -176,6 +188,8 @@ func (mge *DeptManager) getDeptFullName(deptId int) string {
 }
 
 func (mge *DeptManager) GetDeptNode(deptId int) *DeptStruct {
+	mge.lock.RLock()
+	defer mge.lock.RUnlock()
 	node, ok := mge.idDeptMapping[deptId]
 	if !ok {
 		return nil
@@ -184,6 +198,8 @@ func (mge *DeptManager) GetDeptNode(deptId int) *DeptStruct {
 }
 
 func (mge *DeptManager) GetDeptAllChildNode(deptId int) []*DeptStruct {
+	mge.lock.RLock()
+	defer mge.lock.RUnlock()
 	var rData []*DeptStruct
 	node, ok := mge.idDeptMapping[deptId]
 	if !ok || node == nil {
@@ -197,6 +213,8 @@ func (mge *DeptManager) GetDeptAllChildNode(deptId int) []*DeptStruct {
 }
 
 func (mge *DeptManager) GetDeptAllChildDeptId(deptId int) []int {
+	mge.lock.RLock()
+	defer mge.lock.RUnlock()
 	var rData = []int{deptId}
 	for _, node := range mge.GetDeptAllChildNode(deptId) {
 		rData = append(rData, node.Id)
@@ -206,6 +224,8 @@ func (mge *DeptManager) GetDeptAllChildDeptId(deptId int) []int {
 
 // GetFullDeptName 通过部门id获取部门全名
 func (mge *DeptManager) GetFullDeptName(deptId int) string {
+	mge.lock.RLock()
+	defer mge.lock.RUnlock()
 	dept, ok := mge.idDeptMapping[deptId]
 	if !ok {
 		return ""
@@ -235,7 +255,8 @@ func (mge *DeptManager) GetShowDeptTree(deptName ...string) (rData []interface{}
 }
 
 func (mge *DeptManager) GetShowDeptSalesTree(ctx context.Context, deptName ...string) []g.Map {
-
+	mge.lock.RLock()
+	defer mge.lock.RUnlock()
 	return nil
 }
 
@@ -291,6 +312,25 @@ func (mge *DeptManager) GetDeptInfoByEntId(ctx context.Context, entUserId int) (
 	return
 }
 
+func (mge *DeptManager) GetSalerByEntUserId(ctx context.Context, entUserId int) *Saler {
+	mge.lock.RLock()
+	defer mge.lock.RUnlock()
+	return mge.userIdMapping[entUserId]
+}
+
+func (mge *DeptManager) GetLeaderId(ctx context.Context, deptId int) []*Saler {
+	mge.lock.RLock()
+	defer mge.lock.RUnlock()
+	var results []*Saler
+	deptObj := mge.idDeptMapping[deptId]
+	for _, s := range deptObj.Salers {
+		if s.RoleId > 0 {
+			results = append(results, s)
+		}
+	}
+	return results
+}
+
 // 获取本部门与子部门id
 func DepId(entUserId int) []int {
 	res, _ := g.DB().GetOne(ctx, `SELECT

+ 96 - 0
internal/jyutil/sendMail.go

@@ -0,0 +1,96 @@
+package jyutil
+
+//发送邮件
+
+import (
+	"app.yhyue.com/moapp/jybase/mail"
+	"context"
+	"fmt"
+	"github.com/gogf/gf/v2/frame/g"
+	"log"
+	"strings"
+	"time"
+)
+
+var mailAuth []*mail.GmailAuth
+
+func SentMail(ctx context.Context, subject, content string, toMail ...string) {
+	if len(mailAuth) == 0 {
+		if err := g.Cfg().MustGet(ctx, "mailConfig").Struct(&mailAuth); err != nil {
+			g.Log().Errorf(ctx, "load mail auth conf Error: %v", err)
+			return
+		}
+	}
+	SendRetryMailMany(ctx, 3, strings.Join(toMail, ","), subject, content, "", "", mailAuth)
+}
+
+// SendRetryMailMany 发送邮件  收件人多个使用英文逗号分割
+func SendRetryMailMany(ctx context.Context, retry int, user_mail, subject, content, fname string, rename string, auth []*mail.GmailAuth) bool {
+	for i := 1; i <= retry; i++ {
+		for _, v := range auth { //使用多个邮箱尝试发送
+			if mail.GSendMail_q("剑鱼标讯", user_mail, "", "", subject, content, fname, rename, v) {
+				return true
+			}
+			t := time.Duration(i) * 30 * time.Second
+			g.Log().Errorf(ctx, user_mail, fmt.Sprintf("第%d轮,使用%s发送邮件失败!%v后重试", i, v.User, t))
+			time.Sleep(t)
+		}
+		if i == retry {
+			g.Log().Errorf(ctx, user_mail, fmt.Sprintf("发送邮件失败"))
+		}
+	}
+	return false
+}
+
+func SendRetryMail(retry int, userMail, subject, content, fName string, bt []byte, auth []*mail.GmailAuth) bool {
+	for i := 1; i <= retry; i++ {
+		for _, v := range auth { //使用多个邮箱尝试发送
+			if mail.GSendMail_B("剑鱼标讯", userMail, "", "", subject, content, fName, bt, v) {
+				return true
+			}
+			t := time.Duration(i) * 30 * time.Second
+			log.Println(userMail, fmt.Sprintf("第%d轮,使用%s发送邮件失败!%v后重试", i, v.User, t))
+			time.Sleep(t)
+		}
+		if i == retry {
+			log.Println(userMail, fmt.Sprintf("发送邮件失败"))
+		}
+	}
+	return false
+}
+
+// 发送邮箱验证码
+//func SendMailIdentCode(to, code string, auth []*mail.GmailAuth) bool {
+//	html := fmt.Sprintf(`<div>
+//            <div>
+//                  %s,您好!
+//            </div>
+//            <div style="padding: 20px 70px 10px 70px;">
+//                  <p>您正在进行绑定邮箱地址验证,请在邮件验证码输入框输入下方验证码:</p>
+//                  <span style="font-weight: bold;font-size: x-large;">%s</span>
+//                  <p>请勿向任何人泄露您收到的验证码。</p>
+//                  <p>如果您没有使用剑鱼标讯,请忽略此邮件。</p>
+//                  <p>此为系统邮件,请勿回复。</p>
+//                  <p>如有疑问,请联系客服 400-108-6670。</p>
+//            </div>
+//            <div>
+//                  <p>此致</p>
+//                  <p>剑鱼标讯</p>
+//            </div>
+//      </div>`, to, code)
+//
+//	for k, v := range auth {
+//		if mail.GSendMail("剑鱼标讯", to, "", "", "剑鱼标讯邮箱校验", html, "", "", v) {
+//			log.Println(to, fmt.Sprintf("使用%s发送邮件成功", v.User))
+//			return true
+//		}
+//		if k < len(auth)-1 {
+//			log.Println(to, fmt.Sprintf("使用%s发送邮件失败!3s后使用其他邮箱尝试", v.User))
+//		} else {
+//			log.Println(to, fmt.Sprintf("使用%s发送邮件失败!", v.User))
+//		}
+//		time.Sleep(time.Second * 3)
+//	}
+//
+//	return false
+//}

+ 1 - 0
internal/logic/timedTask/init.go

@@ -12,5 +12,6 @@ func AutoReturnTask() {
 	go OnlineReturnMoney() //首次执行
 	cr.AddFunc(g.Cfg().MustGet(context.Background(), "autoReturnTaskTime", "0 */3 * * * ?").String(), AutomaticPayment)
 	cr.AddFunc(g.Cfg().MustGet(context.Background(), "onlineReturnMoney", "0 */2 * * * ?").String(), OnlineReturnMoney)
+	cr.AddFunc(g.Cfg().MustGet(context.Background(), "protocolNotice.cron", "0 */2 * * * ?").String(), OrderProtocolNotice)
 	cr.Start()
 }

+ 99 - 0
internal/logic/timedTask/protocolNotice.go

@@ -0,0 +1,99 @@
+package timedTask
+
+import (
+	"context"
+	"fmt"
+	"github.com/gogf/gf/v2/frame/g"
+	"github.com/gogf/gf/v2/util/gconv"
+	"jyOrderManager/internal/jyutil"
+	"time"
+)
+
+func init() {
+	OrderProtocolNotice()
+}
+
+// OrderProtocolNotice
+// 订单审核通过后7天后(支持配置),协议状态为签协议,且协议归档状态为“未归档”,则发送邮件提醒该笔订单的销售人员、所属及上级部门的管理员、销管,
+func OrderProtocolNotice() {
+	type NoticeRow struct {
+		EntUserId     int    `json:"salesperson_entUserId"` //销售人员
+		SalerName     string `json:"salerName"`             //销售人员名字
+		OrderCode     string `json:"order_code"`            //订单号
+		CreateTime    string `json:"create_time"`           //审核通过时间
+		ContractMoney string `json:"contract_money"`        //合同金额
+		CompanyName   string `json:"company_name"`          //公司名称
+		PersonName    string `json:"personName"`            //联系人
+	}
+	var (
+		ctx            = context.Background()
+		dayLimit       = g.Cfg().MustGet(ctx, "protocolNotice.dayLimit", 7).Int()
+		salerManagerId = g.Cfg().MustGet(ctx, "protocolNotice.salerManager").Int()
+		dataStr        = time.Now().AddDate(0, 0, -dayLimit).Format(time.DateOnly)
+		mapping        = map[int][]*NoticeRow{} //销售人员待通知列表
+		leaderMapping  = map[int][]*NoticeRow{} //领导及销管
+		mailMapping    = map[int]string{}       //邮箱
+		execSql        = fmt.Sprintf("SELECT do.salesperson_entUserId,do.order_code,ar.create_time,c.contract_money,do.company_name,do.personName,do.create_person FROM jianyu.audit_records ar inner join jianyu.dataexport_order do on ar.order_code=do.order_code INNER JOIN jianyu.contract c on ar.order_code=c.order_code WHERE ar.operator_type =5 AND c.contract_archive_status=0 and do.salesperson_entUserId!='' AND ar.create_time > '%s 00:00:00'AND ar.create_time < '%s 23:59:59'", dataStr, dataStr)
+	)
+
+	res, err := g.DB().Query(ctx, execSql)
+	if err != nil {
+		g.Log().Error(ctx, err)
+	}
+
+	for _, m := range res.List() {
+		var noticeRow NoticeRow
+		if err := gconv.Struct(m, &noticeRow); err != nil {
+			g.Log().Errorf(ctx, "OrderProtocolNotice range Struct err:%v", err)
+			continue
+		}
+		saler := jyutil.JyDepartmentManager.GetSalerByEntUserId(ctx, noticeRow.EntUserId)
+		if saler == nil {
+			continue
+		}
+		if saler.UserMail != "" {
+			mailMapping[noticeRow.EntUserId] = saler.UserMail
+		}
+		noticeRow.SalerName = saler.Name
+		mapping[noticeRow.EntUserId] = append(mapping[noticeRow.EntUserId], &noticeRow)
+		leaderMapping[-1] = append(leaderMapping[-1], &noticeRow)
+
+		for _, s := range jyutil.JyDepartmentManager.GetLeaderId(ctx, saler.DeptId) {
+			leaderMapping[s.Id] = append(leaderMapping[s.Id], &noticeRow)
+		}
+	}
+	for sid, datas := range mapping {
+		if mail := mailMapping[sid]; mail != "" {
+			tableHtml := ""
+			for i, data := range datas {
+				tableHtml += fmt.Sprintf("<tr><td>%d</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td></tr>",
+					i+1, data.SalerName, data.OrderCode, data.CompanyName, data.PersonName, data.ContractMoney, data.CreateTime)
+			}
+			jyutil.SentMail(ctx, "协议归档提醒", fmt.Sprintf("<p>%s</p><table border><tr><th>序号</th><th>销售人员</th><th>订单编号</th><th>公司名称</th><th>联系人姓名</th><th>合同金额合计</th><th>订单审核通过时间</th></tr>%s</table>",
+				fmt.Sprintf("以下订单协议签订状态为签协议,已超过%d天仍未归 ,请及时将签订好的协议交由销管进行上传归档:", dayLimit), tableHtml), mail)
+		}
+	}
+	for sid, datas := range leaderMapping {
+		if sid == salerManagerId {
+			continue
+		}
+		if sid == -1 {
+			sid = salerManagerId
+			if mailMapping[sid] == "" { //获取销管
+				if t := jyutil.JyDepartmentManager.GetSalerByEntUserId(ctx, sid); t != nil {
+					mailMapping[sid] = t.UserMail
+				}
+			}
+		}
+		if mail := mailMapping[sid]; mail != "" {
+			tableHtml := ""
+			for i, data := range datas {
+				tableHtml += fmt.Sprintf("<tr><td>%d</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td></tr>",
+					i+1, data.SalerName, data.OrderCode, data.CompanyName, data.PersonName, data.ContractMoney, data.CreateTime)
+			}
+			jyutil.SentMail(ctx, "协议归档提醒", fmt.Sprintf("<p>%s</p><table border><tr><th>序号</th><th>销售人员</th><th>订单编号</th><th>公司名称</th><th>联系人姓名</th><th>合同金额合计</th><th>订单审核通过时间</th></tr>%s</table>",
+				fmt.Sprintf("以下订单协议签订状态为签协议,已超过%d天仍未归档,请及时提醒相关人员将签订好的协议交由销管进行上传归档::", dayLimit), tableHtml), mail)
+		}
+	}
+
+}