Procházet zdrojové kódy

Merge remote-tracking branch 'origin/edb/frequency_notice'

# Conflicts:
#	models/db.go
#	services/task.go
Roc před 3 roky
rodič
revize
32cfbfa2a3

+ 9 - 0
models/data_entry.go

@@ -37,3 +37,12 @@ func GetEdbdataCount(tradeCode, nowDate string) (count int, err error) {
 	err = o.Raw(sql, tradeCode, nowDate).QueryRow(&count)
 	return
 }
+
+// GetEdbInfoByFrequencyNotDay 获取频度非日度 且 提醒时间不为空 的指标数据
+func GetEdbInfoByFrequencyNotDay() (items []*EdbInfo, err error) {
+	sql := `SELECT * FROM edbinfo WHERE frequency!="日度" AND notice_time<>''  `
+	o := orm.NewOrm()
+	o.Using("edb")
+	_, err = o.Raw(sql).QueryRows(&items)
+	return
+}

+ 1 - 0
models/db.go

@@ -80,5 +80,6 @@ func init() {
 		new(data_manage.BaseFromSmmIndex),
 		new(data_manage.BaseFromSmmData),
 		new(data_manage.EdbDataYs),
+		new(Edbdata),
 	)
 }

+ 22 - 0
models/edbdata.go

@@ -0,0 +1,22 @@
+package models
+
+import (
+	"rdluck_tools/orm"
+	"time"
+)
+
+type Edbdata struct {
+	TradeCode  string    `orm:"column(TRADE_CODE);pk" description:"指标编码"`
+	Dt         string    `orm:"column(DT)" description:"日期"`
+	Close      string    `orm:"column(CLOSE)" description:"值"`
+	ModifyTime time.Time `orm:"column(modify_time)" description:"修改时间"`
+}
+
+// GetLastEdbdataInfo 根据指标编号获取指标最近的一条数据
+func GetLastEdbdataInfo(tradeCode string) (item *Edbdata, err error) {
+	sql := `SELECT * FROM edbdata WHERE TRADE_CODE=? order by DT desc `
+	o := orm.NewOrm()
+	o.Using("edb")
+	err = o.Raw(sql, tradeCode).QueryRow(&item)
+	return
+}

+ 86 - 23
models/stack_company_statistic.go

@@ -14,6 +14,7 @@ type StackCompanyStatistic struct {
 	ProductId    int       `description:"客户产品id"`
 	ProductName  string    `description:"客户产品名称"`
 	ContractNum  int       `description:"第几份合同,默认是:1"`
+	ContractId   int       `description:"合同id,默认是:0"`
 	SellerId     int       `description:"所属销售id"`
 	SellerName   string    `description:"所属销售名称"`
 	GroupId      int       `description:"所属销售分组id"`
@@ -87,29 +88,30 @@ GROUP BY company_id,product_id `
 
 //增量客户统计报表列表数据结构
 type IncrementalList struct {
-	CompanyContractId int     `description:"合同id"`
-	ContractType      string  `description:"合同类型"`
-	CompanyId         int     `description:"企业客户id"`
-	CompanyName       string  `description:"企业客户名称"`
-	ProductId         int     `description:"产品id"`
-	ProductName       string  `description:"产品名称"`
-	CompanyProductId  int     `description:"客户购买产品授权id"`
-	ContractCode      string  `description:"合同编码"`
-	StartDate         string  `description:"合同开始日期"`
-	EndDate           string  `description:"合同结束日期"`
-	Money             float64 `description:"合同金额"`
-	PayMethod         string  `description:"付款方式"`
-	PayChannel        string  `description:"付款渠道"`
-	ImgUrl            string  `description:"合同图片"`
-	CreateTime        string  `description:"合同创建时间"`
-	ModifyTime        string  `description:"合同修改时间"`
-	Status            string  `description:"合同审批状态,0:待审批,1:已审批;默认:1"`
-	RegionType        string  `description:"企业客户所属区域;可选范围:国内,海外"`
-	SellerId          int     `description:"归属销售id"`
-	GroupId           int     `description:"归属分组id"`
-	DepartmentId      int     `description:"归属部门id"`
-	SellerName        string  `description:"归属销售名称"`
-	ExpireDay         string  `description:"剩余可用天数"`
+	CompanyOperationRecordId int     `description:"操作记录id"`
+	CompanyContractId        int     `description:"合同id"`
+	ContractType             string  `description:"合同类型"`
+	CompanyId                int     `description:"企业客户id"`
+	CompanyName              string  `description:"企业客户名称"`
+	ProductId                int     `description:"产品id"`
+	ProductName              string  `description:"产品名称"`
+	CompanyProductId         int     `description:"客户购买产品授权id"`
+	ContractCode             string  `description:"合同编码"`
+	StartDate                string  `description:"合同开始日期"`
+	EndDate                  string  `description:"合同结束日期"`
+	Money                    float64 `description:"合同金额"`
+	PayMethod                string  `description:"付款方式"`
+	PayChannel               string  `description:"付款渠道"`
+	ImgUrl                   string  `description:"合同图片"`
+	CreateTime               string  `description:"合同创建时间"`
+	ModifyTime               string  `description:"合同修改时间"`
+	Status                   string  `description:"合同审批状态,0:待审批,1:已审批;默认:1"`
+	RegionType               string  `description:"企业客户所属区域;可选范围:国内,海外"`
+	SellerId                 int     `description:"归属销售id"`
+	GroupId                  int     `description:"归属分组id"`
+	DepartmentId             int     `description:"归属部门id"`
+	SellerName               string  `description:"归属销售名称"`
+	ExpireDay                string  `description:"剩余可用天数"`
 }
 
 //未续约客户数(存量客户)
@@ -132,3 +134,64 @@ ORDER BY create_time DESC , company_id DESC  `
 	total, err = o.Raw(sql, dateTime).QueryRows(&items)
 	return
 }
