Bläddra i källkod

Merge branch 'crm/13.8'

hsun 1 år sedan
förälder
incheckning
f532337620

+ 15 - 2
models/db.go

@@ -6,6 +6,7 @@ import (
 	"hongze/hongze_task/models/company_contract"
 	"hongze/hongze_task/models/data_manage"
 	"hongze/hongze_task/models/data_manage/future_good"
+	"hongze/hongze_task/models/eta_business"
 	"hongze/hongze_task/models/roadshow"
 	"hongze/hongze_task/models/yb"
 	"hongze/hongze_task/utils"
@@ -94,6 +95,9 @@ func init() {
 
 	//注册持仓分析 数据表
 	initTradePositionTop()
+
+	// ETA商家数据表
+	initEtaBusiness()
 }
 
 // initCompany 注册客户信息 数据表
@@ -109,7 +113,7 @@ func initCompany() {
 		new(CompanyEndDate),
 		new(CompanyProductUpdateLog), //客户产品状态变更表
 		new(CompanyViewStatistics),
-		new(CompanyRemindRecord),	// 客户提醒记录
+		new(CompanyRemindRecord), // 客户提醒记录
 	)
 }
 
@@ -195,4 +199,13 @@ func initTradePositionTop() {
 		new(data_manage.TradePositionIneTop),
 		new(data_manage.TradePositionCffexTop),
 	)
-}
+}
+
+// initEtaBusiness ETA商家数据表
+func initEtaBusiness() {
+	orm.RegisterModel(
+		new(eta_business.EtaBusiness),
+		new(eta_business.EtaBusinessContract),
+		new(eta_business.EtaBusinessRemindRecord),
+	)
+}

+ 327 - 0
models/eta_business/eta_business.go

