Create & Init Project...

This commit is contained in:
2019-04-22 18:49:16 +08:00
commit fc4fa37393
25440 changed files with 4054998 additions and 0 deletions

View File

@@ -0,0 +1,75 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_test",
"go_library",
)
go_test(
name = "go_default_test",
srcs = [
"archive_test.go",
"service_test.go",
],
embed = [":go_default_library"],
tags = ["automanaged"],
deps = [
"//app/job/main/up/conf:go_default_library",
"//app/job/main/up/dao/upcrm:go_default_library",
"//app/job/main/up/model/archivemodel:go_default_library",
"//app/job/main/up/model/signmodel:go_default_library",
],
)
go_library(
name = "go_default_library",
srcs = [
"archive.go",
"service.go",
"sign_due_job.go",
"sign_state_job.go",
"sign_task_job.go",
"sign_update_tid_job.go",
],
importpath = "go-common/app/job/main/up/service",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/admin/main/up/util:go_default_library",
"//app/admin/main/up/util/databusutil:go_default_library",
"//app/admin/main/up/util/mathutil:go_default_library",
"//app/interface/main/mcn/tool/worker:go_default_library",
"//app/job/main/up/conf:go_default_library",
"//app/job/main/up/dao/account:go_default_library",
"//app/job/main/up/dao/email:go_default_library",
"//app/job/main/up/dao/upcrm:go_default_library",
"//app/job/main/up/model:go_default_library",
"//app/job/main/up/model/archivemodel:go_default_library",
"//app/job/main/up/model/signmodel:go_default_library",
"//app/job/main/up/model/upcrmmodel:go_default_library",
"//app/service/main/account/model:go_default_library",
"//app/service/main/archive/api:go_default_library",
"//app/service/main/up/api/v1:go_default_library",
"//app/service/main/upcredit/mathutil:go_default_library",
"//library/log:go_default_library",
"//library/queue/databus:go_default_library",
"//library/time:go_default_library",
"//vendor/github.com/jinzhu/gorm:go_default_library",
"//vendor/github.com/robfig/cron:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,269 @@
package service
import (
"context"
"encoding/json"
"math"
"time"
"go-common/app/admin/main/up/util/mathutil"
"go-common/app/job/main/up/model"
"go-common/app/job/main/up/model/upcrmmodel"
upGRPCv1 "go-common/app/service/main/up/api/v1"
"go-common/library/log"
"go-common/library/queue/databus"
"go-common/app/job/main/up/model/archivemodel"
)
// action
const (
ActionUpdate = "update"
ActionInsert = "insert"
)
// table name
const (
TableArchiveStaff = "archive_staff"
)
//ArchiveUpInfo .
type ArchiveUpInfo struct {
Table string `json:"table"`
Action string `json:"action"`
New *archivemodel.ArchiveCanal `json:"new"`
Old *archivemodel.ArchiveCanal `json:"old"`
}
// CanalMsg canal message struct
type CanalMsg struct {
Action string `json:"action"`
Table string `json:"table"`
New json.RawMessage `json:"new"`
Old json.RawMessage `json:"old"`
}
// handle notify t message
func (s *Service) handleArchiveNotifyT(msg *databus.Message) (err error) {
m := &ArchiveUpInfo{}
if err = json.Unmarshal(msg.Value, m); err != nil {
log.Error("json.Unmarshal(%v) error(%v)", msg.Value, err)
return
}
switch m.Table {
case "archive":
err = s.checkArchiveNotify(m)
}
if err != nil {
log.Error("handle msg fail, err=%s", err)
}
return
}
func (s *Service) checkArchiveNotify(msg *ArchiveUpInfo) (err error) {
var arch *archivemodel.ArchiveCanal
switch {
default:
if msg.Action == ActionInsert {
if msg.New != nil && msg.New.State >= 0 {
arch = msg.New
}
break
}
if msg.Action == ActionUpdate {
if archiveStateChange(msg.New, msg.Old) {
arch = msg.New
}
break
}
}
if arch == nil {
log.Warn("no need to update up cache, msg value=%v", msg)
return
}
s.worker.Add(func() {
s.upRPC.UpCount(context.Background(), &upGRPCv1.UpCountReq{
Mid: arch.Mid,
})
var upCacheReq = &upGRPCv1.UpCacheReq{
Mid: arch.Mid,
Aid: arch.AID,
}
if arch.State >= 0 {
s.upRPC.AddUpPassedCache(context.Background(), upCacheReq)
log.Info("rpc add up cache, mid=%d, aid=%d", upCacheReq.Mid, upCacheReq.Aid)
} else {
s.upRPC.DelUpPassedCache(context.Background(), upCacheReq)
log.Info("rpc delete up cache, mid=%d, aid=%d", upCacheReq.Mid, upCacheReq.Aid)
}
})
return
}
func (s *Service) handleArchiveT(msg *databus.Message) (err error) {
var m = &CanalMsg{}
if err = json.Unmarshal(msg.Value, m); err != nil {
log.Error("json.Unmarshal(%v) error(%v)", msg.Value, err)
return
}
switch m.Table {
case TableArchiveStaff:
var new, old archivemodel.ArchiveStaff
switch m.Action {
case ActionInsert:
if err = json.Unmarshal(m.New, &new); err != nil {
log.Error("m.New -> json.Unmarshal(%v) error(%v)", m.New, err)
return
}
case ActionUpdate:
if err = json.Unmarshal(m.New, &new); err != nil {
log.Error("m.New -> json.Unmarshal(%v) error(%v)", m.New, err)
return
}
if err = json.Unmarshal(m.Old, &old); err != nil {
log.Error("m.Old -> json.Unmarshal(%v) error(%v)", m.New, err)
return
}
if new.State == old.State {
log.Warn("new staff state(%d) eq old staff state(%d)", new.State, old.State)
return
}
}
// state是正常说明是新增否则是删除
needInsert := new.State == archivemodel.StaffStateNormal
var req = &upGRPCv1.UpCacheReq{Mid: new.StaffMid, Aid: new.Aid}
if needInsert {
_, err = s.upRPC.AddUpPassedCacheByStaff(context.Background(), req)
if err != nil {
log.Error("rpc call add up staff, new=%v, err=%v", new, err)
} else {
log.Info("rpc call add up staff, new=%v", new)
}
} else {
_, err = s.upRPC.DelUpPassedCacheByStaff(context.Background(), req)
if err != nil {
log.Error("rpc call del up staff, new=%v, err=%v", new, err)
} else {
log.Info("rpc call del up staff, new=%v", new)
}
}
}
return
}
func archiveStateChange(a, b *archivemodel.ArchiveCanal) bool {
if a == b {
return false
} else if a == nil || b == nil {
return true
}
if a.State == b.State {
return false
}
var min, max int
if a.State > b.State {
min, max = b.State, a.State
} else {
min, max = a.State, b.State
}
if min < 0 && max >= 0 {
return true
}
return false
}
//WarmUp warm up
func (s *Service) WarmUp(c context.Context, req *model.WarmUpReq) (res *model.WarmUpReply, err error) {
var (
d = s.crmdb.GetDb()
lastID = req.LastID
limit, thisCount = 100, 100
)
var count = 0
if req.Size == -1 {
req.Size = math.MaxInt32
}
for ; count < req.Size && thisCount == limit; count += thisCount {
time.Sleep(time.Millisecond * 1000)
var end = count + limit
if end > req.Size {
limit = req.Size - count
}
var upList []*upcrmmodel.UpBaseInfo
err = d.Select("mid, id").Where("id>?", lastID).Limit(limit).Find(&upList).Error
if err != nil {
log.Error("fail to query db, err=%v", err)
return
}
thisCount = len(upList)
var mids []int64
for _, v := range upList {
lastID = mathutil.Max(lastID, int(v.ID))
mids = append(mids, v.Mid)
}
var _, e = s.upRPC.UpsArcs(context.Background(), &upGRPCv1.UpsArcsReq{
Mids: mids,
Pn: 1,
Ps: 1,
})
if e != nil {
log.Warn("up rpc UpsArcs return err=%v", e)
}
_, e = s.upRPC.UpsCount(context.Background(), &upGRPCv1.UpsCountReq{
Mids: mids,
})
if e != nil {
log.Warn("up rpc UpsCount return err=%v", e)
}
log.Info("warm ups, handled last id=%d, count=%d", lastID, count)
}
log.Info("warm ups, begin id=%d, expect up size=%d, end id=%d, real count=%d", req.LastID, req.Size, lastID, count)
res = &model.WarmUpReply{
LastID: lastID,
}
return
}
//WarmUpMid warm up by mid
func (s *Service) WarmUpMid(c context.Context, req *model.WarmUpReq) (res *model.WarmUpReply, err error) {
if _, err = s.upRPC.UpArcs(context.Background(), &upGRPCv1.UpArcsReq{
Mid: req.Mid,
Pn: 1,
Ps: 1,
}); err != nil {
log.Error("up rpc UpsArc(%d) return err=%v", req.Mid, err)
return
}
var (
count int64
cntReply *upGRPCv1.UpCountReply
)
if cntReply, err = s.upRPC.UpCount(context.Background(), &upGRPCv1.UpCountReq{
Mid: req.Mid,
}); err != nil {
log.Error("up rpc UpsCount(%d) return err=%v", req.Mid, err)
return
}
if cntReply != nil {
count = cntReply.Count
}
log.Info("warm up(%d) real count=%d", req.Mid, count)
return
}
//AddStaff .
func (s *Service) AddStaff(c context.Context, req *model.AddStaffReq) (res *upGRPCv1.NoReply, err error) {
return s.upRPC.AddUpPassedCacheByStaff(c, &upGRPCv1.UpCacheReq{Aid: req.Aid, Mid: req.StaffMid})
}
//DeleteStaff .
func (s *Service) DeleteStaff(c context.Context, req *model.AddStaffReq) (res *upGRPCv1.NoReply, err error) {
return s.upRPC.DelUpPassedCacheByStaff(c, &upGRPCv1.UpCacheReq{Aid: req.Aid, Mid: req.StaffMid})
}

View File

@@ -0,0 +1,33 @@
package service
import (
"testing"
"go-common/app/job/main/up/model/archivemodel"
)
func Test_archiveStateChange(t *testing.T) {
var (
testcase = [][]*archivemodel.ArchiveCanal{
{{State: 0}, {State: -1}},
{{State: -5}, {State: 0}},
{{State: 10}, {State: 10}},
{{State: -5}, {State: -5}},
}
testresult = []bool{
true,
true,
false,
false,
}
)
for i := range testcase {
var cas = testcase[i]
if archiveStateChange(cas[0], cas[1]) != testresult[i] {
t.Errorf("test fail, testcase[%d]=%v, expect=%t", i, cas, testresult[i])
t.Fail()
}
}
}

View File

@@ -0,0 +1,100 @@
package service
import (
"context"
"sync"
"time"
"go-common/app/interface/main/mcn/tool/worker"
"go-common/app/job/main/up/conf"
"go-common/app/job/main/up/dao/account"
"go-common/app/job/main/up/dao/email"
"go-common/app/job/main/up/dao/upcrm"
archive "go-common/app/service/main/archive/api"
upGRPCv1 "go-common/app/service/main/up/api/v1"
"go-common/library/queue/databus"
"github.com/robfig/cron"
"go-common/app/admin/main/up/util/databusutil"
)
// Service struct
type Service struct {
c *conf.Config
maildao *email.Dao
crmdb *upcrm.Dao
acc *account.Dao
arcRPC archive.ArchiveClient
cron *cron.Cron
worker *worker.Pool
wg sync.WaitGroup
archiveNotifyT *databus.Databus
archiveT *databus.Databus
closeCh chan struct{}
upRPC upGRPCv1.UpClient
databusHandler *databusutil.DatabusHandler
}
// New init
func New(c *conf.Config) (s *Service) {
s = &Service{
c: c,
cron: cron.New(),
crmdb: upcrm.New(c),
acc: account.New(c),
maildao: email.New(c),
worker: worker.New(&worker.Conf{
WorkerProcMax: 10,
QueueSize: 1024,
WorkerNumber: 4}),
archiveNotifyT: databus.New(c.DatabusConf.ArchiveNotify),
archiveT: databus.New(c.DatabusConf.Archive),
closeCh: make(chan struct{}),
databusHandler: databusutil.NewDatabusHandler(),
}
var err error
s.arcRPC, err = archive.NewClient(c.GRPCClient.Archive)
if err != nil {
panic(err)
}
if err = s.initEmailTemplate(); err != nil {
panic(err)
}
if s.upRPC, err = upGRPCv1.NewClient(c.GRPCClient.Up); err != nil {
panic(err)
}
s.createJobs()
s.databusHandler.GoWatch(s.archiveNotifyT, s.handleArchiveNotifyT)
s.databusHandler.GoWatch(s.archiveT, s.handleArchiveT)
return s
}
func (s *Service) createJobs() {
s.cron.AddFunc(conf.Conf.Job.UpCheckDateDueTaskTime, cronWrap(s.CheckDateDueJob))
s.cron.AddFunc(conf.Conf.Job.TaskScheduleTime, cronWrap(s.CheckTaskJob))
s.cron.AddFunc(conf.Conf.Job.CheckStateJobTime, cronWrap(s.CheckStateJob))
s.cron.AddFunc(conf.Conf.Job.UpdateUpTidJobTime, cronWrap(s.UpdateUpTidJob))
s.cron.Start()
}
func cronWrap(f func(tm time.Time)) func() {
return func() {
f(time.Now())
}
}
// Ping Service
func (s *Service) Ping(c context.Context) (err error) {
return s.crmdb.Ping(c)
}
// Close Service
func (s *Service) Close() {
s.databusHandler.Close()
s.wg.Wait()
s.crmdb.Close()
}

View File

@@ -0,0 +1,114 @@
package service
import (
"flag"
"go-common/app/job/main/up/conf"
"go-common/app/job/main/up/dao/upcrm"
"go-common/app/job/main/up/model/signmodel"
"html/template"
"os"
"testing"
)
func TestMain(m *testing.M) {
flag.Set("conf", "../cmd/up-job.toml")
flag.Parse()
if err := conf.Init(); err != nil {
panic(err)
}
m.Run()
os.Exit(0)
}
func TestTemplateSign(t *testing.T) {
var data = &dueData{
Signs: []*upcrm.SignWithName{
{Name: "test", SignUp: signmodel.SignUp{Mid: 123, EndDate: 1540901779}},
{Name: "tes2t", SignUp: signmodel.SignUp{Mid: 1234, EndDate: 1540902000}},
},
}
tmpl, err := template.New("signTitle").Funcs(funcHelper).Parse(conf.Conf.MailTemplateConf.SignTmplTitle)
if err != nil {
t.Errorf("err=%v", err)
t.FailNow()
}
err = tmpl.Execute(os.Stdout, data.Signs)
if err != nil {
t.Errorf("err=%v", err)
t.FailNow()
}
tmpl, err = template.New("sign").Funcs(funcHelper).Parse(conf.Conf.MailTemplateConf.SignTmplContent)
if err != nil {
t.Errorf("err=%v", err)
t.FailNow()
}
err = tmpl.Execute(os.Stdout, data.Signs)
if err != nil {
t.Errorf("err=%v", err)
t.FailNow()
}
}
func TestTemplatePay(t *testing.T) {
var data = &dueData{
Pays: []*upcrm.PayWithAdmin{
{Name: "test", SignPay: signmodel.SignPay{Mid: 123, DueDate: 1540901779, PayValue: 10000}},
{Name: "test", SignPay: signmodel.SignPay{Mid: 123, DueDate: 1540901779, PayValue: 10000}},
{Name: "test", SignPay: signmodel.SignPay{Mid: 123, DueDate: 1540901779, PayValue: 10000}},
},
}
tmpl, err := template.New("payTitle").Funcs(funcHelper).Parse(conf.Conf.MailTemplateConf.PayTmplTitle)
if err != nil {
t.Errorf("err=%v", err)
t.FailNow()
}
err = tmpl.Execute(os.Stdout, data.Pays)
if err != nil {
t.Errorf("err=%v", err)
t.FailNow()
}
tmpl, err = template.New("pay").Funcs(funcHelper).Parse(conf.Conf.MailTemplateConf.PayTmplContent)
if err != nil {
t.Errorf("err=%v", err)
t.FailNow()
}
err = tmpl.Execute(os.Stdout, data.Pays)
if err != nil {
t.Errorf("err=%v", err)
t.FailNow()
}
}
func TestTemplateTask(t *testing.T) {
var data = &dueData{
Tasks: []*upcrm.TaskWithAdmin{
{Name: "test", SignTaskHistory: signmodel.SignTaskHistory{Mid: 123, GenerateDate: 1540901779, TaskType: 2, TaskCounter: 1, TaskCondition: 10}},
{Name: "test", SignTaskHistory: signmodel.SignTaskHistory{Mid: 123, GenerateDate: 1540901779, TaskType: 3, TaskCounter: 1, TaskCondition: 10}},
{Name: "test", SignTaskHistory: signmodel.SignTaskHistory{Mid: 123, GenerateDate: 1540901779, TaskType: 0, TaskCounter: 1, TaskCondition: 10}},
{Name: "test", SignTaskHistory: signmodel.SignTaskHistory{Mid: 123, GenerateDate: 1540901779, TaskType: 1, TaskCounter: 1, TaskCondition: 10}},
}}
tmpl, err := template.New("task").Funcs(funcHelper).Parse(conf.Conf.MailTemplateConf.TaskTmplTitle)
if err != nil {
t.Errorf("err=%v", err)
t.FailNow()
}
err = tmpl.Execute(os.Stdout, data.Tasks)
if err != nil {
t.Errorf("err=%v", err)
t.FailNow()
}
tmpl, err = template.New("task").Funcs(funcHelper).Parse(conf.Conf.MailTemplateConf.TaskTmplContent)
if err != nil {
t.Errorf("err=%v", err)
t.FailNow()
}
err = tmpl.Execute(os.Stdout, data.Tasks)
if err != nil {
t.Errorf("err=%v", err)
t.FailNow()
}
}

View File

@@ -0,0 +1,295 @@
package service
import (
"bytes"
"context"
"errors"
"fmt"
"html/template"
"time"
//"go-common/app/job/main/up/conf"
"go-common/app/admin/main/up/util"
"go-common/app/job/main/up/dao/upcrm"
"go-common/app/job/main/up/model/signmodel"
"go-common/app/job/main/up/model/upcrmmodel"
account "go-common/app/service/main/account/model"
"go-common/library/log"
"go-common/app/job/main/up/conf"
)
var (
//ErrNoAdminName no admin name
ErrNoAdminName = errors.New("no admin name")
tmplSignDueTitle *template.Template
tmplSignDueContent *template.Template
tmplPayDueTitle *template.Template
tmplPayDueContent *template.Template
tmplTaskDueTitle *template.Template
tmplTaskDueContent *template.Template
)
// use for template function call
var funcHelper = template.FuncMap{
"Now": time.Now,
}
func (s *Service) initEmailTemplate() (err error) {
if conf.Conf.MailTemplateConf.SignTmplTitle == "" ||
conf.Conf.MailTemplateConf.SignTmplContent == "" ||
conf.Conf.MailTemplateConf.PayTmplTitle == "" ||
conf.Conf.MailTemplateConf.PayTmplContent == "" ||
conf.Conf.MailTemplateConf.TaskTmplTitle == "" ||
conf.Conf.MailTemplateConf.TaskTmplContent == "" {
err = fmt.Errorf(`mail template conf is invalid, check mail-template.toml file, make sure all the following has value:
TaskTmplContent
TaskTmplTitle
PayTmplContent
PayTmplTitle
SignTmplContent
SignTmplTitle`)
return
}
tmplSignDueTitle, err = template.New("signTitle").Funcs(funcHelper).Parse(conf.Conf.MailTemplateConf.SignTmplTitle)
if err != nil {
log.Error("parse template fail, err=%v", err)
return
}
tmplSignDueContent, err = template.New("signContent").Funcs(funcHelper).Parse(conf.Conf.MailTemplateConf.SignTmplContent)
if err != nil {
log.Error("parse template fail, err=%v", err)
return
}
tmplPayDueTitle, err = template.New("payTitle").Funcs(funcHelper).Parse(conf.Conf.MailTemplateConf.PayTmplTitle)
if err != nil {
log.Error("parse template fail, err=%v", err)
return
}
tmplPayDueContent, err = template.New("payContent").Funcs(funcHelper).Parse(conf.Conf.MailTemplateConf.PayTmplContent)
if err != nil {
log.Error("parse template fail, err=%v", err)
return
}
tmplTaskDueTitle, err = template.New("taskTitle").Funcs(funcHelper).Parse(conf.Conf.MailTemplateConf.TaskTmplTitle)
if err != nil {
log.Error("parse template fail, err=%v", err)
return
}
tmplTaskDueContent, err = template.New("taskContent").Funcs(funcHelper).Parse(conf.Conf.MailTemplateConf.TaskTmplContent)
if err != nil {
log.Error("parse template fail, err=%v", err)
return
}
return
}
//CheckDateDueJob check task due
/*
快到期的job提醒
*/
func (s *Service) CheckDateDueJob(date time.Time) {
log.Info("start run CheckDateDueJob, date=%s", date)
s.checkSignUpDue(date)
log.Info("finish run CheckDateDueJob, date=%s", date)
}
type dueData struct {
Signs []*upcrm.SignWithName
Pays []*upcrm.PayWithAdmin
Tasks []*upcrm.TaskWithAdmin
}
func (d *dueData) addSign(sign *upcrm.SignWithName) {
d.Signs = append(d.Signs, sign)
}
func (d *dueData) addPay(pay *upcrm.PayWithAdmin) {
d.Pays = append(d.Pays, pay)
}
func (d *dueData) addTask(task *upcrm.TaskWithAdmin) {
d.Tasks = append(d.Tasks, task)
}
func getOrCreate(dataMap map[string]*dueData, key string) *dueData {
var data, ok = dataMap[key]
if !ok {
data = &dueData{}
dataMap[key] = data
}
return data
}
func getName(infoMap map[int64]*account.Info, mid int64) string {
if info, ok := infoMap[mid]; ok {
return info.Name
}
return ""
}
func (s *Service) checkSignUpDue(date time.Time) {
// 30天内到期 sign
list, err := s.crmdb.GetDueSignUp(date, 30)
s.crmdb.StartTask(upcrmmodel.TaskTypeSignCheckDue, date)
defer func() {
if err == nil {
s.crmdb.FinishTask(upcrmmodel.TaskTypeSignCheckDue, date, upcrmmodel.TaskStateFinish)
} else {
s.crmdb.FinishTask(upcrmmodel.TaskTypeSignCheckDue, date, upcrmmodel.TaskStateError)
}
}()
if err != nil {
log.Error("fail to get due sign, date=%+v, err=%+v", date, err)
return
}
var adminDueDataMap = make(map[string]*dueData)
var ids []int64
for _, v := range list {
ids = append(ids, v.Mid)
var data = getOrCreate(adminDueDataMap, v.AdminName)
data.addSign(v)
}
// 7天内到期的pay
listPayDue, err := s.crmdb.GetDuePay(date, 7)
if err != nil {
log.Error("fail to get due pay, date=%+v, err=%+v", date, err)
return
}
for _, v := range listPayDue {
ids = append(ids, v.Mid)
var data = getOrCreate(adminDueDataMap, v.AdminName)
data.addPay(v)
}
// 到期的任务,
listTaskDue, err := s.crmdb.GetDueTask(date)
if err != nil {
log.Error("fail to get due task, date=%+v, err=%+v", date, err)
return
}
for _, v := range listTaskDue {
ids = append(ids, v.Mid)
var data = getOrCreate(adminDueDataMap, v.AdminName)
data.addTask(v)
}
ids = util.Unique(ids)
infoMap, e := s.acc.GetCachedInfos(context.Background(), ids, "")
if e == nil {
for _, v := range list {
v.Name = getName(infoMap, v.Mid)
}
for _, v := range listPayDue {
v.Name = getName(infoMap, v.Mid)
}
for _, v := range listTaskDue {
v.Name = getName(infoMap, v.Mid)
}
}
for admin, v := range adminDueDataMap {
var adminAll = append(conf.Conf.MailConf.DueMailReceivers, admin)
// 发送sign到期邮件
var due = v
s.worker.Add(func() {
var succIds []uint32
if len(due.Signs) > 0 {
var e = s.sendMailWithTemplate(due.Signs, tmplSignDueTitle, tmplSignDueContent, adminAll...)
if e == nil {
for _, data := range due.Signs {
succIds = append(succIds, data.ID)
}
} else {
log.Warn("fail to send email, err=%v", e)
}
// 更新邮件发送标记
s.crmdb.UpdateEmailState(signmodel.TableNameSignUp, succIds, signmodel.EmailStateSendSucc)
}
if len(due.Pays) > 0 {
// 发送pay到期邮件
succIds = nil
e = s.sendMailWithTemplate(due.Pays, tmplPayDueTitle, tmplPayDueContent, adminAll...)
if e == nil {
for _, data := range due.Pays {
succIds = append(succIds, data.ID)
}
} else {
log.Warn("fail to send email, err=%v", e)
}
// 更新邮件发送标记
s.crmdb.UpdateEmailState(signmodel.TableNameSignPay, succIds, signmodel.EmailStateSendSucc)
}
if len(due.Tasks) > 0 {
// 发送task到期邮件
e = s.sendMailWithTemplate(due.Tasks, tmplTaskDueTitle, tmplTaskDueContent, adminAll...)
if e != nil {
log.Warn("fail to send email, err=%v", e)
}
// 这个没有邮件发送的标记
//s.crmdb.UpdateEmailState(signmodel.TableNameSignPay, succIds, signmodel.EmailStateSendSucc)
}
})
}
}
// data, data to generate email content
// contentTmpl, template to generate email content
// adminname, slice for all admin name
//
func (s *Service) sendMailWithTemplate(data interface{}, subjectTmpl, contentTmpl *template.Template, adminName ...string) (err error) {
if contentTmpl == nil {
err = fmt.Errorf("template for email is nil, data=%+v", data)
log.Error("%s", err)
return
}
var contentBuf = bytes.NewBuffer(nil)
err = contentTmpl.Execute(contentBuf, data)
if err != nil {
log.Error("template fail to execute, err=%v", err)
return
}
var subjectBuf = bytes.NewBuffer(nil)
err = subjectTmpl.Execute(subjectBuf, data)
if err != nil {
log.Error("template fail to execute, err=%v", err)
return
}
var addrs []string
for _, v := range adminName {
if v == "" {
log.Warn("admin name is empty")
continue
}
var addr = fmt.Sprintf("%s@bilibili.com", v)
addrs = append(addrs, addr)
}
//log.Info("email sub=%s, content=%s", subjectBuf.String(), contentBuf.String())
if len(addrs) == 0 {
log.Error("admin name is empty, cannot send email, data=%+v", data)
err = ErrNoAdminName
return
}
log.Info("email send , sub=%s, admin=%s, data=%+v", subjectBuf.String(), adminName, data)
err = s.maildao.SendMail(contentBuf.String(), subjectBuf.String(), addrs)
return
}

View File

@@ -0,0 +1,134 @@
package service
import (
"github.com/jinzhu/gorm"
"go-common/app/job/main/up/dao/upcrm"
"go-common/app/job/main/up/model/signmodel"
"go-common/app/service/main/upcredit/mathutil"
"go-common/library/log"
"time"
)
// change a lot state for sign up
// state, 签约状态,正常或过期
// due_wan, 签约状态即将过期
// pay_expire_state付费状态
//CheckStateJob 检查sign_up中的状态
func (s *Service) CheckStateJob(date time.Time) {
log.Info("start run state job, date=%s", date)
s.checkState(date)
log.Info("finish run state job, date=%s", date)
}
func (s *Service) checkState(date time.Time) {
// 更新签约状态:
var crmdb = s.crmdb.GetDb()
// 签约30天内到期
var signExpireList []*signmodel.SignUp
var signDueList []*signmodel.SignUp
var limit, offset = 200, 0
var count = limit
var err error
var signEndDate = date.AddDate(0, 0, 30)
for count == limit {
var items []*signmodel.SignUp
// 把endDate在[-60, +30]范围内的用户全都找出来,过一遍
err = crmdb.Offset(offset).Limit(limit).Where("end_date<? and end_date>?", signEndDate, date.AddDate(0, -4, 0)).Find(&items).Error
if err != nil {
log.Error("fail to get sign ups, err=%v", err)
break
}
count = len(items)
offset += count
for _, v := range items {
if v.EndDate.Time().Before(date) && v.State == signmodel.SignStateOnSign {
// 过期
signExpireList = append(signExpireList, v)
} else if !(v.EndDate.Time().Before(date) || v.EndDate.Time().After(signEndDate)) && v.DueWarn <= signmodel.DueWarnNoWarn {
// 快到期
signDueList = append(signDueList, v)
}
}
}
var expireIDList []uint32
for _, v := range signExpireList {
expireIDList = append(expireIDList, v.ID)
}
err = updateListWithLimit(crmdb, "state", signmodel.SignStateExpire, expireIDList, limit)
if err != nil {
log.Error("fail to update sign state, err=%v", err)
return
}
var dueIDList []uint32
for _, v := range signDueList {
dueIDList = append(dueIDList, v.ID)
}
err = updateListWithLimit(crmdb, "due_warn", signmodel.DueWarnWarn, dueIDList, limit)
if err != nil {
log.Error("fail to update sign due_warn, err=%v", err)
return
}
// 付款7天内到期
offset = 0
count = limit
var payDueDate = date.AddDate(0, 0, 7)
var payExpireMap = map[uint32]int8{}
for count == limit {
var items []*signmodel.SignPay
// 把endDate在[-max, +7]范围内的用户全都找出来,过一遍
err = crmdb.Offset(offset).Limit(limit).Where("due_date<?", payDueDate).Find(&items).Error
if err != nil {
log.Error("fail to get sign ups, err=%v", err)
break
}
count = len(items)
offset += count
for _, v := range items {
// 如果有即将到期,则立即标记为即将到期
if v.State == upcrm.PayStateUnpay {
payExpireMap[v.SignID] = signmodel.PayExpireStateDue
} else {
// 如果没有即将到期的状态,则把其标记为未到期
// 即:只有所有的付款都已标记完成,才会认为是未到期状态
if payExpireMap[v.SignID] != signmodel.PayExpireStateDue {
payExpireMap[v.SignID] = signmodel.PayExpireStateNormal
}
}
}
}
// 更新到期状态
var stateSignIDListMap = map[int8][]uint32{}
for k, v := range payExpireMap {
stateSignIDListMap[v] = append(stateSignIDListMap[v], k)
}
// 分区更新
limit = 200
for state, list := range stateSignIDListMap {
err = updateListWithLimit(crmdb, "pay_expire_state", state, list, limit)
if err != nil {
log.Error("err update pay_expire_state, err=%v", err)
return
}
}
}
func updateListWithLimit(crmdb *gorm.DB, field string, state int8, list []uint32, limit int) (err error) {
for begin := 0; begin < len(list); begin += limit {
var end = mathutil.Min(begin+limit, len(list))
var needUpdate = list[begin:end]
err = updateSignTable(crmdb, needUpdate, field, state)
if err != nil {
log.Error("fail to update state [%s], err=%v", field, err)
return
}
}
return
}
func updateSignTable(crmdb *gorm.DB, ids interface{}, field string, state int8) (err error) {
err = crmdb.Table(signmodel.TableNameSignUp).Where("id in (?)", ids).Update(field, state).Error
return
}

View File

@@ -0,0 +1,300 @@
package service
import (
"context"
"time"
"go-common/app/admin/main/up/util"
"go-common/app/job/main/up/conf"
"go-common/app/job/main/up/dao/upcrm"
"go-common/app/job/main/up/model/signmodel"
"go-common/app/job/main/up/model/upcrmmodel"
v1 "go-common/app/service/main/archive/api"
"go-common/library/log"
xtime "go-common/library/time"
"github.com/jinzhu/gorm"
)
//CheckTaskJob check task job
func (s *Service) CheckTaskJob(tm time.Time) {
// 今天计算昨天的数据
var yesterday = tm.AddDate(0, 0, -1)
log.Info("start to run CheckTaskJob, date=%s, yesterday=%s", tm, yesterday)
s.CheckTaskFinish(yesterday)
log.Info("finish run CheckTaskJob, date=%s", tm)
}
//Archive data
type Archive struct {
ID int64 `gorm:"column:id"`
}
//CheckTaskFinish check task finish, calculate datas in (-,date]
func (s *Service) CheckTaskFinish(date time.Time) {
// 1.查找所有有效的合同id, begin_date <= date && end _date >= date
// 2.找到所有合同id对应的任务id,
// 3.根据任务类型,日、周、月、累计,计算任务周期[a,b)
// 4.计算完成数量
var crmdb = s.crmdb.GetDb()
var dateStr = date.Format(upcrmmodel.TimeFmtDate)
var offset = 0
var limit = 200
var actualSize = limit
log.Info("start to check task state")
archiveDb, err := gorm.Open("mysql", conf.Conf.ArchiveOrm.DSN)
s.crmdb.StartTask(upcrmmodel.TaskTypeSignTaskCalculate, date)
archiveDb.LogMode(true)
if err != nil {
log.Error("connect archive db fail")
return
}
defer archiveDb.Close()
defer func() {
if err == nil {
s.crmdb.FinishTask(upcrmmodel.TaskTypeSignTaskCalculate, date, upcrmmodel.TaskStateFinish)
} else {
s.crmdb.FinishTask(upcrmmodel.TaskTypeSignTaskCalculate, date, upcrmmodel.TaskStateError)
}
}()
var taskTotalCount = 0
for actualSize == limit {
var signUps []*signmodel.SignUp
var signUpMap = make(map[uint32]*signmodel.SignUp)
// 1
err = crmdb.Table(signmodel.TableNameSignUp).
Select("id, begin_date, end_date").
Where("begin_date <= ? and end_date >= ?", dateStr, dateStr).
Offset(offset).
Limit(limit).
Find(&signUps).Error
offset += limit
if err != nil && err != gorm.ErrRecordNotFound {
log.Error("err get signs, err=%+v", err)
return
}
actualSize = len(signUps)
var signIDs []uint32
for _, v := range signUps {
signUpMap[v.ID] = v
signIDs = append(signIDs, v.ID)
}
// 2
var taskList []*signmodel.SignTask
err = crmdb.Where("sign_id in (?) and state != ? and generate_date<?", signIDs, signmodel.SignTaskStateDelete, dateStr).
Find(&taskList).Error
if err != nil {
log.Error("err get tasks, err=%+v", err)
return
}
// 3
for _, task := range taskList {
taskTotalCount++
if task.Mid == 0 {
log.Error("task's mid is zero, please check! task id=%d", task.ID)
continue
}
var signInfo, ok = signUpMap[task.SignID]
if !ok {
log.Error("sign not found, err=%v", err)
continue
}
// 4 计算数量
err = s.checkSingleTask(task, signInfo, date, archiveDb)
if err != nil {
log.Error("check task err, task_id=%d, err=%v", task.ID, err)
continue
}
}
log.Info("finish to check task state, task total num=%d", taskTotalCount)
}
}
// get task history, if not exist, then will create it
func (s *Service) getOrCreateTaskHistory(task *signmodel.SignTask, generateDate time.Time) (res *signmodel.SignTaskHistory, err error) {
var crmdb = s.crmdb.GetDb()
res = new(signmodel.SignTaskHistory)
err = crmdb.Select("*").Where("task_template_id=? and generate_date=?", task.ID, generateDate).
Find(&res).Error
// 创建一条,如果没找到的话
if err == gorm.ErrRecordNotFound {
res = &signmodel.SignTaskHistory{
Mid: task.Mid,
SignID: task.SignID,
TaskTemplateID: task.ID,
TaskType: task.TaskType,
TaskCondition: task.TaskCondition,
Attribute: task.Attribute,
GenerateDate: xtime.Time(generateDate.Unix()),
State: signmodel.SignTaskStateRunning,
}
err = crmdb.Save(&res).Error
if err != nil {
log.Error("create task history fail, err=%v, task=%v", err, task)
return
}
}
return
}
// check task state
func (s *Service) checkSingleTask(task *signmodel.SignTask, signInfo *signmodel.SignUp, date time.Time, archiveDb *gorm.DB) (err error) {
var taskBegin, taskEnd time.Time
if task.TaskType == signmodel.TaskTypeAccumulate {
taskBegin = signInfo.BeginDate.Time()
taskEnd = signInfo.EndDate.Time()
} else {
taskBegin, taskEnd = upcrm.GetTaskDuration(date, task.TaskType)
}
if task.Mid == 0 {
log.Error("task's mid is zero, please check! task id=%d", task.ID)
return
}
// get task history
// 如果是累计任务这里的taskBegin要设置为0
var tBegin = taskBegin
if task.TaskType == signmodel.TaskTypeAccumulate {
tBegin = time.Time{}
}
taskHistory, err := s.getOrCreateTaskHistory(task, tBegin)
if err != nil {
log.Error("get task history fail, task=%+v, err=%+v", task, err)
return
}
var dateStr = date.Format(upcrmmodel.TimeFmtDate)
switch {
default:
var crmdb = s.crmdb.GetDb()
// 4.去稿件库中查找对应的稿件数量
var archiveCount = 0
var archiveList []*Archive
err = archiveDb.Table("archive").
Where("mid = ? and ctime>= ? and ctime <? and (state >= 0 or state = -6)",
task.Mid, taskBegin, taskEnd).
Select("id").
Find(&archiveList).
Error
if err != nil && err != gorm.ErrRecordNotFound {
log.Error("check archive count fail, taskid=%+v err=%+v", task, err)
break
}
var finalResult []int64
// 5.任务完成度统计时,
// 若录入为不包含商单,
// 则任务完成数=新增稿件数-减去(绿洲/商单报备)稿件数+请假任务数;
// 若录入为包含商单,
// 则任务完成数=新增稿件数+请假任务数;
// 如果没有archive就直接返回
if len(archiveList) != 0 {
// 需要判断商单
if task.IsAttrSet(signmodel.SignTaskAttrBitBusiness) {
var ids []int64
for _, v := range archiveList {
ids = append(ids, v.ID)
}
ids = util.Unique(ids)
// 查询archive服务
archiveResult, e := s.arcRPC.Arcs(context.Background(), &v1.ArcsRequest{Aids: ids})
if e != nil {
err = e
log.Error("get archive result err, err=%+v", err)
break
}
for _, v := range archiveList {
a, ok := archiveResult.Arcs[v.ID]
// 是商单的要排除
if !ok || a.OrderID > 0 {
continue
}
finalResult = append(finalResult, v.ID)
}
} else {
for _, v := range archiveList {
finalResult = append(finalResult, v.ID)
}
}
archiveCount = len(finalResult)
}
// 请假任务数
var absence signmodel.SignTaskAbsence
err = crmdb.Select("sum(absence_count) as absence_count").
Where("task_history_id=? and state!=?", taskHistory.ID, signmodel.SignTaskAbsenceStateDelete).
Find(&absence).Error
if err != nil {
log.Error("get task absence fail, task history=%+v", taskHistory)
return
}
archiveCount += int(absence.AbsenceCount)
log.Info("task count=%d, archive=%d, absence=%d, task=%+v", archiveCount, len(finalResult), absence.AbsenceCount, taskHistory)
// 更新task history的数量
task.TaskCounter = int32(archiveCount)
var tx = crmdb.Begin()
defer func() {
if r := recover(); r != nil || err != nil {
log.Error("roll back task update, task=%+v, r=%+v | err=%+v", task, r, err)
tx.Rollback()
}
}()
err = tx.Table(signmodel.TableNameSignTask).Where("id=?", task.ID).
Updates(map[string]interface{}{
"generate_date": dateStr,
}).Error
if err != nil {
log.Error("update sign task fail, task=%+v, err=%+v", task, err)
return
}
// update history
var state = signmodel.SignTaskStateRunning
if archiveCount >= int(task.TaskCondition) {
state = signmodel.SignTaskStateFinish
}
err = tx.Table(signmodel.TableNameSignTaskHistory).Where("id=?", taskHistory.ID).
Updates(map[string]interface{}{
"task_counter": task.TaskCounter,
"task_condition": task.TaskCondition,
"state": state,
"attribute": task.Attribute,
"task_type": task.TaskType,
}).Error
if err != nil {
log.Error("update sign task history fail, task=%+v, err=%+v", task, err)
return
}
// update sign
err = tx.Table(signmodel.TableNameSignUp).Where("id=?", task.SignID).
Updates(map[string]interface{}{
"task_state": state,
}).Error
if err != nil {
log.Error("update sign up fail, task=%+v, err=%+v", task, err)
return
}
err = tx.Commit().Error
if err != nil {
log.Error("commit err, err=%+v", err)
}
}
return
}

View File

@@ -0,0 +1,66 @@
package service
import (
"go-common/app/admin/main/up/util"
"go-common/app/admin/main/up/util/mathutil"
"go-common/app/job/main/up/model/signmodel"
"go-common/app/job/main/up/model/upcrmmodel"
"go-common/library/log"
"time"
)
// UpdateUpTidJob 检查sign_up中的状态
func (s *Service) UpdateUpTidJob(date time.Time) {
log.Info("start run UpdateUpTidJob, date=%s", date)
s.updateUpTidJob(date)
log.Info("finish run UpdateUpTidJob, date=%s", date)
}
func (s *Service) updateUpTidJob(date time.Time) {
// 获取所有mid信息
var limit = 200
var count = limit
var err error
var mids []int64
var offset = 0
for limit == count {
var signList []*signmodel.SignUp
if err = s.crmdb.GetDb().Offset(offset).Limit(limit).Find(&signList).Error; err != nil {
log.Error("fail to get signs from sign ups, err=%v", err)
return
}
count = len(signList)
offset += count
for _, v := range signList {
mids = append(mids, v.Mid)
}
}
mids = util.Unique(mids)
// 从up_base_info中读取tid并更新
// <tid, mid list>
var tidMidMap = make(map[int64][]int64)
for begin := 0; begin < len(mids); begin += limit {
var end = mathutil.Min(begin+limit, len(mids))
var baseInfoList []*upcrmmodel.UpBaseInfo
if err = s.crmdb.GetDb().Select("mid, active_tid").Where("mid in (?)", mids[begin:end]).Limit(limit).Find(&baseInfoList).Error; err != nil {
log.Error("fail to get signs from sign ups, err=%v", err)
return
}
// 更新到sign表中
for _, v := range baseInfoList {
tidMidMap[v.ActiveTid] = append(tidMidMap[v.ActiveTid], v.Mid)
}
}
// 更新到sign_up表中
for k, v := range tidMidMap {
if err = s.crmdb.GetDb().Table(signmodel.TableNameSignUp).Where("mid in (?)", v).Update("active_tid", k).Error; err != nil {
log.Error("update sign up's active tid fail, err=%v", err)
return
}
}
}