+
+// GetIncrementalCompanyListByOperationRecord 获取未续约客户报表列表数据(根据新增客户时间来展示)
+func GetIncrementalCompanyListByOperationRecord(condition string, pars []interface{}) (items []*IncrementalList, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT a.id company_operation_record_id,a.company_id,b.company_name,c.seller_id,c.seller_name,
+a.product_id,a.product_name,a.create_time,b.region_type,c.renewal_reason FROM company_operation_record a
+		RIGHT JOIN company b ON a.company_id = b.company_id 
+		 JOIN company_product c ON b.company_id = c.company_id and a.product_id=c.product_id WHERE 1 = 1 `
+
+	if condition != "" {
+		sql += condition
+	}
+	sql = `select * from  (` + sql + ` order by create_time asc) f group by company_id,product_id order by create_time desc,company_id desc `
+	_, err = o.Raw(sql, pars).QueryRows(&items)
+	return
+}
+
+// GetTodayStackCompanyList 获取当天存量客户报表列表数据(新签客户,根据合同来展示)
+func GetTodayStackCompanyList(condition string, pars []interface{}) (items []*IncrementalList, err error) {
+	o := orm.NewOrm()
+
+	sql := `SELECT a.*,b.region_type,c.seller_id,c.seller_name,b.company_name,c.renewal_reason FROM company_contract a
+		 JOIN company b ON a.company_id = b.company_id
+		 JOIN company_product c ON a.company_id = c.company_id and a.product_id=c.product_id WHERE a.status = 1 `
+
+	if condition != "" {
+		sql += condition
+	}
+	sql += " order by a.start_date desc "
+	sql = `select * from (` + sql + `) b  order by start_date desc,company_id desc`
+	_, err = o.Raw(sql, pars).QueryRows(&items)
+	return
+}
+
+// GetTodayStackCompanyListV2 获取当天存量客户报表列表数据(续约客户,根据合同来展示)
+func GetTodayStackCompanyListV2(condition string, pars []interface{}) (items []*IncrementalList, err error) {
+	o := orm.NewOrm()
+
+	sql1 := `SELECT a.*,b.region_type,c.seller_id,c.seller_name,b.company_name,c.renewal_reason FROM company_contract a
+		 JOIN company b ON a.company_id = b.company_id
+		 JOIN company_product c ON a.company_id = c.company_id and a.product_id=c.product_id WHERE a.status = 1 and a.product_id=1 `
+	if condition != "" {
+		sql1 += condition
+	}
+	sql1 += ` and a.company_id not in (select company_id from company_contract where contract_type="新签合同" and status=1 and product_id=1 and end_date > ?) `
+
+	sql2 := `SELECT a.*,b.region_type,c.seller_id,c.seller_name,b.company_name,c.renewal_reason FROM company_contract a
+		 JOIN company b ON a.company_id = b.company_id
+		 JOIN company_product c ON a.company_id = c.company_id and a.product_id=c.product_id WHERE a.status = 1 and a.product_id=2 `
+	if condition != "" {
+		sql2 += condition
+	}
+	sql2 += ` and a.company_id not in (select company_id from company_contract where contract_type="新签合同" and status=1 and product_id=2 and end_date > ?) `
+
+	sql := `select * from (` + sql1 + ` union (` + sql2 + `)  ` + `) f order by start_date desc`
+	sql = `select * from (` + sql + `) b order by start_date desc,company_id desc`
+
+	pars = append(pars, pars...)
+	_, err = o.Raw(sql, pars).QueryRows(&items)
+	return
+}

+ 7 - 0
models/users.go

@@ -74,3 +74,10 @@ func ModifyUserLastViewTime(uid int, lastViewTime string) (err error) {
 	_, err = o.Raw(sql, lastViewTime, uid).Exec()
 	return
 }
+
+// GetUserOpenidListByUserId 根据用户id来获取他的openid列表集合
+func GetUserOpenidListByUserId(userId int) (list []*OpenIdList, err error) {
+	sql := `SELECT open_id FROM user_record WHERE user_id = ? and create_platform = 1`
+	_, err = orm.NewOrm().Raw(sql, userId).QueryRows(&list)
+	return
+}

+ 9 - 8
models/wechat.go

@@ -22,7 +22,7 @@ func GetWxAccessToken() (accessTokenStr string, err error) {
 	wxToken := new(WxToken)
 	err = o.Raw(sql).QueryRow(&wxToken)
 	if err != nil && err.Error() != utils.ErrNoRow() {
-		utils.FileLog.Info("Get wxToken Err:", err.Error())
+		utils.FileLog.Info(fmt.Sprintf("Get wxToken Err:", err.Error()))
 		return
 	}
 	//Token不存在
@@ -60,8 +60,8 @@ func GetWxAccessToken() (accessTokenStr string, err error) {
 }
 
 type WxAccessToken struct {
-	AccessToken string
-	ExpiresIn   int
+	AccessToken string `json:"access_token"`
+	ExpiresIn   int    `json:"expires_in"`
 	Errcode     int
 	Errmsg      string
 }
@@ -70,20 +70,21 @@ func GetWxToken() (item *WxAccessToken, err error) {
 	getUrl := "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" + utils.WxAppId + "&secret=" + utils.WxAppSecret
 	result, err := http.Get(getUrl)
 	if err != nil {
-		utils.FileLog.Info("GetWxToken Err:", err.Error())
+		utils.FileLog.Info(fmt.Sprintf("GetWxToken Err:", err.Error()))
 		return
 	}
 	if string(result) == "" {
-		utils.FileLog.Info("GetWxToken: %s", string(result))
+		utils.FileLog.Info(fmt.Sprintf("GetWxToken: %s", string(result)))
 	}
 	fmt.Println("result", string(result))
-	err = json.Unmarshal(result, item)
+	//必须要初始化该变量,要不然下面会报错
+	err = json.Unmarshal(result, &item)
 	if err != nil {
-		utils.FileLog.Info("GetWxToken Unmarshal Err: %s ,%s", string(result), err.Error())
+		utils.FileLog.Info(fmt.Sprintf("GetWxToken Unmarshal Err: %s ,%s", string(result), err.Error()))
 		return
 	}
 	if item.Errmsg != "" {
-		utils.FileLog.Info("GetWxToken fail result:%s", string(result))
+		utils.FileLog.Info(fmt.Sprintf("GetWxToken fail result:%s", string(result)))
 	}
 	return
 }

+ 145 - 3
services/company_statistic.go

@@ -1,15 +1,15 @@
 package services
 
 import (
+	"context"
 	"fmt"
 	"hongze/hongze_task/models"
 	"hongze/hongze_task/utils"
 	"time"
-	"context"
 )
 
-//存量客户数据统计
-func StackCompanyStatistic(cont context.Context) (err error) {
+// StackCompanyStatisticOld 存量客户数据统计(2021-10-26 14:12:27 过期失效)
+func StackCompanyStatisticOld(cont context.Context) (err error) {
 	defer func() {
 		if err != nil {
 			go utils.SendEmail(utils.APPNAME+"【"+utils.RunMode+"】"+"失败提醒", "存量客户数据统计 ErrMsg:"+err.Error(), utils.EmailSendToUsers)
@@ -110,3 +110,145 @@ func StackCompanyStatistic(cont context.Context) (err error) {
 	}
 	return
 }
+
+// StackCompanyStatistic 存量客户数据统计
+func StackCompanyStatistic(cont context.Context) (err error) {
+	defer func() {
+		if err != nil {
+			go utils.SendEmail(utils.APPNAME+"【"+utils.RunMode+"】"+"失败提醒", "存量客户数据统计 ErrMsg:"+err.Error(), utils.EmailSendToUsers)
+		}
+	}()
+	dayStr := time.Now().AddDate(0, 0, -1).Format(utils.FormatDate) //截止到昨天的数据
+	//查询昨天的数据有没有生成,如果没有生成的话,那么重新生成
+	count, err := models.GetStackCompanyCount(dayStr)
+	if err != nil {
+		fmt.Println("查询昨天的数据是否生成语句 执行异常:", err.Error())
+		return
+	}
+	//昨天数据有生成,那么就不往下执行了
+	if count > 0 {
+		fmt.Println("昨天的数据已经生成,不允许重复生成")
+		return
+	}
+
+	//新签客户
+	{
+		var condition1 string
+		var pars1 []interface{}
+		condition1 += ` AND a.start_date <= ? AND a.end_date >= ? `
+		pars1 = append(pars1, dayStr, dayStr)
+		condition1 += ` AND a.contract_type = ? `
+		pars1 = append(pars1, "新签合同")
+		list, countErr := models.GetTodayStackCompanyList(condition1, pars1)
+		if countErr != nil {
+			err = countErr
+			return
+		}
+
+		for _, companyInfo := range list {
+			item := models.StackCompanyStatistic{
+				CompanyId:    companyInfo.CompanyId,
+				CompanyName:  companyInfo.CompanyName,
+				ProductId:    companyInfo.ProductId,
+				ProductName:  companyInfo.ProductName,
+				ContractId:   companyInfo.CompanyContractId,
+				SellerId:     companyInfo.SellerId,
+				SellerName:   companyInfo.SellerName,
+				GroupId:      companyInfo.GroupId,
+				DepartmentId: companyInfo.DepartmentId,
+				Date:         dayStr, //截止到昨天的数据
+				StartDate:    companyInfo.StartDate,
+				EndDate:      companyInfo.EndDate,
+				RegionType:   companyInfo.RegionType,
+				CreateTime:   time.Now(),
+				Type:         "新签客户",
+			}
+			addErr := models.AddStackCompanyStatistic(&item)
+			if addErr != nil {
+				fmt.Println("存量新签客户数据统计,插入数据异常:", addErr)
+			}
+		}
+	}
+
+	//续约客户
+	{
+		var condition1 string
+		var pars1 []interface{}
+		condition1 += ` AND a.start_date <= ? AND a.end_date >= ? `
+		pars1 = append(pars1, dayStr, dayStr)
+		condition1 += ` AND a.contract_type = ? `
+		pars1 = append(pars1, "续约合同")
+		//额外条件(续约合同的起始日期包含在所选时间段内且不包含在新签合同存续期内的客户)
+		pars1 = append(pars1, dayStr)
+		list, countErr := models.GetTodayStackCompanyListV2(condition1, pars1)
+		if countErr != nil {
+			err = countErr
+			return
+		}
+
+		for _, companyInfo := range list {
+			item := models.StackCompanyStatistic{
+				CompanyId:    companyInfo.CompanyId,
+				CompanyName:  companyInfo.CompanyName,
+				ProductId:    companyInfo.ProductId,
+				ProductName:  companyInfo.ProductName,
+				ContractId:   companyInfo.CompanyContractId,
+				SellerId:     companyInfo.SellerId,
+				SellerName:   companyInfo.SellerName,
+				GroupId:      companyInfo.GroupId,
+				DepartmentId: companyInfo.DepartmentId,
+				Date:         dayStr, //截止到昨天的数据
+				StartDate:    companyInfo.StartDate,
+				EndDate:      companyInfo.EndDate,
+				RegionType:   companyInfo.RegionType,
+				CreateTime:   time.Now(),
+				Type:         "续约客户",
+			}
+			addErr := models.AddStackCompanyStatistic(&item)
+			if addErr != nil {
+				fmt.Println("存量续约客户数据统计,插入数据异常:", addErr)
+			}
+		}
+	}
+
+	//未续约客户
+	{
+		var condition1 string
+		var pars1 []interface{}
+		condition1 += ` AND c.status not in ("永续","正式") AND a.create_time <= ? `
+		pars1 = append(pars1, time.Now().Format(utils.FormatDateTime))
+		condition1 += ` AND a.operation = 'try_out' `
+
+		list, countErr := models.GetIncrementalCompanyListByOperationRecord(condition1, pars1)
+		if countErr != nil {
+			err = countErr
+			return
+		}
+
+		for _, companyInfo := range list {
+			item := models.StackCompanyStatistic{
+				CompanyId:    companyInfo.CompanyId,
+				CompanyName:  companyInfo.CompanyName,
+				ProductId:    companyInfo.ProductId,
+				ProductName:  companyInfo.ProductName,
+				ContractId:   companyInfo.CompanyOperationRecordId,
+				SellerId:     companyInfo.SellerId,
+				SellerName:   companyInfo.SellerName,
+				GroupId:      companyInfo.GroupId,
+				DepartmentId: companyInfo.DepartmentId,
+				Date:         dayStr, //截止到昨天的数据
+				StartDate:    companyInfo.StartDate,
+				EndDate:      companyInfo.EndDate,
+				RegionType:   companyInfo.RegionType,
+				CreateTime:   time.Now(),
+				Type:         "未续约客户",
+			}
+			addErr := models.AddStackCompanyStatistic(&item)
+			if addErr != nil {
+				fmt.Println("存量未续约客户数据统计,插入数据异常:", addErr)
+			}
+		}
+	}
+
+	return
+}

+ 442 - 3
services/task.go

@@ -2,14 +2,18 @@ package services
 
 import (
 	"context"
+	"errors"
 	"fmt"
+	"github.com/beego/beego/v2/task"
+	"hongze/hongze_task/models"
 	"hongze/hongze_task/services/company_contract"
 	"hongze/hongze_task/services/data"
 	"hongze/hongze_task/utils"
+	"strconv"
+	"strings"
 	"sync"
 	"time"
 
-	"github.com/beego/beego/v2/task"
 )
 
 func Task() {
@@ -57,6 +61,12 @@ func Task() {
 	//LzExportExcel()
 	//GetLzProductList()GetLzProductDetail
 
+	// 定时新增手工指标数据提醒
+	addEdbTask := task.NewTask("sendWaitReport", "1 0 0 * * * ", AddEdbTask)
+	task.AddTask("定时新增手工指标数据提醒", addEdbTask)
+	//每次服务启动都需要执行一次的
+	_ = AddEdbTask(nil)
+
 	fmt.Println("task end")
 }
 
@@ -88,8 +98,8 @@ func releaseTask() {
 	sendEmail := task.NewTask("sendEmail", "0 0 12 * * 0 ", SendEmail)
 	task.AddTask("sendEmail", sendEmail)
 
-	oneMinute := task.NewTask("oneMinute", "0 */1 7-23 * * * ", OneMinute)
-	task.AddTask("oneMinute", oneMinute)
+	//oneMinute := task.NewTask("oneMinute", "0 */1 7-23 * * * ", OneMinute)
+	//task.AddTask("oneMinute", oneMinute)
 
 	// 正式/试用 用户到期提醒
 	companyRemind := task.NewTask("companyRemind", "0 30 08 * * *", CompanyRemind)
@@ -245,3 +255,432 @@ endData:=time.Now().UnixNano()/1e6
 	dateTime:=time.Unix(endData/1000,0)
 	fmt.Println(dateTime)
 */
+
+// EdbTaskNameMap 手工指标定时任务名称map集合
+var EdbTaskNameMap map[string]map[string]bool
+
+// EdbTaskNameChannel 手工指标定时任务名称channel
+var EdbTaskNameChannel chan string
+
+// EdbTaskStopChannel 手工指标定时任务停止channel
+var EdbTaskStopChannel chan string
+
+// EdbTaskRunNum 手工指标定时任务开始次数
+var EdbTaskRunNum int
+
+// AddEdbTask 新增手工指标数据录入提醒
+func AddEdbTask(cont context.Context) (err error) {
+	//func AddEdbTask() (err error) {
+	list, err := models.GetEdbInfoByFrequencyNotDay()
+	if err != nil {
+		fmt.Println("查询获取频度非日度 且 提醒时间不为空 的指标数据失败,Err:", err.Error())
+	}
+	//如果还没有初始化map,那么先初始
+	if EdbTaskNameMap == nil {
+		EdbTaskNameMap = make(map[string]map[string]bool)
+	}
+	tmpEdbTaskNameMap := make(map[string]bool)
+
+	// 今天的日期字符串(格式:2021-10-25)
+	todayStr := time.Now().Format(utils.FormatDate)
+
+	//当前周的周一与周日
+	nowWeekFirstDay := utils.GetNowWeekMonday()
+	nowWeekLastDay := utils.GetNowWeekLastDay()
+
+	//当前月的一号与最后一天
+	nowMonthFirstDay := utils.GetNowMonthFirstDay()
+	nowMonthLastDay := utils.GetNowMonthLastDay()
+
+	//当前季度的第一天与最后一天
+	nowQuarterFirstDay := utils.GetNowQuarterFirstDay()
+	nowQuarterLastDay := utils.GetNowQuarterLastDay()
+
+	//当前半年的第一天与最后一天
+	nowHalfYearFirstDay := utils.GetNowHalfYearFirstDay()
+	nowHalfYearLastDay := utils.GetNowHalfYearLastDay()
+
+	// 当前年的第一天与最后一天
+	nowYearFirstDay := utils.GetNowYearFirstDay()
+	nowYearLastDay := utils.GetNowYearLastDay()
+
+	//失败列表
+	failList := make([]string, 0)
+
+	debugNoticeUserId := 0 //测试环境,需要发送消息的用户
+	if utils.RunMode == "debug" {
+		tmpWxUser, tmpErr := models.GetWxUserByMobile("17634786714")
+		if tmpErr == nil && tmpWxUser != nil {
+			//debugNoticeUserId = 44078 //测试环境的话,发送邮箱给颜鹏
+			debugNoticeUserId = int(tmpWxUser.UserId) //测试环境的话,发送邮箱给嘉豪
+		}
+	}
+	//task.globalTaskManager.adminTaskList
+	for _, edb := range list {
+		tmpEdb := edb            //指标信息
+		isNotice := false        //是否需要提醒
+		noticeTime := "12:00:00" //提醒时间
+
+		var dataDtTime time.Time
+		edbData, tmpErr := models.GetLastEdbdataInfo(edb.TradeCode)
+		if tmpErr != nil {
+			if tmpErr.Error() != utils.ErrNoRow() {
+				failList = append(failList, fmt.Sprint(edb.TradeCode, "失败,Err:", tmpErr.Error()))
+				continue
+			}
+		}
+
+		//如果确实是有数据的
+		if edbData != nil {
+			tmpDataDtTime, _ := time.ParseInLocation(utils.FormatDate, edbData.Dt, time.Now().Location())
+			dataDtTime = tmpDataDtTime
+		}
+
+		switch edb.Frequency {
+		case "周度":
+			modifyDate := nowWeekLastDay //下次更新日期
+			if edb.NoticeTime != "" {
+				addDay := 7
+				noticeArr := strings.Split(edb.NoticeTime, " ")
+				if len(noticeArr) >= 2 {
+					noticeTime = noticeArr[1]
+				}
+				noticeWeek := noticeArr[0]
+				switch noticeWeek {
+				case "周一":
+					addDay = 1
+				case "周二":
+					addDay = 2
+				case "周三":
+					addDay = 3
+				case "周四":
+					addDay = 4
+				case "周五":
+					addDay = 5
+				case "周六":
+					addDay = 6
+				case "周日":
+					addDay = 7
+				}
+				modifyDate = modifyDate.AddDate(0, 0, addDay-7)
+			}
+
+			//如果正好是提醒日,同时本周没有过记录,那么需要提醒
+			if todayStr == modifyDate.Format(utils.FormatDate) && !nowWeekFirstDay.Before(dataDtTime) {
+				isNotice = true
+			}
+		case "月度":
+			addDay := 0
+			modifyDate := nowMonthLastDay //下次更新日期
+			if edb.NoticeTime != "" {
+				strArr := strings.Split(edb.NoticeTime, "日")
+				if len(strArr) >= 2 {
+					noticeTime = strArr[1]
+				}
+				tmpAddDay, tmpErr := strconv.Atoi(strArr[0])
+				if tmpErr != nil {
+					continue
+				}
+				addDay = tmpAddDay - 1
+				modifyDate = nowMonthFirstDay.AddDate(0, 0, addDay)
+			}
+
+			//如果正好是提醒日,同时本月没有过记录,那么需要提醒
+			if todayStr == modifyDate.Format(utils.FormatDate) && !nowMonthFirstDay.Before(dataDtTime) {
+				isNotice = true
+			}
+		case "季度":
+			//提醒时间
+			if edb.NoticeTime != "" {
+				noticeArr := strings.Split(edb.NoticeTime, " ")
+				if len(noticeArr) >= 2 {
+					noticeTime = noticeArr[1]
+				}
+			}
+			//每季度更新数据时间
+			//如果正好是提醒日(每季度最后一天),同时本季度没有过记录,那么需要提醒
+			if todayStr == nowQuarterLastDay.Format(utils.FormatDate) && !nowQuarterFirstDay.Before(dataDtTime) {
+				isNotice = true
+			}
+		case "半年度":
+			//提醒时间
+			if edb.NoticeTime != "" {
+				noticeArr := strings.Split(edb.NoticeTime, " ")
+				if len(noticeArr) >= 2 {
+					noticeTime = noticeArr[1]
+				}
+			}
+			//每半年度更新数据时间
+			//如果正好是提醒日(每半年度最后一天),同时本半年度没有过记录,那么需要提醒
+			if todayStr == nowHalfYearLastDay.Format(utils.FormatDate) && !nowHalfYearFirstDay.Before(dataDtTime) {
+				isNotice = true
+			}
+		case "年度":
+			//提醒时间
+			if edb.NoticeTime != "" {
+				noticeArr := strings.Split(edb.NoticeTime, " ")
+				if len(noticeArr) >= 2 {
+					noticeTime = noticeArr[1]
+				}
+			}
+			//每年度更新数据时间
+			//如果正好是提醒日(每年度最后一天),同时半年度没有过记录,那么需要提醒
+			if todayStr == nowYearLastDay.Format(utils.FormatDate) && !nowYearFirstDay.Before(dataDtTime) {
+				isNotice = true
+			}
+		}
+
+		if isNotice {
+			taskName := "edb_task_" + todayStr + ":" + fmt.Sprint(edb.TradeCode)
+			fmt.Println(taskName, ";", edb.SecName)
+
+			//定时任务
+			tmpTaskFunc := func(ctx context.Context) (funcErr error) {
+				//方法执行结束后,移除定时任务
+				defer func() {
+					EdbTaskNameChannel <- taskName
+				}()
+				// 匿名方法内判断是否发送提醒,因为可能时间到的时候,发现
+				funcIsNotice := false
+				// 再次获取指标数据详情
+				edbData, tmpErr := models.GetLastEdbdataInfo(tmpEdb.TradeCode)
+				if tmpErr != nil {
+					if tmpErr.Error() != utils.ErrNoRow() {
+						funcErr = tmpErr
+						return
+					}
+				}
+				if utils.RunMode == "debug" && debugNoticeUserId > 0 {
+					tmpEdb.UserId = debugNoticeUserId //测试环境的话,发送邮箱给嘉豪
+				}
+
+				//数据过期时间
+				var funcDataDtTime time.Time
+				//如果确实是有数据的
+				if edbData != nil {
+					tmpDataDtTime, _ := time.ParseInLocation(utils.FormatDate, edbData.Dt, time.Now().Location())
+					funcDataDtTime = tmpDataDtTime
+				}
+
+				switch tmpEdb.Frequency {
+				case "周度":
+					modifyDate := nowWeekLastDay //下次更新日期
+					if tmpEdb.NoticeTime != "" {
+						addDay := 7
+						noticeArr := strings.Split(tmpEdb.NoticeTime, " ")
+						if len(noticeArr) >= 2 {
+							noticeTime = noticeArr[1]
+						}
+						noticeWeek := noticeArr[0]
+						switch noticeWeek {
+						case "周一":
+							addDay = 1
+						case "周二":
+							addDay = 2
+						case "周三":
+							addDay = 3
+						case "周四":
+							addDay = 4
+						case "周五":
+							addDay = 5
+						case "周六":
+							addDay = 6
+						case "周日":
+							addDay = 7
+						}
+						modifyDate = modifyDate.AddDate(0, 0, addDay-7)
+					}
+
+					//如果正好是提醒日,同时本周没有过记录,那么需要提醒
+					if todayStr == modifyDate.Format(utils.FormatDate) && !nowWeekFirstDay.Before(funcDataDtTime) {
+						funcIsNotice = true
+					}
+				case "月度":
+					addDay := 0
+					modifyDate := nowMonthLastDay //下次更新日期
+					if tmpEdb.NoticeTime != "" {
+						strArr := strings.Split(tmpEdb.NoticeTime, "日")
+						if len(strArr) >= 2 {
+							noticeTime = strArr[1]
+						}
+						tmpAddDay, tmpErr := strconv.Atoi(strArr[0])
+						if tmpErr != nil {
+							funcErr = tmpErr
+						}
+						addDay = tmpAddDay - 1
+						modifyDate = nowMonthFirstDay.AddDate(0, 0, addDay)
+					}
+
+					//如果正好是提醒日,同时本月没有过记录,那么需要提醒
+					if todayStr == modifyDate.Format(utils.FormatDate) && !nowMonthFirstDay.Before(funcDataDtTime) {
+						funcIsNotice = true
+					}
+				case "季度":
+					//提醒时间
+					if tmpEdb.NoticeTime != "" {
+						noticeArr := strings.Split(tmpEdb.NoticeTime, " ")
+						if len(noticeArr) >= 2 {
+							noticeTime = noticeArr[1]
+						}
+					}
+					//每季度更新数据时间
+					//如果正好是提醒日(每季度最后一天),同时本季度没有过记录,那么需要提醒
+					if todayStr == nowQuarterLastDay.Format(utils.FormatDate) && !nowQuarterFirstDay.Before(funcDataDtTime) {
+						funcIsNotice = true
+					}
+				case "半年度":
+					//提醒时间
+					if tmpEdb.NoticeTime != "" {
+						noticeArr := strings.Split(tmpEdb.NoticeTime, " ")
+						if len(noticeArr) >= 2 {
+							noticeTime = noticeArr[1]
+						}
+					}
+					//每半年度更新数据时间
+					//如果正好是提醒日(每半年度最后一天),同时本半年度没有过记录,那么需要提醒
+					if todayStr == nowHalfYearLastDay.Format(utils.FormatDate) && !nowHalfYearFirstDay.Before(funcDataDtTime) {
+						funcIsNotice = true
+					}
+				case "年度":
+					//提醒时间
+					if tmpEdb.NoticeTime != "" {
+						noticeArr := strings.Split(tmpEdb.NoticeTime, " ")
+						if len(noticeArr) >= 2 {
+							noticeTime = noticeArr[1]
+						}
+					}
+					//每年度更新数据时间
+					//如果正好是提醒日(每年度最后一天),同时半年度没有过记录,那么需要提醒
+					if todayStr == nowYearLastDay.Format(utils.FormatDate) && !nowYearFirstDay.Before(funcDataDtTime) {
+						funcIsNotice = true
+					}
+				}
+
+				fmt.Println(tmpEdb.TradeCode, " funcIsNotice:", funcIsNotice)
+				//如果还是要提醒
+				if funcIsNotice {
+					//用户微信openid列表数据
+					openIdList := make([]*models.OpenIdList, 0)
+
+					//获取用户信息
+					isAdmin := true
+					admin, err := models.GetAdminByAdminId(tmpEdb.UserId)
+					if err != nil {
+						if err.Error() == utils.ErrNoRow() {
+							isAdmin = false
+						} else {
+							return err
+						}
+					}
+					if admin == nil {
+						isAdmin = false
+					}
+					if isAdmin {
+						if admin.Mobile == "" {
+
+						} else {
+							wxUser, err := models.GetWxUserByMobile(admin.Mobile)
+							if err != nil {
+								return err
+							}
+							if wxUser == nil {
+								funcErr = errors.New("用户信息不存在:mobile:" + admin.Mobile)
+								return err
+							}
+							tmpOpenidList, err := models.GetUserOpenidListByUserId(int(wxUser.UserId))
+							if err != nil {
+								return err
+							}
+							openIdList = tmpOpenidList
+						}
+					} else {
+						tmpOpenidList, err := models.GetUserOpenidListByUserId(tmpEdb.UserId)
+						if err != nil {
+							return err
+						}
+						openIdList = tmpOpenidList
+					}
+					//发送消息
+					if len(openIdList) <= 0 {
+						funcErr = errors.New("openId 列表为空" + strconv.Itoa(tmpEdb.UserId))
+						return
+					}
+
+					first := "数据录入提醒"
+					keyword1 := tmpEdb.SecName
+					keyword2 := "每周 " + edb.NoticeTime
+					remark := tmpEdb.SecName + "该更新了"
+
+					err = SendWxMsgWithFrequency(first, keyword1, keyword2, remark, openIdList)
+					if err != nil {
+						return err
+					}
+					//发送成功,记录发送日志
+					{
+						sendRecord := new(models.EdbinfoSendMsgRecord)
+						sendRecord.UserId = tmpEdb.UserId
+						sendRecord.TradeCode = tmpEdb.TradeCode
+						sendRecord.CreateTime = time.Now()
+						err = models.AddEdbinfoSendMsgRecord(sendRecord)
+						if err != nil {
+							return err
+						}
+					}
+				}
+				return
+			}
+
+			//添加定时任务
+			spec := ``
+			if noticeTime != "" {
+				noticeArr := strings.Split(noticeTime, ":")
+				if len(noticeArr) == 3 {
+					//spec = ` */20 * * * * * `
+					spec = fmt.Sprintf(` %s %s %s * * * `, noticeArr[2], noticeArr[1], noticeArr[0])
+				}
+			}
+			//定时任务开始的时间
+			tmpTask := task.NewTask(taskName, spec, tmpTaskFunc)
+
+			task.AddTask(taskName, tmpTask)
+			tmpEdbTaskNameMap[taskName] = true
+		}
+	}
+
+	//将当天的手工指标加入到手工指标池去
+	EdbTaskNameMap[todayStr] = tmpEdbTaskNameMap
+	//开启协程,用来清除定时任务
+	go deleteTask()
+
+	//如果当前定时任务执行次数大于0次,那么需要往手工指标定时任务停止channel写入数据,用来关闭昨天没有执行的的定时任务
+	if EdbTaskRunNum > 0 {
+		//清除昨天的数据
+		EdbTaskStopChannel <- time.Now().AddDate(0, 0, -1).Format(utils.FormatDate)
+	}
+	//手工指标定时任务开始次数累加
+	EdbTaskRunNum++
+
+	for _, v := range failList {
+		fmt.Println(v)
+	}
+	return
+	//fmt.Println(task.NewMapSorter())
+}
+
+// deleteTask 清除已通知的任务
+func deleteTask() {
+	for {
+		select {
+		case taskName := <-EdbTaskNameChannel:
+			task.DeleteTask(taskName)
+			delete(EdbTaskNameMap, taskName)
+
+		case dayStr := <-EdbTaskStopChannel: //收到停止信号,先清除掉那一天的定时任务,
+			for taskName := range EdbTaskNameMap[dayStr] {
+				task.DeleteTask(taskName)
+				delete(EdbTaskNameMap, taskName)
+			}
+			break
+		}
+	}
+}

+ 20 - 21
services/wx_template_msg.go

@@ -20,7 +20,7 @@ func SendWxMsgWithFrequency(first, keyword1, keyword2, remark string, openIdList
 		}
 		fmt.Println("line 21", err, msg)
 	}()
-	utils.FileLog.Info("%s", "services SendMsg")
+	utils.FileLog.Info("services SendMsg")
 	accessToken, err := models.GetWxAccessToken()
 	if err != nil {
 		msg = "GetWxAccessToken Err:" + err.Error()
@@ -87,37 +87,36 @@ func SendTemplateMsg(sendUrl string, data []byte) (err error) {
 	return
 }
 
-
 //到期提醒模板消息
-func SendWxMsgWithCompanyRemind(first,keyword1,keyword2,remark string,openIdList []*models.OpenIdList)(err error) {
+func SendWxMsgWithCompanyRemind(first, keyword1, keyword2, remark string, openIdList []*models.OpenIdList) (err error) {
 	var msg string
 	defer func() {
-		if err!=nil {
+		if err != nil {
 			go utils.SendEmail("发送模版消息失败"+"【"+utils.APPNAME+"】"+time.Now().Format("2006-01-02 15:04:05"), msg+";Err:"+err.Error(), utils.EmailSendToUsers)
-			utils.FileLog.Info("发送模版消息失败,Err:%s",err.Error())
+			utils.FileLog.Info("发送模版消息失败,Err:%s", err.Error())
 		}
 	}()
-	utils.FileLog.Info("%s","services SendMsg")
-	accessToken,err:=models.GetWxAccessToken()
-	if err!=nil {
-		msg="GetWxAccessToken Err:"+err.Error()
+	utils.FileLog.Info("%s", "services SendMsg")
+	accessToken, err := models.GetWxAccessToken()
+	if err != nil {
+		msg = "GetWxAccessToken Err:" + err.Error()
 		return
 	}
-	if accessToken ==""{
-		msg="accessToken is empty"
+	if accessToken == "" {
+		msg = "accessToken is empty"
 		return
 	}
 
-	sendUrl:= "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token="+accessToken
-	sendMap:=make(map[string]interface{})
-	sendData:=make(map[string]interface{})
+	sendUrl := "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=" + accessToken
+	sendMap := make(map[string]interface{})
+	sendData := make(map[string]interface{})
 
-	sendMap["template_id"]=utils.RemindTemplateId
-	sendData["first"]=map[string]interface{}{"value":first,"color":"#173177"}
-	sendData["productname"]=map[string]interface{}{"value":keyword1,"color":"#173177"}
-	sendData["date"]=map[string]interface{}{"value":keyword2,"color":"#173177"}
-	sendData["remark"]=map[string]interface{}{"value":remark,"color":"#173177"}
-	sendMap["data"]=sendData
-	WxSendTemplateMsg(sendUrl,sendMap,openIdList)
+	sendMap["template_id"] = utils.RemindTemplateId
+	sendData["first"] = map[string]interface{}{"value": first, "color": "#173177"}
+	sendData["productname"] = map[string]interface{}{"value": keyword1, "color": "#173177"}
+	sendData["date"] = map[string]interface{}{"value": keyword2, "color": "#173177"}
+	sendData["remark"] = map[string]interface{}{"value": remark, "color": "#173177"}
+	sendMap["data"] = sendData
+	WxSendTemplateMsg(sendUrl, sendMap, openIdList)
 	return
 }

+ 224 - 17
utils/common.go

@@ -1,4 +1,3 @@
-
 package utils
 
 import (
@@ -7,6 +6,7 @@ import (
 	"encoding/base64"
 	"encoding/hex"
 	"encoding/json"
+	"errors"
 	"fmt"
 	"image"
 	"image/png"
@@ -291,22 +291,22 @@ func SaveBase64ToFile(content, path string) error {
 
 func SaveBase64ToFileBySeek(content, path string) (err error) {
 	data, err := base64.StdEncoding.DecodeString(content)
-	exist,err:=PathExists(path)
-	if err!=nil {
+	exist, err := PathExists(path)
+	if err != nil {
 		return
 	}
 	if !exist {
 		f, err := os.Create(path)
-		if err!=nil {
+		if err != nil {
 			return err
 		}
 		n, _ := f.Seek(0, 2)
 		// 从末尾的偏移量开始写入内容
 		_, err = f.WriteAt([]byte(data), n)
 		defer f.Close()
-	}else{
+	} else {
 		f, err := os.OpenFile(path, os.O_WRONLY, 0644)
-		if err!=nil {
+		if err != nil {
 			return err
 		}
 		n, _ := f.Seek(0, 2)
@@ -318,7 +318,7 @@ func SaveBase64ToFileBySeek(content, path string) (err error) {
 	return nil
 }
 
-func PathExists(path string) (bool,error) {
+func PathExists(path string) (bool, error) {
 	_, err := os.Stat(path)
 	if err == nil {
 		return true, nil
@@ -488,33 +488,240 @@ func Sha1(data string) string {
 	return hex.EncodeToString(sha1.Sum([]byte("")))
 }
 
-func GetWeekDay() (weekStr string){
+func GetWeekDay() (weekStr string) {
 	nowWeek := time.Now().Weekday().String()
 	switch nowWeek {
 	case "Monday":
-		weekStr="周一"
+		weekStr = "周一"
 		break
 	case "Tuesday":
-		weekStr="周二"
+		weekStr = "周二"
 		break
 	case "Wednesday":
-		weekStr="周三"
+		weekStr = "周三"
 		break
 	case "Thursday":
-		weekStr="周四"
+		weekStr = "周四"
 		break
 	case "Friday":
-		weekStr="周五"
+		weekStr = "周五"
 		break
 	case "Saturday":
-		weekStr="周六"
+		weekStr = "周六"
 		break
 	case "Sunday":
-		weekStr="周日"
+		weekStr = "周日"
 		break
 	default:
-		weekStr=""
+		weekStr = ""
 		break
 	}
 	return
-}
+}
+
+// GetNowWeekMonday 获取本周周一的时间
+func GetNowWeekMonday() time.Time {
+	offset := int(time.Monday - time.Now().Weekday())
+	if offset == 1 { //正好是周日,但是按照中国人的理解,周日是一周最后一天,而不是一周开始的第一天
+		offset = -6
+	}
+	mondayTime := time.Now().AddDate(0, 0, offset)
+	mondayTime = time.Date(mondayTime.Year(), mondayTime.Month(), mondayTime.Day(), 0, 0, 0, 0, mondayTime.Location())
+	return mondayTime
+}
+
+// GetNowWeekLastDay 获取本周最后一天的时间
+func GetNowWeekLastDay() time.Time {
+	offset := int(time.Monday - time.Now().Weekday())
+	if offset == 1 { //正好是周日,但是按照中国人的理解,周日是一周最后一天,而不是一周开始的第一天
+		offset = -6
+	}
+	firstDayTime := time.Now().AddDate(0, 0, offset)
+	firstDayTime = time.Date(firstDayTime.Year(), firstDayTime.Month(), firstDayTime.Day(), 0, 0, 0, 0, firstDayTime.Location()).AddDate(0, 0, 6)
+	lastDayTime := time.Date(firstDayTime.Year(), firstDayTime.Month(), firstDayTime.Day(), 23, 59, 59, 0, firstDayTime.Location())
+
+	return lastDayTime
+}
+
+// GetNowMonthFirstDay 获取本月第一天的时间
+func GetNowMonthFirstDay() time.Time {
+	nowMonthFirstDay := time.Date(time.Now().Year(), time.Now().Month(), 1, 0, 0, 0, 0, time.Now().Location())
+	return nowMonthFirstDay
+}
+
+// GetNowMonthLastDay 获取本月最后一天的时间
+func GetNowMonthLastDay() time.Time {
+	nowMonthLastDay := time.Date(time.Now().Year(), time.Now().Month(), 1, 0, 0, 0, 0, time.Now().Location()).AddDate(0, 1, -1)
+	nowMonthLastDay = time.Date(nowMonthLastDay.Year(), nowMonthLastDay.Month(), nowMonthLastDay.Day(), 23, 59, 59, 0, nowMonthLastDay.Location())
+	return nowMonthLastDay
+}
+
+// GetNowQuarterFirstDay 获取本季度第一天的时间
+func GetNowQuarterFirstDay() time.Time {
+	month := int(time.Now().Month())
+	var nowQuarterFirstDay time.Time
+	if month >= 1 && month <= 3 {
+		//1月1号
+		nowQuarterFirstDay = time.Date(time.Now().Year(), 1, 1, 0, 0, 0, 0, time.Now().Location())
+	} else if month >= 4 && month <= 6 {
+		//4月1号
+		nowQuarterFirstDay = time.Date(time.Now().Year(), 4, 1, 0, 0, 0, 0, time.Now().Location())
+	} else if month >= 7 && month <= 9 {
+		nowQuarterFirstDay = time.Date(time.Now().Year(), 7, 1, 0, 0, 0, 0, time.Now().Location())
+	} else {
+		nowQuarterFirstDay = time.Date(time.Now().Year(), 10, 1, 0, 0, 0, 0, time.Now().Location())
+	}
+	return nowQuarterFirstDay
+}
+
+// GetNowQuarterLastDay 获取本季度最后一天的时间
+func GetNowQuarterLastDay() time.Time {
+	month := int(time.Now().Month())
+	var nowQuarterLastDay time.Time
+	if month >= 1 && month <= 3 {
+		//03-31 23:59:59
+		nowQuarterLastDay = time.Date(time.Now().Year(), 3, 31, 23, 59, 59, 0, time.Now().Location())
+	} else if month >= 4 && month <= 6 {
+		//06-30 23:59:59
+		nowQuarterLastDay = time.Date(time.Now().Year(), 6, 30, 23, 59, 59, 0, time.Now().Location())
+	} else if month >= 7 && month <= 9 {
+		//09-30 23:59:59
+		nowQuarterLastDay = time.Date(time.Now().Year(), 9, 30, 23, 59, 59, 0, time.Now().Location())
+	} else {
+		//12-31 23:59:59
+		nowQuarterLastDay = time.Date(time.Now().Year(), 12, 31, 23, 59, 59, 0, time.Now().Location())
+	}
+	return nowQuarterLastDay
+}
+
+// GetNowHalfYearFirstDay 获取当前半年的第一天的时间
+func GetNowHalfYearFirstDay() time.Time {
+	month := int(time.Now().Month())
+	var nowHalfYearLastDay time.Time
+	if month >= 1 && month <= 6 {
+		//03-31 23:59:59
+		nowHalfYearLastDay = time.Date(time.Now().Year(), 1, 1, 0, 0, 0, 0, time.Now().Location())
+	} else {
+		//12-31 23:59:59
+		nowHalfYearLastDay = time.Date(time.Now().Year(), 7, 1, 0, 0, 0, 0, time.Now().Location())
+	}
+	return nowHalfYearLastDay
+}
+
+// GetNowHalfYearLastDay 获取当前半年的最后一天的时间
+func GetNowHalfYearLastDay() time.Time {
+	month := int(time.Now().Month())
+	var nowHalfYearLastDay time.Time
+	if month >= 1 && month <= 6 {
+		//03-31 23:59:59
+		nowHalfYearLastDay = time.Date(time.Now().Year(), 6, 30, 23, 59, 59, 0, time.Now().Location())
+	} else {
+		//12-31 23:59:59
+		nowHalfYearLastDay = time.Date(time.Now().Year(), 12, 31, 23, 59, 59, 0, time.Now().Location())
+	}
+	return nowHalfYearLastDay
+}
+
+// GetNowYearFirstDay 获取当前年的最后一天的时间
+func GetNowYearFirstDay() time.Time {
+	//12-31 23:59:59
+	nowYearFirstDay := time.Date(time.Now().Year(), 1, 1, 0, 0, 0, 0, time.Now().Location())
+	return nowYearFirstDay
+}
+
+// GetNowYearLastDay 获取当前年的最后一天的时间
+func GetNowYearLastDay() time.Time {
+	//12-31 23:59:59
+	nowYearLastDay := time.Date(time.Now().Year(), 12, 31, 23, 59, 59, 0, time.Now().Location())
+	return nowYearLastDay
+}
+
+// CalculationDate 计算两个日期之间相差n年m月y天
+func CalculationDate(startDate, endDate time.Time) (beetweenDay string, err error) {
+	//startDate := time.Date(2021, 3, 28, 0, 0, 0, 0, time.Now().Location())
+	//endDate := time.Date(2022, 3, 31, 0, 0, 0, 0, time.Now().Location())
+	numYear := endDate.Year() - startDate.Year()
+
+	numMonth := int(endDate.Month()) - int(startDate.Month())
+
+	numDay := 0
+	//获取截止月的总天数
+	endDateDays := getMonthDay(endDate.Year(), int(endDate.Month()))
+
+	//获取截止月的前一个月
+	endDatePrevMonthDate := endDate.AddDate(0, -1, 0)
+	//获取截止日期的上一个月的总天数
+	endDatePrevMonthDays := getMonthDay(endDatePrevMonthDate.Year(), int(endDatePrevMonthDate.Month()))
+	//获取开始日期的的月份总天数
+	startDateMonthDays := getMonthDay(startDate.Year(), int(startDate.Month()))
+
+	//判断,截止月是否完全被选中,如果相等,那么代表截止月份全部天数被选择
+	if endDate.Day() == endDateDays {
+		numDay = startDateMonthDays - startDate.Day() + 1
+
+		//如果剩余天数正好与开始日期的天数是一致的,那么月份加1
+		if numDay == startDateMonthDays {
+			numMonth++
+			numDay = 0
+			//超过月份了,那么年份加1
+			if numMonth == 12 {
+				numYear++
+				numMonth = 0
+			}
+		}
+	} else {
+		numDay = endDate.Day() - startDate.Day() + 1
+	}
+
+	//天数小于0,那么向月份借一位
+	if numDay < 0 {
+		//向上一个月借一个月的天数
+		numDay += endDatePrevMonthDays
+
+		//总月份减去一个月
+		numMonth = numMonth - 1
+	}
+
+	//月份小于0,那么向年份借一位
+	if numMonth < 0 {
+		//向上一个年借12个月
+		numMonth += 12
+
+		//总年份减去一年
+		numYear = numYear - 1
+	}
+	if numYear < 0 {
+		err = errors.New("日期异常")
+		return
+	}
+
+	if numYear > 0 {
+		beetweenDay += fmt.Sprint(numYear, "年")
+	}
+	if numMonth > 0 {
+		beetweenDay += fmt.Sprint(numMonth, "个月")
+	}
+	if numDay > 0 {
+		beetweenDay += fmt.Sprint(numDay, "天")
+	}
+	return
+}
+
+// getMonthDay 获取某年某月有多少天
+func getMonthDay(year, month int) (days int) {
+	if month != 2 {
+		if month == 4 || month == 6 || month == 9 || month == 11 {
+			days = 30
+
+		} else {
+			days = 31
+		}
+	} else {
+		if ((year%4) == 0 && (year%100) != 0) || (year%400) == 0 {
+			days = 29
+		} else {
+			days = 28
+		}
+	}
+	return
+}

+ 1 - 1
utils/config.go

@@ -86,7 +86,7 @@ ZwIDAQAB
 		AdminId = 11
 		WxAppId = "wx9b5d7291e581233a"
 		WxAppSecret = "f4d52e34021eee262dce9682b31f8861"
-		TemplateId = "P0klzaZjEI2UYth-z-WnmtOQgyxcF8klPoA_MlsA8Eo"
+		TemplateId = "pDk4o924gSZWj80ZdNnHodnLMIXjPSlKZU0ciQMOhec"
 		RemindTemplateId = "9JYV6sHMJlu2EHRBIj_8ift6wkrrTb9_UO-M_-YXKBw"
 		//同花顺测试地址
 		THS_SendUrl = `https://mtest.10jqka.com.cn/gateway/ps/syncNews`