@@ -0,0 +1,327 @@
+package eta_business
+
+import (
+	"fmt"
+	"github.com/beego/beego/v2/client/orm"
+	"github.com/rdlucklib/rdluck_tools/paging"
+	"hongze/hongze_task/utils"
+	"strings"
+	"time"
+)
+
+const (
+	EtaBusinessSigningStatusFirst = iota + 1
+	EtaBusinessSigningStatusContinue
+	EtaBusinessSigningStatusTerminate
+)
+
+type EtaBusiness struct {
+	EtaBusinessId    int       `orm:"column(eta_business_id);pk"`
+	BusinessName     string    `description:"商家名称"`
+	BusinessCode     string    `description:"商家编码"`
+	CreditCode       string    `description:"社会统一信用码"`
+	RegionType       string    `description:"所属区域:国内;海外"`
+	Province         string    `description:"省份"`
+	City             string    `description:"城市"`
+	Address          string    `description:"商家地址"`
+	SellerId         int       `description:"销售ID"`
+	SellerName       string    `description:"销售名称"`
+	Leader           string    `description:"决策人"`
+	IndustryId       int       `description:"行业ID"`
+	IndustryName     string    `description:"行业名称"`
+	CapitalScale     string    `description:"资金规模"`
+	ResearchTeamSize string    `description:"研究团队规模"`
+	UserMax          int       `description:"用户上限"`
+	SigningStatus    int       `description:"签约状态:1-首次签约;2-续约中;3-已终止"`
+	Enable           int       `description:"状态:0-禁用;1-启用"`
+	ContractId       int       `description:"当前合约ID"`
+	SigningTime      time.Time `description:"当前合约的签约时间"`
+	ExpiredTime      time.Time `description:"当前合约的到期时间"`
+	CreateTime       time.Time `description:"创建时间"`
+	ModifyTime       time.Time `description:"更新时间"`
+}
+
+func (m *EtaBusiness) TableName() string {
+	return "eta_business"
+}
+
+func (m *EtaBusiness) PrimaryId() string {
+	return EtaBusinessColumns.EtaBusinessId
+}
+
+var EtaBusinessColumns = struct {
+	EtaBusinessId    string
+	BusinessName     string
+	BusinessCode     string
+	CreditCode       string
+	RegionType       string
+	Province         string
+	City             string
+	Address          string
+	SellerId         string
+	SellerName       string
+	Leader           string
+	IndustryId       string
+	IndustryName     string
+	CapitalScale     string
+	ResearchTeamSize string
+	UserMax          string
+	SigningStatus    string
+	Enable           string
+	SigningTime      string
+	ExpiredTime      string
+	CreateTime       string
+	ModifyTime       string
+}{
+	EtaBusinessId:    "eta_business_id",
+	BusinessName:     "business_name",
+	BusinessCode:     "business_code",
+	CreditCode:       "credit_code",
+	RegionType:       "region_type",
+	Province:         "province",
+	City:             "city",
+	Address:          "address",
+	SellerId:         "seller_id",
+	SellerName:       "seller_name",
+	Leader:           "leader",
+	IndustryId:       "industry_id",
+	IndustryName:     "industry_name",
+	CapitalScale:     "capital_scale",
+	ResearchTeamSize: "research_team_size",
+	UserMax:          "user_max",
+	SigningStatus:    "signing_status",
+	Enable:           "enable",
+	SigningTime:      "signing_time",
+	ExpiredTime:      "expired_time",
+	CreateTime:       "create_time",
+	ModifyTime:       "modify_time",
+}
+
+func (m *EtaBusiness) Create() (err error) {
+	o := orm.NewOrm()
+	id, err := o.Insert(m)
+	if err != nil {
+		return
+	}
+	m.EtaBusinessId = int(id)
+	return
+}
+
+func (m *EtaBusiness) CreateMulti(items []*EtaBusiness) (err error) {
+	if len(items) == 0 {
+		return
+	}
+	o := orm.NewOrm()
+	_, err = o.InsertMulti(len(items), items)
+	return
+}
+
+func (m *EtaBusiness) Update(cols []string) (err error) {
+	o := orm.NewOrm()
+	_, err = o.Update(m, cols...)
+	return
+}
+
+func (m *EtaBusiness) UpdateMulti(items []*EtaBusiness, cols []string) (err error) {
+	if len(items) == 0 {
+		return
+	}
+	o := orm.NewOrm()
+	for _, v := range items {
+		_, err = o.Update(v, cols...)
+		if err != nil {
+			return
+		}
+	}
+	return
+}
+
+func (m *EtaBusiness) Del() (err error) {
+	o := orm.NewOrm()
+	sql := fmt.Sprintf(`DELETE FROM %s WHERE %s = ? LIMIT 1`, m.TableName(), m.PrimaryId())
+	_, err = o.Raw(sql, m.EtaBusinessId).Exec()
+	return
+}
+
+func (m *EtaBusiness) GetItemById(id int) (item *EtaBusiness, err error) {
+	o := orm.NewOrm()
+	sql := fmt.Sprintf(`SELECT * FROM %s WHERE %s = ? LIMIT 1`, m.TableName(), m.PrimaryId())
+	err = o.Raw(sql, id).QueryRow(&item)
+	return
+}
+
+func (m *EtaBusiness) GetItemByCondition(condition string, pars []interface{}) (item *EtaBusiness, err error) {
+	o := orm.NewOrm()
+	sql := fmt.Sprintf(`SELECT * FROM %s WHERE 1=1 %s LIMIT 1`, m.TableName(), condition)
+	err = o.Raw(sql, pars).QueryRow(&item)
+	return
+}
+
+func (m *EtaBusiness) GetCountByCondition(condition string, pars []interface{}) (count int, err error) {
+	o := orm.NewOrm()
+	sql := fmt.Sprintf(`SELECT COUNT(1) FROM %s WHERE 1=1 %s`, m.TableName(), condition)
+	err = o.Raw(sql, pars).QueryRow(&count)
+	return
+}
+
+func (m *EtaBusiness) GetItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string) (items []*EtaBusiness, err error) {
+	o := orm.NewOrm()
+	fields := strings.Join(fieldArr, ",")
+	if len(fieldArr) == 0 {
+		fields = `*`
+	}
+	order := `ORDER BY create_time DESC`
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT %s FROM %s WHERE 1=1 %s %s`, fields, m.TableName(), condition, order)
+	_, err = o.Raw(sql, pars).QueryRows(&items)
+	return
+}
+
+func (m *EtaBusiness) GetPageItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string, startSize, pageSize int) (items []*EtaBusiness, err error) {
+	o := orm.NewOrm()
+	fields := strings.Join(fieldArr, ",")
+	if len(fieldArr) == 0 {
+		fields = `*`
+	}
+	order := `ORDER BY create_time DESC`
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT %s FROM %s WHERE 1=1 %s %s LIMIT ?,?`, fields, m.TableName(), condition, order)
+	_, err = o.Raw(sql, pars, startSize, pageSize).QueryRows(&items)
+	return
+}
+
+// CreateEtaBusinessAndContract 新增商家和签约
+func CreateEtaBusinessAndContract(businessItem *EtaBusiness, contractItem *EtaBusinessContract) (err error) {
+	if businessItem == nil || contractItem == nil {
+		err = fmt.Errorf("item empty")
+		return
+	}
+
+	o := orm.NewOrm()
+	tx, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			_ = tx.Rollback()
+			return
+		}
+		_ = tx.Commit()
+	}()
+
+	// 商家
+	businessId, e := tx.Insert(businessItem)
+	if e != nil {
+		err = fmt.Errorf("business insert err: %s", e.Error())
+		return
+	}
+	businessItem.EtaBusinessId = int(businessId)
+
+	// 签约
+	contractItem.EtaBusinessId = businessItem.EtaBusinessId
+	_, e = tx.Insert(contractItem)
+	if e != nil {
+		err = fmt.Errorf("contract insert err: %s", e.Error())
+	}
+	return
+}
+
+// EtaBusinessAddReq 新增商家请求体
+type EtaBusinessAddReq struct {
+	BusinessName     string `description:"商家名称"`
+	CreditCode       string `description:"社会统一信用码"`
+	RegionType       string `description:"所属区域:国内;海外"`
+	Province         string `description:"省份"`
+	City             string `description:"城市"`
+	SellerId         int    `description:"销售ID"`
+	SellerName       string `description:"销售名称"`
+	Leader           string `description:"决策人"`
+	IndustryId       int    `description:"行业ID"`
+	IndustryName     string `description:"行业名称"`
+	CapitalScale     string `description:"资金规模"`
+	ResearchTeamSize string `description:"研究团队规模"`
+	UserMax          int    `description:"用户上限"`
+	SigningTime      string `description:"签约时间"`
+	ExpiredTime      string `description:"到期时间"`
+	IsCheck          bool   `description:"是否只做校验而不实际新增(业务操作上基础信息和签约时间分成两个步骤了)"`
+}
+
+// EtaBusinessEditReq 编辑商家请求体
+type EtaBusinessEditReq struct {
+	EtaBusinessId    int    `description:"商家ID"`
+	Province         string `description:"省份"`
+	City             string `description:"城市"`
+	Leader           string `description:"决策人"`
+	IndustryId       int    `description:"行业ID"`
+	IndustryName     string `description:"行业名称"`
+	CapitalScale     string `description:"资金规模"`
+	ResearchTeamSize string `description:"研究团队规模"`
+	UserMax          int    `description:"用户上限"`
+}
+
+// EtaBusinessSigningReq 商家签约请求体
+type EtaBusinessSigningReq struct {
+	EtaBusinessId int    `description:"商家ID"`
+	SigningTime   string `description:"当前合约的签约时间"`
+	ExpiredTime   string `description:"当前合约的到期时间"`
+}
+
+// EtaBusinessEnableReq 禁启用商家请求体
+type EtaBusinessEnableReq struct {
+	EtaBusinessId int `description:"商家ID"`
+}
+
+// EtaBusinessMoveSellerReq 移动商家销售请求体
+type EtaBusinessMoveSellerReq struct {
+	EtaBusinessId int    `description:"商家ID"`
+	SellerId      int    `description:"销售ID"`
+	SellerName    string `description:"销售名称"`
+}
+
+// CreateEtaBusinessCode 生成ETA商家编码
+func CreateEtaBusinessCode() (code string, err error) {
+	var num int
+	o := orm.NewOrm()
+	sql := `SELECT COUNT(1) AS num FROM eta_business WHERE create_time >= ? `
+	err = o.Raw(sql, time.Now().Format(utils.FormatDate)).QueryRow(&num)
+	if err != nil {
+		return
+	}
+	code = "E" + time.Now().Format("20060102") + fmt.Sprintf("%02d", num)
+	return
+}
+
+// EtaBusinessListResp 商家分页列表响应体
+type EtaBusinessListResp struct {
+	List   []*EtaBusinessItem `description:"商家列表数据"`
+	Paging *paging.PagingItem `description:"分页数据"`
+}
+
+// EtaBusinessItem ETA商家信息
+type EtaBusinessItem struct {
+	EtaBusinessId    int
+	BusinessName     string `description:"商家名称"`
+	BusinessCode     string `description:"商家编码"`
+	CreditCode       string `description:"社会统一信用码"`
+	RegionType       string `description:"所属区域:国内;海外"`
+	Address          string `description:"商家地址"`
+	SellerId         int    `description:"销售ID"`
+	SellerName       string `description:"销售名称"`
+	Leader           string `description:"决策人"`
+	IndustryId       int    `description:"行业ID"`
+	IndustryName     string `description:"行业名称"`
+	CapitalScale     string `description:"资金规模"`
+	ResearchTeamSize string `description:"研究团队规模"`
+	UserMax          int    `description:"用户上限"`
+	SigningStatus    int    `description:"签约状态:1-首次签约;2-续约中;3-已终止"`
+	Enable           int    `description:"状态:0-禁用;1-启用"`
+	SigningTime      string `description:"当前合约的签约时间"`
+	ExpiredTime      string `description:"当前合约的到期时间"`
+	CreateTime       string `description:"创建时间"`
+	ModifyTime       string `description:"更新时间"`
+}

+ 121 - 0
models/eta_business/eta_business_contract.go

@@ -0,0 +1,121 @@
+package eta_business
+
+import (
+	"fmt"
+	"github.com/beego/beego/v2/client/orm"
+	"strings"
+	"time"
+)
+
+// EtaBusinessContract ETA商家合同表
+type EtaBusinessContract struct {
+	EtaBusinessContractId int       `orm:"column(eta_business_contract_id);pk"`
+	EtaBusinessId         int       `description:"ETA商家ID"`
+	SigningTime           time.Time `description:"签约时间"`
+	ExpiredTime           time.Time `description:"到期时间"`
+	IsFirst               int       `description:"是否为首份签约"`
+	CreateTime            time.Time `description:"创建时间"`
+	ModifyTime            time.Time `description:"更新时间"`
+}
+
+func (m *EtaBusinessContract) TableName() string {
+	return "eta_business_contract"
+}
+
+func (m *EtaBusinessContract) PrimaryId() string {
+	return EtaBusinessContractColumns.EtaBusinessContractId
+}
+
+var EtaBusinessContractColumns = struct {
+	EtaBusinessContractId string
+	EtaBusinessId         string
+	SigningTime           string
+	ExpiredTime           string
+	CreateTime            string
+	ModifyTime            string
+}{
+	EtaBusinessContractId: "eta_business_contract_id",
+	EtaBusinessId:         "eta_business_id",
+	SigningTime:           "signing_time",
+	ExpiredTime:           "expired_time",
+	CreateTime:            "create_time",
+	ModifyTime:            "modify_time",
+}
+
+func (m *EtaBusinessContract) Create() (err error) {
+	o := orm.NewOrm()
+	id, err := o.Insert(m)
+	if err != nil {
+		return
+	}
+	m.EtaBusinessContractId = int(id)
+	return
+}
+
+func (m *EtaBusinessContract) CreateMulti(items []*EtaBusinessContract) (err error) {
+	if len(items) == 0 {
+		return
+	}
+	o := orm.NewOrm()
+	_, err = o.InsertMulti(len(items), items)
+	return
+}
+
+func (m *EtaBusinessContract) Update(cols []string) (err error) {
+	o := orm.NewOrm()
+	_, err = o.Update(m, cols...)
+	return
+}
+
+func (m *EtaBusinessContract) Del() (err error) {
+	o := orm.NewOrm()
+	sql := fmt.Sprintf(`DELETE FROM %s WHERE %s = ? LIMIT 1`, m.TableName(), m.PrimaryId())
+	_, err = o.Raw(sql, m.EtaBusinessContractId).Exec()
+	return
+}
+
+func (m *EtaBusinessContract) GetItemById(id int) (item *EtaBusinessContract, err error) {
+	o := orm.NewOrm()
+	sql := fmt.Sprintf(`SELECT * FROM %s WHERE %s = ? LIMIT 1`, m.TableName(), m.PrimaryId())
+	err = o.Raw(sql, id).QueryRow(&item)
+	return
+}
+
+func (m *EtaBusinessContract) GetItemByCondition(condition string, pars []interface{}) (err error) {
+	o := orm.NewOrm()
+	sql := fmt.Sprintf(`SELECT * FROM %s WHERE 1=1 %s LIMIT 1`, m.TableName(), condition)
+	err = o.Raw(sql, pars).QueryRow(&m)
+	return
+}
+
+func (m *EtaBusinessContract) GetCountByCondition(condition string, pars []interface{}) (count int, err error) {
+	o := orm.NewOrm()
+	sql := fmt.Sprintf(`SELECT COUNT(1) FROM %s WHERE 1=1 %s`, m.TableName(), condition)
+	err = o.Raw(sql, pars).QueryRow(&count)
+	return
+}
+
+func (m *EtaBusinessContract) GetItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string) (items []*EtaBusinessContract, err error) {
+	o := orm.NewOrm()
+	fields := strings.Join(fieldArr, ",")
+	if len(fieldArr) == 0 {
+		fields = `*`
+	}
+	order := `ORDER BY create_time DESC`
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT %s FROM %s WHERE 1=1 %s %s`, fields, m.TableName(), condition, order)
+	_, err = o.Raw(sql, pars).QueryRows(&items)
+	return
+}
+
+// EtaBusinessContractItem 商家签约信息
+type EtaBusinessContractItem struct {
+	EtaBusinessContractId int
+	EtaBusinessId         int    `description:"ETA商家ID"`
+	SigningTime           string `description:"签约时间"`
+	ExpiredTime           string `description:"到期时间"`
+	ExpireDay             string `description:"到期天数"`
+	Using                 bool   `description:"是否当前合约"`
+}

+ 38 - 0
models/eta_business/eta_business_remind_record.go

@@ -0,0 +1,38 @@
+package eta_business
+
+import (
+	"github.com/beego/beego/v2/client/orm"
+	"time"
+)
+
+// EtaBusinessRemindRecord 客户提醒记录
+type EtaBusinessRemindRecord struct {
+	CompanyRemindRecordId int    `orm:"column(eta_business_remind_record_id);pk"`
+	Type                  int    `description:"过期类型:1-1天;2-7天;3-15天;4-30天;5-60天"`
+	SellerId              int    `description:"销售id"`
+	SellerName            string `description:"销售名称"`
+	EtaBusinessId         int    `description:"ETA商家ID"`
+	BusinessName          string `description:"商家名称"`
+	EndDate               string `description:"到期日期"`
+	UniqueCode            string `description:"唯一code"`
+	CreateTime            time.Time
+}
+
+func (m *EtaBusinessRemindRecord) Create() (err error) {
+	o := orm.NewOrm()
+	id, err := o.Insert(m)
+	if err != nil {
+		return
+	}
+	m.CompanyRemindRecordId = int(id)
+	return
+}
+
+func (m *EtaBusinessRemindRecord) CreateMulti(items []*EtaBusinessRemindRecord) (err error) {
+	if len(items) == 0 {
+		return
+	}
+	o := orm.NewOrm()
+	_, err = o.InsertMulti(len(items), items)
+	return
+}

+ 76 - 0
services/company_product.go

@@ -267,6 +267,8 @@ func CompanyRemind(cont context.Context) (err error) {
 	sellers, err := models.GetSellersOpenId()
 	for k, v := range sellers {
 		fmt.Println(k, v.AdminId, v.Mobile)
+		CompanyRemind60Day(v)
+		time.Sleep(5 * time.Second)
 		CompanyRemind30Day(v)
 		time.Sleep(5 * time.Second)
 		CompanyRemind15Day(v)
@@ -279,6 +281,80 @@ func CompanyRemind(cont context.Context) (err error) {
 	return
 }
 
+// CompanyRemind60Day 60天后到期客户
+func CompanyRemind60Day(seller *models.Sellers) {
+	var err error
+	defer func() {
+		if err != nil {
+			go utils.SendEmail(utils.APPNAME+"失败提醒"+utils.RunMode, "到期提醒失败:CompanyRemind;Err"+err.Error(), utils.EmailSendToUsers)
+		}
+	}()
+
+	companyRemindRecordList := make([]*models.CompanyRemindRecord, 0)
+	remindType := 5
+	uniqueCode := fmt.Sprint(seller.AdminId, time.Now().Format(utils.FormatDateUnSpace), remindType, utils.GetRandString(5))
+
+	//remindEndDate := "2020-12-31" //time.Now().AddDate(0, 0, 30).Format(utils.FormatDate)
+	remindEndDate := time.Now().AddDate(0, 0, 60).Format(utils.FormatDate)
+	companyItems, err := models.GetRemindCompany(seller.AdminId, remindEndDate)
+	if err != nil {
+		return
+	}
+	emailContents := "<div><p>您有【" + strconv.Itoa(len(companyItems)) + "】 客户将于60天后到期,请注意查看</p>"
+	emailContents += "<table border='1'><tr><td width='200'>60天后到期客户名称</td><td width='200'>到期日期</td><td width='200'>销售人员</td><td>客户类型</td></tr>"
+	var isSend bool
+	msgContent := ``
+	for _, v := range companyItems {
+		endTime := v.EndDate
+		if v.Status == "正式" {
+			endTime = v.ContractEndDate
+		}
+		emailContents += `<tr><td>` + v.CompanyName + `</td><td>` + endTime + `</td><td>` + seller.RealName + `</td><td>` + v.Status + `</td></tr>`
+		msgContent += `客户:` + v.CompanyName + ";状态:" + v.Status + "\n"
+		isSend = true
+
+		// 数据入库
+		companyRemindRecordList = append(companyRemindRecordList, &models.CompanyRemindRecord{
+			//CompanyRemindRecordId: 0,
+			Type:        remindType,
+			SellerId:    seller.AdminId,
+			SellerName:  seller.RealName,
+			CompanyId:   v.CompanyId,
+			CompanyName: v.CompanyName,
+			Status:      v.Status,
+			EndDate:     endTime,
+			UniqueCode:  uniqueCode,
+			CreateTime:  time.Now(),
+		})
+	}
+	emailContents += "</table></br>"
+
+	if isSend {
+		if seller.Email != "" {
+			utils.SendEmailByHongze("到期前60天提醒", emailContents, seller.Email, "", "")
+		}
+
+		if seller.OpenId != "" {
+			first := "您有【" + strconv.Itoa(len(companyItems)) + "】 客户将于60天后到期,请注意查看"
+			//keyword1 := "到期前30天提醒"
+			keyword1 := fmt.Sprintf(`【%d】客户到期前60天提醒,点击查看`, len(companyItems))
+			keyword2 := remindEndDate
+			remark := msgContent
+
+			openIdList := make([]*models.OpenIdList, 0)
+			openIdItem := new(models.OpenIdList)
+			openIdItem.OpenId = seller.OpenId
+			openIdList = append(openIdList, openIdItem)
+			SendWxMsgWithCompanyRemind(first, keyword1, keyword2, remark, uniqueCode, openIdList)
+		}
+	}
+
+	// 数据入库
+	if len(companyRemindRecordList) > 0 {
+		models.AddMultiCompanyRemindRecord(companyRemindRecordList)
+	}
+}
+
 // CompanyRemind30Day 30天后到期客户
 func CompanyRemind30Day(seller *models.Sellers) {
 	var err error

+ 255 - 0
services/eta_business.go

@@ -0,0 +1,255 @@
+package services
+
+import (
+	"context"
+	"fmt"
+	"hongze/hongze_task/models"
+	"hongze/hongze_task/models/eta_business"
+	"hongze/hongze_task/services/alarm_msg"
+	"hongze/hongze_task/utils"
+	"time"
+)
+
+// EtaBusinessExpiredRemind ETA商家到期提醒
+func EtaBusinessExpiredRemind(cont context.Context) (err error) {
+	// 频次: 1-1天; 2-7天; 3-15天; 4-30天; 5-60天
+	frequencyArr := []int{1, 2, 3, 4, 5}
+	for _, f := range frequencyArr {
+		_ = HandleEtaBusinessExpiredRemind(f)
+		time.Sleep(15 * time.Second)
+	}
+	return
+}
+
+// HandleEtaBusinessExpiredRemind ETA商家到期提醒
+func HandleEtaBusinessExpiredRemind(frequency int) (err error) {
+	defer func() {
+		if err != nil {
+			tips := fmt.Sprintf("ETA商家到期提醒失败, frequency: %d, Err: %s", frequency, err.Error())
+			utils.FileLog.Info("%s", tips)
+			go alarm_msg.SendAlarmMsg(tips, 3)
+		}
+	}()
+
+	// 频次对应天数
+	dayMap := map[int]int{1: 1, 2: 7, 3: 15, 4: 30, 5: 60}
+	days := dayMap[frequency]
+	if days <= 0 {
+		err = fmt.Errorf("提醒频次有误")
+		return
+	}
+
+	// 获取当前合约N后过期的ETA商家
+	monthLater := time.Now().Local().AddDate(0, 0, days)
+	expiredTime := monthLater.Format(utils.FormatDate)
+	businesses := make([]*eta_business.EtaBusiness, 0)
+	{
+		businessOb := new(eta_business.EtaBusiness)
+		cond := fmt.Sprintf(` AND %s = ?`, eta_business.EtaBusinessColumns.ExpiredTime)
+		pars := make([]interface{}, 0)
+		pars = append(pars, expiredTime)
+		list, e := businessOb.GetItemsByCondition(cond, pars, []string{}, "")
+		if e != nil {
+			err = fmt.Errorf("获取商家列表失败, Err: " + e.Error())
+			return
+		}
+		businesses = list
+	}
+	if len(businesses) == 0 {
+		return
+	}
+
+	// 获取销售
+	sellers, e := models.GetSellersOpenId()
+	if e != nil {
+		err = fmt.Errorf("获取销售信息失败, Err: " + e.Error())
+		return
+	}
+
+	// 以销售为单位
+	sellerBusinessMap := make(map[int][]*eta_business.EtaBusiness)
+	for _, v := range businesses {
+		if sellerBusinessMap[v.SellerId] == nil {
+			sellerBusinessMap[v.SellerId] = make([]*eta_business.EtaBusiness, 0)
+		}
+		sellerBusinessMap[v.SellerId] = append(sellerBusinessMap[v.SellerId], v)
+	}
+	sellerMap := make(map[int]*models.Sellers)
+	for _, s := range sellers {
+		sellerMap[s.AdminId] = s
+	}
+
+	// 推送邮件和公众号
+	remindRecords := make([]*eta_business.EtaBusinessRemindRecord, 0)
+	for k, v := range sellerBusinessMap {
+		seller := sellerMap[k]
+		if seller == nil {
+			continue
+		}
+		if seller.Email == "" && seller.OpenId == "" {
+			continue
+		}
+		if len(v) == 0 {
+			continue
+		}
+		uniqueCode := fmt.Sprint(seller.AdminId, time.Now().Format(utils.FormatDateUnSpace), frequency, utils.GetRandDigit(5))
+
+		contentRemark := ""
+		contentsEmail := fmt.Sprintf(`<div><p>您有【%d】ETA客户将于%d天后到期,请注意查看</p>`, len(v), days)
+		contentsEmail += fmt.Sprintf(`<table border='1'><tr><td width='200'>%d天后到期客户名称</td><td width='200'>到期日期</td><td width='200'>销售人员</td><td>客户类型</td></tr>`, days)
+		for _, bz := range v {
+			row := fmt.Sprintf(`<tr><td>%s</td><td>%s</td><td>%s</td><td>%s</td></tr>`, bz.BusinessName, bz.ExpiredTime.Format(utils.FormatDate), bz.SellerName, "ETA")
+			contentsEmail += row
+			contentRemark += fmt.Sprintf("【%s】", bz.BusinessName)
+
+			// 数据入库
+			remindRecords = append(remindRecords, &eta_business.EtaBusinessRemindRecord{
+				Type:          frequency,
+				SellerId:      seller.AdminId,
+				SellerName:    seller.RealName,
+				EtaBusinessId: bz.EtaBusinessId,
+				BusinessName:  bz.BusinessName,
+				EndDate:       bz.ExpiredTime.Format(utils.FormatDate),
+				UniqueCode:    uniqueCode,
+				CreateTime:    time.Now(),
+			})
+		}
+		contentsEmail += "</table></div>"
+
+		// 邮件
+		if seller.Email != "" {
+			msg := fmt.Sprintf("到期前%d天提醒", days)
+			utils.SendEmailByHongze(msg, contentsEmail, seller.Email, "", "")
+		}
+
+		// 公众号, first和remark已经无效了
+		if seller.OpenId != "" {
+			first := fmt.Sprintf(`您有【%d】 客户将于%d天后到期,请注意查看`, len(v), days)
+			keyword1 := fmt.Sprintf(`【%d】ETA到期前%d天提醒,点击查看`, len(v), days)
+			keyword2 := expiredTime
+			remark := contentRemark
+
+			openIdList := make([]*models.OpenIdList, 0)
+			openIdItem := new(models.OpenIdList)
+			openIdItem.OpenId = seller.OpenId
+			openIdList = append(openIdList, openIdItem)
+			_ = SendWxMsgWithEtaBusinessRemind(first, keyword1, keyword2, remark, uniqueCode, openIdList)
+		}
+
+		// 数据入库
+		if len(remindRecords) > 0 {
+			recordOb := new(eta_business.EtaBusinessRemindRecord)
+			if e = recordOb.CreateMulti(remindRecords); e != nil {
+				err = fmt.Errorf("批量新增ETA商家提醒记录失败, Err: %s", e.Error())
+				return
+			}
+		}
+	}
+	return
+}
+
+// EtaBusinessUpdateStatus 每日更新ETA商家签约状态
+func EtaBusinessUpdateStatus(cont context.Context) (err error) {
+	defer func() {
+		if err != nil {
+			tips := "ETA商家签约状态更新: EtaBusinessUpdateStatus, Err: " + err.Error()
+			utils.FileLog.Info("%s", tips)
+			go alarm_msg.SendAlarmMsg(tips, 3)
+		}
+	}()
+
+	// 获取所有商家
+	businesses := make([]*eta_business.EtaBusiness, 0)
+	{
+		ob := new(eta_business.EtaBusiness)
+		cond := ``
+		pars := make([]interface{}, 0)
+		list, e := ob.GetItemsByCondition(cond, pars, []string{}, "")
+		if e != nil {
+			err = fmt.Errorf("获取商家列表失败, Err: " + e.Error())
+			return
+		}
+		businesses = list
+	}
+	if len(businesses) == 0 {
+		return
+	}
+
+	// 获取所有商家签约
+	contracts := make([]*eta_business.EtaBusinessContract, 0)
+	{
+		ob := new(eta_business.EtaBusinessContract)
+		cond := ``
+		pars := make([]interface{}, 0)
+		list, e := ob.GetItemsByCondition(cond, pars, []string{}, "")
+		if e != nil {
+			err = fmt.Errorf("获取商家合同列表失败, Err: " + e.Error())
+			return
+		}
+		contracts = list
+	}
+	if len(contracts) == 0 {
+		return
+	}
+	businessContracts := make(map[int][]*eta_business.EtaBusinessContract) // 商家对应的所有签约
+	for _, c := range contracts {
+		if businessContracts[c.EtaBusinessId] == nil {
+			businessContracts[c.EtaBusinessId] = make([]*eta_business.EtaBusinessContract, 0)
+		}
+		businessContracts[c.EtaBusinessId] = append(businessContracts[c.EtaBusinessId], c)
+	}
+
+	// 遍历判断商家当前合约的时间, 更新签约状态
+	updateBusiness := make([]*eta_business.EtaBusiness, 0)
+	strToday := time.Now().Format(utils.FormatDate)
+	today, _ := time.ParseInLocation(utils.FormatDate, strToday, time.Local)
+	for _, b := range businesses {
+		cs := businessContracts[b.EtaBusinessId]
+		// 无签约-正常来讲不会出现这种情况
+		if cs == nil || (cs != nil && len(cs) == 0) {
+			if b.SigningStatus != eta_business.EtaBusinessSigningStatusTerminate {
+				b.SigningStatus = eta_business.EtaBusinessSigningStatusTerminate
+				b.ModifyTime = time.Now().Local()
+				updateBusiness = append(updateBusiness, b)
+			}
+			continue
+		}
+
+		// 有签约
+		using := false // 是否在任一存续期内
+		for _, c := range cs {
+			// 当前合约
+			if today.Equal(c.SigningTime) || today.Equal(c.ExpiredTime) || (today.After(c.SigningTime) && today.Before(c.ExpiredTime)) {
+				b.ContractId = c.EtaBusinessContractId
+				b.SigningTime = c.SigningTime
+				b.ExpiredTime = c.ExpiredTime
+				b.ModifyTime = time.Now().Local()
+				// 是否为首次签约
+				if c.IsFirst == 1 {
+					b.SigningStatus = eta_business.EtaBusinessSigningStatusFirst
+				} else {
+					b.SigningStatus = eta_business.EtaBusinessSigningStatusContinue
+				}
+				updateBusiness = append(updateBusiness, b)
+				using = true
+				break
+			}
+		}
+		// 不存在任一合同期内
+		if !using {
+			b.SigningStatus = eta_business.EtaBusinessSigningStatusTerminate
+			updateBusiness = append(updateBusiness, b)
+		}
+	}
+
+	// 更新签约状态
+	if len(updateBusiness) > 0 {
+		ob := new(eta_business.EtaBusiness)
+		cols := []string{"ContractId", "SigningStatus", "SigningTime", "ExpiredTime", "ModifyTime"}
+		if e := ob.UpdateMulti(updateBusiness, cols); e != nil {
+			err = fmt.Errorf("批量更新签约状态失败, Err: %s", e.Error())
+			return
+		}
+	}
+	return
+}

+ 17 - 5
services/task.go

@@ -161,6 +161,10 @@ func Task() {
 	englishReportEmailTermination := task.NewTask("englishReportEmailTermination", "0 30 2 * * *", EnglishReportEmailTermination)
 	task.AddTask("英文研报客户 临时->终止", englishReportEmailTermination)
 
+	// ETA商家签约状态更新
+	etaBusinessUpdateStatus := task.NewTask("etaBusinessUpdateStatus", "0 5 5 * * *", EtaBusinessUpdateStatus)
+	task.AddTask("etaBusinessUpdateStatus", etaBusinessUpdateStatus)
+
 	task.StartTask()
 
 	fmt.Println("task end")
@@ -250,16 +254,24 @@ func releaseTask() {
 	// 每日4:01更新每刻报销-客户档案
 	syncMaycurCompanyProfile := task.NewTask("syncMaycurCompanyProfile", "0 1 4 * * * ", maycur.DailyUpdateCompanyProfile)
 	task.AddTask("每日更新每刻报销-客户档案", syncMaycurCompanyProfile)
+
+	// ETA商家到期提醒
+	etaBusinessRemind := task.NewTask("etaBusinessRemind", "0 20 8 * * *", EtaBusinessExpiredRemind)
+	task.AddTask("etaBusinessRemind", etaBusinessRemind)
 }
 
 //func TaskTest() {
 //	fmt.Println("The task is start")
 //
-//	e, msg := data.InitTradePosition("shanghai", "2023-05-05", "2023-05-05")
-//	if e != nil {
-//		fmt.Println(e.Error())
-//		fmt.Println(msg)
-//	}
+//	//e, msg := data.InitTradePosition("shanghai", "2023-05-05", "2023-05-05")
+//	//if e != nil {
+//	//	fmt.Println(e.Error())
+//	//	fmt.Println(msg)
+//	//}
+//
+//	//var ctx context.Context
+//	//_ = EtaBusinessExpiredRemind(ctx)
+//	//_ = EtaBusinessUpdateStatus(ctx)
 //
 //	//task.StartTask()
 //	fmt.Println("The task is end")

+ 41 - 0
services/wx_template_msg.go

@@ -292,3 +292,44 @@ func SendYbVoiceBroadcastWxMsg(broadcastId int, sectionName, broadcastName strin
 	err = SendTemplateMsgV2(sendInfo)
 	return
 }
+
+// SendWxMsgWithEtaBusinessRemind ETA商家-到期提醒
+func SendWxMsgWithEtaBusinessRemind(first, keyword1, keyword2, remark, code string, openIdList []*models.OpenIdList) (err error) {
+	defer func() {
+		if err != nil {
+			tips := "ETA商家-推送到期提醒模板消息失败, Err: " + err.Error()
+			utils.FileLog.Info("%s", tips)
+			go alarm_msg.SendAlarmMsg(tips, 3)
+			//go utils.SendEmail("发送模版消息失败"+"【"+utils.APPNAME+"】"+time.Now().Format("2006-01-02 15:04:05"), msg+";Err:"+err.Error(), utils.EmailSendToUsers)
+		}
+	}()
+	utils.FileLog.Info("%s", "services SendMsg")
+
+	var wxAppPath string
+	if utils.RunMode == `debug` {
+		wxAppPath = `http://msgsendtest.hzinsights.com/ETAExpirationHint.html?code=` + code
+	} else {
+		wxAppPath = `https://msgsend.hzinsights.com/ETAExpirationHint.html?code=` + code
+	}
+	openIdArr := make([]string, len(openIdList))
+	for i, v := range openIdList {
+		openIdArr[i] = v.OpenId
+	}
+	if len(openIdArr) == 0 {
+		return
+	}
+
+	sendInfo := new(SendWxTemplate)
+	sendInfo.WxAppId = utils.AdminWxAppId
+	sendInfo.First = first
+	sendInfo.Productname = keyword1
+	sendInfo.Date = keyword2
+	sendInfo.RedirectUrl = wxAppPath
+	sendInfo.RedirectTarget = 0
+	sendInfo.TemplateId = utils.RemindTemplateId
+	sendInfo.Resource = wxAppPath
+	sendInfo.OpenIdArr = openIdArr
+	sendInfo.Remark = remark
+	err = SendTemplateMsgV2(sendInfo)
+	return
+}