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,84 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_test",
"go_library",
)
go_test(
name = "go_default_test",
srcs = [
"associate_test.go",
"autorenew_test.go",
"coupon_test.go",
"frozen_test.go",
"order_test.go",
"salary_test.go",
"service_test.go",
"sync_test.go",
"vip_test.go",
],
embed = [":go_default_library"],
rundir = ".",
tags = ["automanaged"],
deps = [
"//app/job/main/vip/conf:go_default_library",
"//app/job/main/vip/model:go_default_library",
"//library/log:go_default_library",
"//vendor/github.com/smartystreets/goconvey/convey:go_default_library",
],
)
go_library(
name = "go_default_library",
srcs = [
"associate.go",
"autorenew.go",
"bcoin.go",
"check_data.go",
"coupon.go",
"frozen.go",
"message.go",
"order.go",
"push.go",
"salary.go",
"salary_data.go",
"service.go",
"sync.go",
"vip.go",
],
importpath = "go-common/app/job/main/vip/service",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/job/main/vip/conf:go_default_library",
"//app/job/main/vip/dao:go_default_library",
"//app/job/main/vip/model:go_default_library",
"//app/service/main/coupon/model:go_default_library",
"//app/service/main/coupon/rpc/client:go_default_library",
"//app/service/main/vip/api:go_default_library",
"//app/service/main/vip/model:go_default_library",
"//library/database/sql:go_default_library",
"//library/log:go_default_library",
"//library/queue/databus:go_default_library",
"//library/sync/errgroup:go_default_library",
"//library/time:go_default_library",
"//vendor/github.com/pkg/errors: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,37 @@
package service
import (
"context"
"time"
"go-common/app/job/main/vip/model"
v1 "go-common/app/service/main/vip/api"
vipmol "go-common/app/service/main/vip/model"
"go-common/library/log"
)
func (s *Service) eleEompensateJob() {
log.Info("ele grant eompensate job start..................")
if succeed := s.dao.AddTransferLock(context.TODO(), "lock:elegrant"); succeed {
if err := s.EleGrantCompensate(context.TODO()); err != nil {
log.Error("error(%+v)", err)
}
}
log.Info("ele grant eompensate job end..................")
}
// EleGrantCompensate ele frant compensate.
func (s *Service) EleGrantCompensate(c context.Context) (err error) {
var res []*model.VipOrderActivityRecord
if res, err = s.dao.NotGrantActOrders(c, vipmol.PanelTypeEle, s.c.Property.NotGrantLimit); err != nil {
return
}
for _, v := range res {
if _, err = s.vipgRPC.EleVipGrant(c, &v1.EleVipGrantReq{OrderNo: v.OrderNo}); err != nil {
log.Error("EleVipGrant a(%s) err(%+v)", v.OrderNo, err)
continue
}
time.Sleep(time.Second)
}
return
}

View File

@@ -0,0 +1,22 @@
package service
import (
"testing"
. "github.com/smartystreets/goconvey/convey"
)
//go test -test.v -test.run TestEleEompensateJob
func TestEleEompensateJob(t *testing.T) {
Convey("TestEleEompensateJob ", t, func() {
s.eleEompensateJob()
})
}
//go test -test.v -test.run TestEleGrantCompensate
func TestEleGrantCompensate(t *testing.T) {
Convey("TestEleGrantCompensate ", t, func() {
err := s.EleGrantCompensate(c)
So(err, ShouldNotBeNil)
})
}

View File

@@ -0,0 +1,66 @@
package service
import (
"context"
"encoding/json"
"go-common/app/job/main/vip/model"
"go-common/library/log"
)
func (s *Service) autoRenewPay(c context.Context, n *model.VipUserInfoMsg, o *model.VipUserInfoMsg) (err error) {
// wechat autorenew 扣款重放条件:
// 1.自动续费用户
// 2.非IAP支付方式
// 3.用户状态发生变化
// 4.旧状态为未过期
// 5.新状态为过期
// 6.新旧类型都是不是NotVip type
if n.IsAutoRenew == model.AutoRenew &&
o.IsAutoRenew == model.AutoRenew &&
n.PayChannelID != model.IAPChannelID &&
o.PayChannelID != model.IAPChannelID &&
n.Status != o.Status &&
o.Status == model.VipStatusNotOverTime &&
n.Status == model.VipStatusOverTime &&
n.Type != model.NotVip && o.Type != model.NotVip {
_, err = s.dao.AutoRenewPay(c, n.Mid)
}
return
}
func (s *Service) retryautorenewpayproc() {
defer s.waiter.Done()
msgs := s.autoRenewdDatabus.Messages()
var err error
for {
msg, ok := <-msgs
if !ok {
log.Warn("[service.retryautorenewpayproc|vip] dataConsumer has been closed.")
return
}
if err = msg.Commit(); err != nil {
log.Error("retryautorenewpayproc msg.Commit err(%+v)", err)
}
log.Info("cur consumer retryautorenewpayproc(%v)", string(msg.Value))
v := &model.Message{}
if err = json.Unmarshal([]byte(msg.Value), v); err != nil {
log.Error("retryautorenewpayproc json.Unmarshal(%v) err(%v)", v, err)
continue
}
if v.Table != _tableUserInfo || v.Action != _updateAction {
continue
}
n := new(model.VipUserInfoMsg)
if err = json.Unmarshal(v.New, n); err != nil {
log.Error("retryautorenewpayproc json.Unmarshal val(%v) error(%v)", string(v.New), err)
continue
}
o := new(model.VipUserInfoMsg)
if err = json.Unmarshal(v.Old, o); err != nil {
log.Error("retryautorenewpayproc json.Unmarshal val(%v) error(%v)", string(v.Old), err)
continue
}
s.autoRenewPay(context.Background(), n, o)
}
}

View File

@@ -0,0 +1,39 @@
package service
import (
"testing"
"go-common/app/job/main/vip/model"
. "github.com/smartystreets/goconvey/convey"
)
//go test -test.v -test.run TestAutoRenewPay
func TestAutoRenewPay(t *testing.T) {
Convey("TestAutoRenewPay ", t, func() {
err := s.autoRenewPay(c, &model.VipUserInfoMsg{
IsAutoRenew: model.AutoRenew,
PayChannelID: 100,
Status: 0,
Type: 1,
}, &model.VipUserInfoMsg{
Type: 1,
Status: 1,
IsAutoRenew: model.AutoRenew,
PayChannelID: 1,
})
So(err, ShouldBeNil)
err = s.autoRenewPay(c, &model.VipUserInfoMsg{
IsAutoRenew: model.AutoRenew,
PayChannelID: 1,
Status: 0,
Type: 1,
}, &model.VipUserInfoMsg{
Type: 1,
Status: 1,
IsAutoRenew: model.AutoRenew,
PayChannelID: 1,
})
So(err, ShouldBeNil)
})
}

View File

@@ -0,0 +1,256 @@
package service
import (
"context"
"crypto/md5"
"encoding/hex"
"fmt"
"time"
"go-common/app/job/main/vip/model"
"go-common/library/log"
xtime "go-common/library/time"
"github.com/pkg/errors"
)
//HandlerBcoin handler bcoin
func (s *Service) HandlerBcoin() (err error) {
var (
batchSize int64 = 2000
oldMaxID int64
newMaxID int64
exitMap = make(map[string]int)
batch []*model.VipBcoinSalary
)
if oldMaxID, err = s.dao.SelOldBcoinMaxID(context.TODO()); err != nil {
log.Error("s.dao.SelOldBcoinMaxID error(%+v)", err)
return
}
if newMaxID, err = s.dao.SelBcoinMaxID(context.TODO()); err != nil {
log.Error("s.dao.SelBcoinMaxID error(%v)", err)
return
}
page := newMaxID / batchSize
if newMaxID%batchSize != 0 {
page++
}
for i := 0; i < int(page); i++ {
arg := new(model.QueryBcoinSalary)
arg.StartID = int64(i) * batchSize
arg.EndID = int64(i+1) * batchSize
if arg.EndID > newMaxID {
arg.EndID = newMaxID
}
arg.GiveNowStatus = -1
var res []*model.VipBcoinSalary
if res, err = s.dao.SelBcoinSalaryData(context.TODO(), arg.StartID, arg.EndID); err != nil {
log.Error("s.dao.SelBcoinSalary(%+v) error(%+v)", arg, err)
return
}
for _, v := range res {
exitMap[s.makeBcoinMD5(v)] = 1
}
}
page = oldMaxID / batchSize
if oldMaxID%batchSize != 0 {
page++
}
for i := 0; i < int(page); i++ {
startID := int64(i) * batchSize
EndID := int64(i+1) * batchSize
if EndID > oldMaxID {
EndID = oldMaxID
}
var res []*model.VipBcoinSalary
if res, err = s.dao.SelOldBcoinSalary(context.TODO(), startID, EndID); err != nil {
log.Error("sel.OldBcoinSalary(startID:%v endID:%v) error(%+v)", startID, EndID, err)
return
}
for _, v := range res {
if exitMap[s.makeBcoinMD5(v)] == 0 {
batch = append(batch, v)
}
}
if err = s.dao.BatchAddBcoinSalary(batch); err != nil {
log.Error("s.dao.BatchAddBcoinSalary (%+v)", err)
return
}
batch = nil
}
return
}
func (s *Service) handleraddbcoinproc() {
defer func() {
if x := recover(); x != nil {
log.Error("service.handlerupdaterechargeorderproc panic(%v)", x)
go s.handleraddbcoinproc()
log.Info("service.handlerupdaterechargeorderproc recover")
}
}()
for {
msg := <-s.handlerAddBcoinSalary
log.Info("cur bcoin msage:%+v", msg)
for i := 0; i < s.c.Property.Retry; i++ {
if err := s.dao.AddBcoinSalary(context.TODO(), msg); err != nil {
log.Error("s.dao.addbcoinsalary(%+v) error(%+v)", msg, err)
} else if err == nil {
break
}
}
}
}
func (s *Service) handlerdelbcoinproc() {
defer func() {
if x := recover(); x != nil {
log.Error("service.handlerdelbcoinproc panic(%v)", x)
go s.handlerdelbcoinproc()
log.Info("service.handlerdelbcoinproc recover")
}
}()
var err error
for {
msg := <-s.handlerDelBcoinSalary
for i := 0; i < s.c.Property.Retry; i++ {
if err = s.dao.DelBcoinSalary(context.TODO(), msg.Payday, msg.Mid); err == nil {
break
}
log.Error("s.dao.DelBcoinSalary(msg:%+v) error(%+v)", msg, err)
}
}
}
func (s *Service) handlerupdatebcoinproc() {
defer func() {
if x := recover(); x != nil {
log.Error("service.handlerupdaterechargeorderproc panic(%v)", x)
go s.handlerupdatebcoinproc()
log.Info("service.handlerupdaterechargeorderproc recover")
}
}()
var err error
for {
msg := <-s.handlerUpdateBcoinSalary
for i := 0; i < s.c.Property.Retry; i++ {
if err = s.dao.UpdateBcoinSalary(context.TODO(), msg.Payday, msg.Mid, msg.Status); err == nil {
break
}
log.Error("s.dao.UpdateBcoinSalary(msg:%+v) error(%+v)", msg, err)
}
}
}
func (s *Service) sendBcoinJob() {
defer func() {
if r := recover(); r != nil {
r = errors.WithStack(r.(error))
log.Error("recover panic error(%+v)", r)
}
}()
log.Info("sen bcoin job start ........................................ ")
s.sendBcoin()
log.Info("sen bcoin job end ........................................ ")
}
func (s *Service) sendBcoin() {
var (
maxID int64
batchSize int64 = 3000
sendSize = 50
err error
)
if maxID, err = s.dao.SelBcoinMaxID(context.TODO()); err != nil {
log.Error("s.dao.selBcoinMaxID() error(%+v)", err)
return
}
page := maxID / batchSize
if maxID%batchSize != 0 {
page++
}
now := time.Now()
startMonth := now.AddDate(0, 0, 1-now.Day())
endMonth := startMonth.AddDate(0, 1, 0)
sendInfo := s.sendInfo()
for i := 0; i < int(page); i++ {
arg := new(model.QueryBcoinSalary)
arg.StartID = int64(i) * batchSize
arg.EndID = int64(i+1) * batchSize
arg.GiveNowStatus = 0
arg.Status = 0
arg.StartMonth = xtime.Time(startMonth.Unix())
arg.EndMonth = xtime.Time(endMonth.Unix())
var res []*model.VipBcoinSalary
if res, err = s.dao.SelBcoinSalary(context.TODO(), arg); err != nil {
log.Error("s.dao.selBcoinSalary(%+v) error(%+v)", arg, err)
return
}
pageSend := len(res) / sendSize
if len(res)%sendSize != 0 {
pageSend++
}
for j := 0; j < pageSend; j++ {
start := j * sendSize
end := int(j+1) * sendSize
if end > len(res) {
end = len(res)
}
if err = s.sendBocinNow(res[start:end], sendInfo.Amount, sendInfo.DueDate); err != nil {
log.Error("%+v", err)
return
}
}
}
}
func (s *Service) sendInfo() (r *model.BcoinSendInfo) {
var (
c time.Time
day = s.c.Property.AnnualVipBcoinDay
amount = s.c.Property.AnnualVipBcoinCouponMoney
)
r = new(model.BcoinSendInfo)
r.Amount = int32(amount)
r.DayOfMonth = day
c = time.Now()
c = c.AddDate(0, 1, int(day)-1-c.Day())
r.DueDate = xtime.Time(c.Unix())
return
}
func (s *Service) sendBocinNow(res []*model.VipBcoinSalary, amount int32, duTime xtime.Time) (err error) {
var (
mids []int64
ids []int64
)
for _, v := range res {
mids = append(mids, v.Mid)
ids = append(ids, v.ID)
}
if err = s.dao.SendBcoin(context.TODO(), mids, amount, duTime, "127.0.0.1"); err != nil {
err = errors.WithStack(err)
return
}
if err = s.dao.UpdateBcoinSalaryBatch(context.TODO(), ids, 1); err != nil {
err = errors.WithStack(err)
return
}
return
}
func (s *Service) makeBcoinMD5(r *model.VipBcoinSalary) string {
key := fmt.Sprintf("%v,%v,%v,%v,%v,%v", r.Mid, r.Memo, r.Amount, r.Payday.Time().Format("2006-01-02"), r.GiveNowStatus, r.Status)
hash := md5.New()
hash.Write([]byte(key))
sum := hash.Sum(nil)
return hex.EncodeToString(sum)
}

View File

@@ -0,0 +1,226 @@
package service
import (
"context"
"time"
"go-common/app/job/main/vip/model"
"go-common/library/log"
"github.com/pkg/errors"
)
// CheckUserData check vip_user_info data.
func (s *Service) CheckUserData(c context.Context) (diffs map[int64]string, err error) {
var (
maxID int
size = s.c.Property.BatchSize
ids = []int64{}
ousers = make(map[int64]*model.VipUserInfoOld, size)
nusers = make(map[int64]*model.VipUserInfo, _ps)
ou *model.VipUserInfoOld
nu *model.VipUserInfo
ok bool
)
diffs = make(map[int64]string)
if maxID, err = s.dao.SelOldUserInfoMaxID(context.TODO()); err != nil {
err = errors.WithStack(err)
return
}
page := maxID / size
if maxID%size != 0 {
page++
}
log.Info("check vip_user_info total(%d)", page)
for i := 0; i < page; i++ {
log.Info("check vip_user_info page index(%d) total(%d)", i, page)
startID := i * size
endID := (i + 1) * size
if endID > maxID {
endID = maxID
}
if ousers, err = s.dao.SelOldUserInfoMaps(context.TODO(), startID, endID); err != nil {
return
}
j := 1
for _, v := range ousers {
ids = append(ids, v.Mid)
if j%_ps == 0 || j == len(ousers) {
if nusers, err = s.dao.SelVipByIds(context.TODO(), ids); err != nil {
return
}
for _, mid := range ids {
if ou, ok = ousers[mid]; !ok {
diffs[mid] = "old not found"
continue
}
if nu, ok = nusers[mid]; !ok {
diffs[mid] = "new not found"
continue
}
if nu.Type != ou.Type {
diffs[mid] = "vip_type"
continue
}
if nu.Status != ou.Status {
diffs[mid] = "vip_status"
continue
}
if !nu.OverdueTime.Time().Equal(ou.OverdueTime.Time()) {
diffs[mid] = "vip_overdue_time"
continue
}
if !nu.AnnualVipOverdueTime.Time().Equal(ou.AnnualVipOverdueTime.Time()) {
diffs[mid] = "annual_vip_overdue_time"
continue
}
if nu.PayType != ou.IsAutoRenew {
diffs[mid] = "vip_pay_type"
continue
}
if nu.PayChannelID != ou.PayChannelID {
diffs[mid] = "pay_channel_id"
continue
}
if !nu.IosOverdueTime.Time().Equal(ou.IosOverdueTime.Time()) {
diffs[mid] = "ios_overdue_time"
continue
}
}
// reset
ids = []int64{}
}
j++
}
log.Info("check index (%d) vip_user_info diff len (%d)", i, len(diffs))
log.Info("check index (%d) vip_user_info diff data mids(%v)", i, diffs)
time.Sleep(time.Millisecond * _defsleepmsec)
}
return
}
//CheckBcoinData check bcoin data
func (s *Service) CheckBcoinData(c context.Context) (mids []int64, err error) {
var (
maxID int
size = s.c.Property.BatchSize
)
if maxID, err = s.dao.SelMaxID(context.TODO()); err != nil {
err = errors.WithStack(err)
return
}
page := maxID / size
if maxID%size != 0 {
page++
}
for i := 0; i < page; i++ {
startID := size * i
endID := (i + 1) * size
var res []*model.VipUserInfo
if res, err = s.dao.SelUserInfos(context.TODO(), startID, endID); err != nil {
err = errors.WithStack(err)
return
}
var (
tempMids []int64
bcoinMap map[int64][]*model.VipBcoinSalary
oldBcoinMap map[int64][]*model.VipBcoinSalary
)
for _, v := range res {
tempMids = append(tempMids, v.Mid)
}
if bcoinMap, err = s.dao.SelBcoinSalaryDataMaps(context.TODO(), tempMids); err != nil {
err = errors.WithStack(err)
return
}
if oldBcoinMap, err = s.dao.SelOldBcoinSalaryDataMaps(context.TODO(), tempMids); err != nil {
err = errors.WithStack(err)
return
}
if len(bcoinMap) > len(oldBcoinMap) {
for key, val := range bcoinMap {
salaries := oldBcoinMap[key]
if len(salaries) != len(val) {
mids = append(mids, key)
}
}
} else {
for key, val := range oldBcoinMap {
salaries := bcoinMap[key]
if len(salaries) != len(val) {
mids = append(mids, key)
}
}
}
}
log.Info("cur not sync data mid is(%+v)", mids)
return
}
//CheckChangeHistory check change history data
func (s *Service) CheckChangeHistory(c context.Context) (mids []int64, err error) {
var (
maxID int
size = 2000
)
if maxID, err = s.dao.SelMaxID(context.TODO()); err != nil {
err = errors.WithStack(err)
return
}
page := maxID / size
if maxID%size != 0 {
page++
}
for i := 0; i < page; i++ {
startID := size * i
endID := (i + 1) * size
var res []*model.VipUserInfo
if res, err = s.dao.SelUserInfos(context.TODO(), startID, endID); err != nil {
err = errors.WithStack(err)
return
}
var (
tempMids []int64
historyMap map[int64][]*model.VipChangeHistory
oldHistoryMap map[int64][]*model.VipChangeHistory
)
for _, v := range res {
tempMids = append(tempMids, v.Mid)
}
if historyMap, err = s.dao.SelChangeHistoryMaps(context.TODO(), tempMids); err != nil {
err = errors.WithStack(err)
return
}
if oldHistoryMap, err = s.dao.SelOldChangeHistoryMaps(context.TODO(), tempMids); err != nil {
err = errors.WithStack(err)
return
}
if len(historyMap) > len(oldHistoryMap) {
for key, val := range historyMap {
histories := oldHistoryMap[key]
if len(histories) != len(val) {
mids = append(mids, key)
}
}
} else {
for key, val := range oldHistoryMap {
histories := historyMap[key]
if len(histories) != len(val) {
mids = append(mids, key)
}
}
}
}
log.Info("cur not sync data mid is(%+v)", mids)
return
}

View File

@@ -0,0 +1,66 @@
package service
import (
"context"
xlog "log"
"time"
"go-common/app/job/main/vip/model"
comol "go-common/app/service/main/coupon/model"
"go-common/library/log"
)
func (s *Service) couponnotifyproc() {
defer func() {
if x := recover(); x != nil {
log.Error("couponnotifyproc panic(%v)", x)
go s.couponnotifyproc()
log.Info("couponnotifyproc recover")
}
}()
for {
f := <-s.notifycouponchan
time.AfterFunc(2*time.Second, f)
}
}
func (s *Service) couponnotify(f func()) {
defer func() {
if x := recover(); x != nil {
log.Error("couponnotifyproc panic(%v)", x)
}
}()
select {
case s.notifycouponchan <- f:
default:
xlog.Panic("s.couponnotifyproc chan full!")
}
}
// CouponNotify coupon notify.
func (s *Service) CouponNotify(c context.Context, o *model.VipPayOrderNewMsg) (err error) {
var (
state int8
retrytimes = 3
)
if o == nil {
return
}
if o.Status == model.SUCCESS {
state = comol.AllowanceUseSuccess
} else {
state = comol.AllowanceUseFaild
}
for i := 0; i < retrytimes; i++ {
if err = s.couponRPC.CouponNotify(c, &comol.ArgNotify{
Mid: o.Mid,
OrderNo: o.OrderNo,
State: state,
}); err != nil {
log.Error("rpc.CouponNotify(%+v) error(%+v)", o, err)
continue
}
break
}
return
}

View File

@@ -0,0 +1,22 @@
package service
import (
"testing"
"go-common/app/job/main/vip/model"
. "github.com/smartystreets/goconvey/convey"
)
//go test -test.v -test.run TestCouponNotify
func TestCouponNotify(t *testing.T) {
Convey("TestCouponNotify ", t, func() {
o := &model.VipPayOrderNewMsg{
OrderNo: "1807211806450011799",
Status: model.SUCCESS,
Mid: 1,
}
err := s.CouponNotify(c, o)
So(err, ShouldBeNil)
})
}

View File

@@ -0,0 +1,131 @@
package service
import (
"context"
"encoding/json"
"time"
"go-common/app/job/main/vip/model"
"go-common/library/log"
"go-common/library/queue/databus"
"github.com/pkg/errors"
)
func (s *Service) accloginproc() {
defer func() {
if r := recover(); r != nil {
r = errors.WithStack(r.(error))
log.Error("Runtime error caught: %+v", r)
go s.accloginproc()
}
}()
var (
err error
msgChan = s.accLogin.Messages()
msg *databus.Message
ok bool
)
for {
msg, ok = <-msgChan
log.Info("login ip msg %+v", string(msg.Value))
if !ok {
log.Info("accLogin msgChan closed")
}
if err = msg.Commit(); err != nil {
log.Error("msg.Commit err(%+v)", err)
}
m := &model.LoginLog{}
if err = json.Unmarshal([]byte(msg.Value), m); err != nil {
log.Error("json.Unmarshal(%v) err(%+v)", m, err)
continue
}
s.Frozen(context.TODO(), m)
}
}
// Frozen handle vip frozen logic.
func (s *Service) Frozen(c context.Context, ll *model.LoginLog) (err error) {
var (
lc int64
uvs *model.VipUserInfo
ctx = context.TODO()
)
// 判定用户是否为vip
if uvs, err = s.dao.VipStatus(ctx, ll.Mid); err != nil {
log.Error("s.dao.VipStatus%derr(%+v)", ll.Mid, err)
return
}
if uvs == nil || uvs.Status == model.VipStatusOverTime {
log.Warn("user(%d) not vip.(%+v)", ll.Mid, uvs)
return
}
// 判定是否为15分钟4次以上不同ip登录
if err = s.dao.AddLogginIP(ctx, ll.Mid, ll.IP); err != nil {
log.Error("s.dao.AddLogginIP(%derr(%+v)", ll.Mid, err)
return
}
if lc, err = s.dao.LoginCount(ctx, ll.Mid); err != nil {
log.Error("s.dao.LoginCount(%derr(%+v)", ll.Mid, err)
return
}
if lc >= s.c.Property.FrozenLimit {
if err = s.dao.Enqueue(ctx, ll.Mid, time.Now().Add(s.frozenDate).Unix()); err != nil {
log.Error("enqueue error(%+v)", err)
}
if err = s.dao.SetVipFrozen(ctx, ll.Mid); err != nil {
log.Error("set vip frozen err(%+v)", err)
}
//通知业务方清理缓存
s.cleanCache(ll.Mid)
if err = s.notifyOldVip(ll.Mid, -1); err != nil {
log.Error("del vip java frozen err(%+v)", err)
}
log.Info("mid(%+v) frozen success", ll.Mid)
}
return
}
// unFrozenJob timing to unFrozen vip user
func (s *Service) unFrozenJob() {
log.Info("unfrozen job start........................................")
defer func() {
if r := recover(); r != nil {
r = errors.WithStack(r.(error))
log.Error("recover panic error(%+v)", r)
}
log.Info("unfrozen job end.............................")
}()
var (
err error
mids []int64
ctx = context.TODO()
)
if mids, err = s.dao.Dequeue(ctx); err != nil {
log.Error("s.dao.Dequeue err(%+v)", err)
return
}
for _, mid := range mids {
if err = s.dao.RemQueue(ctx, mid); err != nil {
log.Error("s.dao.RemQueue(%derr(%+v)", mid, err)
continue
}
if err = s.dao.DelCache(ctx, mid); err != nil {
log.Error("del cache mid(%+v) err(%+v)", mid, err)
}
if err = s.dao.DelVipFrozen(ctx, mid); err != nil {
log.Error("del vip frozen err(%+v)", err)
}
if err = s.notifyOldVip(mid, 0); err != nil {
log.Error("del vip java frozen err(%+v)", err)
}
s.cleanCache(mid)
log.Info("mid(%+v) unfrozen success", mid)
}
}
// FIXME AFTER REMOVE JAVA.
func (s *Service) notifyOldVip(mid, status int64) error {
return s.dao.OldFrozenChange(mid, status)
}

View File

@@ -0,0 +1,31 @@
package service
import (
"context"
"testing"
"go-common/app/job/main/vip/model"
. "github.com/smartystreets/goconvey/convey"
)
var (
ctx = context.TODO()
)
func Test_Frozen(t *testing.T) {
Convey("test user frozen", t, func() {
la := []uint32{1, 2, 3, 4, 5}
for _, i := range la {
s.dao.AddLogginIP(ctx, 7593666, i)
}
s.Frozen(ctx, &model.LoginLog{Mid: 7593666, IP: 234566})
})
}
func Test_UnFrozen(t *testing.T) {
Test_Frozen(t)
Convey("test user unfrozen", t, func() {
s.unFrozenJob()
})
}

View File

@@ -0,0 +1,151 @@
package service
import (
"context"
"fmt"
"strings"
xtime "time"
"go-common/app/job/main/vip/model"
"go-common/library/log"
"go-common/library/time"
"github.com/pkg/errors"
)
const (
maxSendMsgCount = 200
willExpiredDay = 7
hadExpiredDays = 14
willExpiredMsg = "您还有%d天大会员即将到期请尽快续期享受更多特权"
willExpiredTitle = "大会员即将到期提醒"
hadExpiredMsg = "很抱歉的通知您,您的大会员已过期,请尽快续期享受更多特权!"
hadExpiredTitle = "大会员过期提醒"
vipWillExpiredMsgCode = "10_1_2"
vipHadExpiredMsgCode = "10_1_3"
systemNotify = 4
)
func (s *Service) hadExpiredMsgJob() {
defer func() {
if x := recover(); x != nil {
log.Error("service.hadExpiredMsgJob panic(%v)", x)
go s.hadExpiredMsgJob()
log.Info("service.hadExpiredMsgJob recover")
}
}()
log.Info("start send had expire msg job ...................")
var (
err error
mids []int64
)
now := xtime.Now()
startTime := now.AddDate(0, 0, -hadExpiredDays-1)
endTime := now.AddDate(0, 0, -hadExpiredDays)
if mids, err = s.willExpireUser(time.Time(startTime.Unix()), time.Time(endTime.Unix()), model.VipStatusOverTime); err != nil {
log.Error("will expire user(startTime:%v endTime:%v status:%v) error(%+v)", startTime, endTime, model.VipStatusOverTime, err)
return
}
log.Info("send startTime(%v) endDate(%v) mids(%v)", startTime.Format("2006-01-02"), endTime.Format("2006-01-02"), mids)
if err = s.batchSendMsg(mids, hadExpiredMsg, hadExpiredTitle, vipHadExpiredMsgCode, systemNotify); err != nil {
log.Error("batch send msg error(%+v)", err)
return
}
log.Info("end send had expire msg job..........................")
}
func (s *Service) willExpiredMsgJob() {
defer func() {
if x := recover(); x != nil {
log.Error("service.hadExpiredMsgJob panic(%v)", x)
go s.willExpiredMsgJob()
log.Info("service.hadExpiredMsgJob recover")
}
}()
log.Info("start send will expire msg job............................")
var (
err error
mids []int64
)
now := xtime.Now()
startTime := now.AddDate(0, 0, willExpiredDay)
endTime := now.AddDate(0, 0, willExpiredDay+1)
if mids, err = s.willExpireUser(time.Time(startTime.Unix()), time.Time(endTime.Unix()), model.VipStatusNotOverTime); err != nil {
log.Error("will expire user(startTime:%v endTime:%v status:%v) error(%+v)", startTime, endTime, model.VipStatusNotOverTime, err)
return
}
log.Info("send startTime(%v) endDate(%v) mids(%v)", startTime.Format("2006-01-02"), endTime.Format("2006-01-02"), mids)
if err = s.batchSendMsg(mids, fmt.Sprintf(willExpiredMsg, willExpiredDay), willExpiredTitle, vipWillExpiredMsgCode, systemNotify); err != nil {
log.Error("batch send msg error(%+v)", err)
return
}
log.Info("end send will expire msg job............................")
}
func (s *Service) willExpireUser(startTime time.Time, endTime time.Time, status int) (mids []int64, err error) {
var (
maxID int
size = 10000
)
if maxID, err = s.dao.SelMaxID(context.TODO()); err != nil {
err = errors.WithStack(err)
return
}
page := maxID / size
if maxID%size != 0 {
page++
}
for i := 0; i < page; i++ {
startID := i * size
endID := (i + 1) * size
var tempMid []int64
if tempMid, err = s.dao.SelVipUserInfos(context.TODO(), startID, endID, startTime, endTime, status); err != nil {
err = errors.WithStack(err)
return
}
mids = append(mids, tempMid...)
}
return
}
func (s *Service) batchSendMsg(mids []int64, content string, title string, ms string, dataType int) (err error) {
if len(mids) <= maxSendMsgCount && len(mids) >= 1 {
var midsStr = ""
for _, v := range mids {
midsStr += fmt.Sprintf(",%v", v)
}
if err = s.dao.SendMultipMsg(context.TODO(), midsStr, content, title, ms, dataType); err != nil {
err = errors.WithStack(err)
return
}
} else if len(mids) > maxSendMsgCount {
page := len(mids) / maxSendMsgCount
if len(mids)%maxSendMsgCount != 0 {
page++
}
for i := 0; i < page; i++ {
start := i * maxSendMsgCount
end := (i + 1) * maxSendMsgCount
if len(mids) < end {
end = len(mids)
}
tempMids := mids[start:end]
var midsStr []string
for _, v := range tempMids {
midsStr = append(midsStr, fmt.Sprintf("%v", v))
}
if err = s.dao.SendMultipMsg(context.TODO(), strings.Join(midsStr, ","), content, title, ms, dataType); err != nil {
err = errors.WithStack(err)
continue
}
}
}
return
}

View File

@@ -0,0 +1,471 @@
package service
import (
"context"
"fmt"
"time"
"go-common/app/job/main/vip/model"
"go-common/library/log"
xtime "go-common/library/time"
"github.com/pkg/errors"
)
const (
_autoRenewFailTwoMsg = "连续包月大会员今天凌晨续费又失败了,服务已暂停。如果想要再次享受连续包月大会员服务,请先取消连续包月,再去开通哦~"
_autoRenewFailOneMsg = "连续包月大会员今天凌晨续费失败了,%s 0点将会再次重试。"
_deadlineAutoRenewMsg = "您的连续包月服务将在%s 0点续费。"
_autoRenewFailTitle = "连续包月服务续费失败"
_deadlineAutoRenewTitle = "连续包月服务即将续费"
_sleep = 20 * time.Millisecond
_maxtime = 20
)
func (s *Service) handlerinsertorderproc() {
var (
err error
)
defer func() {
if x := recover(); x != nil {
log.Error("service.handlerinsertorderproc panic(%v)", x)
go s.handlerinsertorderproc()
log.Info("service.handlerinsertorderproc recover")
}
}()
for {
order := <-s.handlerInsertOrder
for i := 0; i < s.c.Property.Retry; i++ {
if err = s.insertOrder(order); err == nil {
break
}
log.Error("error(%+v)", err)
}
}
}
func (s *Service) insertOrder(r *model.VipPayOrder) (err error) {
var aff int64
if aff, err = s.dao.AddPayOrder(context.TODO(), r); err != nil {
err = errors.WithStack(err)
return
}
if aff != 1 {
return
}
log.Info("vip_pay_order sysn data(%+v)", r)
rlog := new(model.VipPayOrderLog)
rlog.Mid = r.Mid
rlog.OrderNo = r.OrderNo
rlog.Status = r.Status
if _, err = s.dao.AddPayOrderLog(context.TODO(), rlog); err != nil {
log.Error("add pay order log(%+v) error(%+v)", rlog, err)
err = nil
}
return
}
func (s *Service) handlerupdateorderproc() {
var (
err error
flag bool
)
defer func() {
if x := recover(); x != nil {
log.Error("service.handlerupdateorderproc panic(%v)", x)
go s.handlerupdateorderproc()
log.Info("service.handlerupdateorderproc recover")
}
}()
for {
order := <-s.handlerUpdateOrder
flag = true
for i := 0; i < s.c.Property.Retry; i++ {
if err = s.updatePayOrder(context.TODO(), order); err == nil {
flag = false
break
}
log.Error("error(%+v)", err)
}
if flag {
s.handlerFailPayOrder <- order
}
}
}
func (s *Service) handlerfailpayorderproc() {
var (
err error
)
defer func() {
if x := recover(); x != nil {
log.Error("service.handlerfailpayorderproc panic(%v)", x)
go s.handlerfailpayorderproc()
log.Info("service.handlerfailpayorderproc recover")
}
}()
for {
order := <-s.handlerFailPayOrder
_time := 0
for {
if err = s.updatePayOrder(context.TODO(), order); err == nil {
break
}
log.Error("pay order error(%+v)", err)
_time++
if _time > _maxtime {
break
}
}
}
}
func (s *Service) handlerfailrechargeorderproc() {
var (
eff int64
err error
)
defer func() {
if x := recover(); x != nil {
log.Error("service.handlerfailrechargeorderproc panic(%v)", x)
go s.handlerfailrechargeorderproc()
log.Info("service.handlerfailrechargeorderproc recover")
}
}()
for {
order := <-s.handlerFailRechargeOrder
_time := 0
for {
if eff, err = s.dao.UpdateRechargeOrder(context.TODO(), order); err != nil {
log.Error("error(%+v)", err)
break
}
if eff > 0 {
break
}
_time++
if _time > _maxtime {
break
}
time.Sleep(_sleep)
}
}
}
func (s *Service) handlerupdaterechargeorderproc() {
var (
eff int64
err error
flag bool
)
defer func() {
if x := recover(); x != nil {
log.Error("service.handlerupdaterechargeorderproc panic(%v)", x)
go s.handlerupdaterechargeorderproc()
log.Info("service.handlerupdaterechargeorderproc recover")
}
}()
for {
order := <-s.handlerRechargeOrder
flag = true
for i := 0; i < s.c.Property.Retry; i++ {
if eff, err = s.dao.UpdateRechargeOrder(context.TODO(), order); err != nil {
log.Error("error(%+v)", err)
continue
}
if eff > 0 {
log.Info("update recharge order(%+v)", order)
flag = false
break
}
time.Sleep(_sleep)
}
if flag {
s.handlerFailRechargeOrder <- order
}
}
}
func (s *Service) updatePayOrder(c context.Context, r *model.VipPayOrder) (err error) {
var eff int64
if eff, err = s.dao.UpdatePayOrderStatus(c, r); err != nil {
err = errors.WithStack(err)
return
}
if eff <= 0 {
err = fmt.Errorf("order更新未执行(%+v)", r)
time.Sleep(_sleep)
return
}
log.Info("cur pay order update order(%+v)", r)
rlogKey := fmt.Sprintf("%v:%v", r.OrderNo, r.Status)
if succeed := s.dao.AddTransferLock(c, rlogKey); succeed {
rlog := new(model.VipPayOrderLog)
rlog.Mid = r.Mid
rlog.OrderNo = r.OrderNo
rlog.Status = r.Status
if _, err = s.dao.AddPayOrderLog(context.TODO(), rlog); err != nil {
log.Error("add pay order log(%+v) error(%+v)", rlog, err)
err = nil
}
return
}
return
}
func (s *Service) convertPayOrder(r *model.VipPayOrderOldMsg) (res *model.VipPayOrder) {
res = new(model.VipPayOrder)
res.Mid = r.Mid
res.AppID = r.AppID
res.AppSubID = r.AppSubID
res.BuyMonths = r.BuyMonths
res.Money = r.Money
res.RechargeBp = r.RechargeBp
res.OrderNo = r.OrderNo
res.OrderType = r.OrderType
res.PayType = r.PayType
res.Platform = r.Platform
res.Status = r.Status
res.ToMid = r.Bmid
res.Ver = r.Ver
res.CouponMoney = r.CouponMoney
if paymentTime, err := time.ParseInLocation("2006-01-02 15:04:05", r.PaymentTime, time.Local); err == nil {
res.PaymentTime = xtime.Time(paymentTime.Unix())
}
res.Ctime = xtime.Time(parseTime(r.Ctime).Unix())
res.Mtime = xtime.Time(parseTime(r.Mtime).Unix())
return
}
func (s *Service) convertPayOrderByMsg(r *model.VipRechargeOrderMsg) (res *model.VipPayOrder) {
res = new(model.VipPayOrder)
res.Mid = r.PayMid
res.OrderNo = r.PayOrderNo
res.ThirdTradeNo = r.ThirdTradeNo
res.RechargeBp = r.RechargeBp
return
}
func convertPayOrderOldToNew(r *model.VipPayOrderOld) (res *model.VipPayOrder) {
res = new(model.VipPayOrder)
res.Mid = r.Mid
res.AppID = r.AppID
res.AppSubID = r.AppSubID
res.BuyMonths = r.BuyMonths
res.Money = r.Money
res.OrderNo = r.OrderNo
res.OrderType = r.OrderType
res.PayType = r.PayType
res.Platform = r.Platform
res.Status = r.Status
res.ToMid = r.Bmid
res.Ver = r.Ver
res.PaymentTime = r.PaymentTime
res.CouponMoney = r.CouponMoney
return
}
//HandlerPayOrder handler pay order
func (s *Service) HandlerPayOrder() (err error) {
var (
size = 1000
oldMaxID int
)
if oldMaxID, err = s.dao.SelOldOrderMaxID(context.TODO()); err != nil {
log.Error("selOldOrderMaxID error(%+v)", err)
return
}
page := oldMaxID / size
if oldMaxID%size != 0 {
page++
}
for i := 0; i < page; i++ {
startID := i * size
endID := (i + 1) * size
if endID > oldMaxID {
endID = oldMaxID
}
var (
res []*model.VipPayOrderOld
batchOrder []*model.VipPayOrder
orderNos []string
oldRechargeOrder []*model.VipRechargeOrder
)
rechargeMap := make(map[string]*model.VipRechargeOrder)
if res, err = s.dao.SelOldPayOrder(context.TODO(), startID, endID); err != nil {
log.Error("selOldPayOrder(startID:%v endID:%v) error(%+v)", startID, endID, err)
return
}
for _, v := range res {
batchOrder = append(batchOrder, convertPayOrderOldToNew(v))
}
for _, v := range batchOrder {
orderNos = append(orderNos, v.OrderNo)
}
if oldRechargeOrder, err = s.dao.SelOldRechargeOrder(context.TODO(), orderNos); err != nil {
return
}
for _, v := range oldRechargeOrder {
rechargeMap[v.PayOrderNo] = v
}
for _, v := range batchOrder {
rechargeOrder := rechargeMap[v.OrderNo]
if rechargeOrder != nil {
v.ThirdTradeNo = rechargeOrder.ThirdTradeNo
v.RechargeBp = rechargeOrder.RechargeBp
}
}
if err = s.dao.BatchAddPayOrder(context.TODO(), batchOrder); err != nil {
return
}
}
return
}
func (s *Service) willDedutionMsg() (err error) {
var (
size = 5000
endID int
now time.Time
vips []*model.VipUserInfo
)
if now, err = time.ParseInLocation("2006-01-02", time.Now().Format("2006-01-02"), time.Local); err != nil {
log.Error("time.ParseInLocation(%v) error(%+v)", time.Now(), err)
return
}
start := now.AddDate(0, 0, 1)
end := start.AddDate(0, 0, 3)
if endID, err = s.dao.SelMaxID(context.TODO()); err != nil {
return
}
page := endID / size
if endID%size != 0 {
page++
}
for i := 0; i < page; i++ {
startID := i * size
eID := (i + 1) * size
if vips, err = s.dao.SelVipUsers(context.TODO(), startID, eID, xtime.Time(start.Unix()), xtime.Time(end.Unix())); err != nil {
continue
}
for _, v := range vips {
if v.OverdueTime.Time().Equal(start) {
s.dao.SendMultipMsg(context.TODO(), fmt.Sprintf("%v", v.Mid),
_autoRenewFailTwoMsg,
_autoRenewFailTitle,
vipWillExpiredMsgCode,
systemNotify)
} else if start.AddDate(0, 0, 1).Equal(v.OverdueTime.Time()) {
s.dao.SendMultipMsg(context.TODO(), fmt.Sprintf("%v", v.Mid),
fmt.Sprint(_autoRenewFailOneMsg, v.OverdueTime.Time().AddDate(0, 0, -1).Format("2006-01-02")),
_autoRenewFailTitle,
vipWillExpiredMsgCode,
systemNotify)
} else if start.AddDate(0, 0, 2).Equal(v.OverdueTime.Time()) {
s.dao.SendMultipMsg(context.TODO(), fmt.Sprintf("%v", v.Mid),
fmt.Sprint(_deadlineAutoRenewMsg, start.Format("2006-01-02")),
_deadlineAutoRenewTitle,
vipWillExpiredMsgCode,
systemNotify)
}
}
}
return
}
func (s *Service) autoRenews() (err error) {
//var (
// size = 5000
// endID int
// now time.Time
// vips []*model.VipUserInfo
// price float64
//)
//if now, err = time.ParseInLocation("2006-01-02", time.Now().Format("2006-01-02"), time.Local); err != nil {
// log.Error("time.ParseInLocation(%v) error(%+v)", time.Now(), err)
// return
//}
//
//start := now.AddDate(0, 0, 1)
//
//end := start.AddDate(0, 0, 3)
//if price, err = s.vipRPC.Price(context.TODO(), 1, xmodel.DevicePC, xmodel.AutoRenew, 1); err != nil {
// err = errors.WithStack(err)
// return
//}
//if endID, err = s.dao.SelMaxID(context.TODO()); err != nil {
// return
//}
//
//page := endID / size
//if endID%size != 0 {
// page++
//}
//for i := 0; i < page; i++ {
// startID := i * size
// eID := (i + 1) * size
// if vips, err = s.dao.SelVipUsers(context.TODO(), startID, eID, xtime.Time(start.Unix()), xtime.Time(end.Unix())); err != nil {
// err = errors.WithStack(err)
// continue
// }
// for _, v := range vips {
// var params = make(map[string]interface{}, 0)
// if params, err = s.vipRPC.CreateOrderPlatfrom(context.TODO(), int64(v.Mid), 0, 0, 1, price, xmodel.DevicePC, 5, xmodel.AutoRenew, ""); err != nil {
// log.Error("CreateOrderPlatform error(%+v)", err)
// continue
// }
// params["payChannelId"] = v.PayChannelId
// params["payChannel"] = s.c.Property.PayMapping[strconv.Itoa(int(v.PayChannelId))]
// if err = s.dao.PayOrder(context.TODO(), params); err != nil {
// log.Error("handler fail orderId->%v mid:%v", params["orderId"], v.Mid)
// continue
// }
// log.Info("handler success orderId:%v mid:%v", params["orderId"], v.Mid)
// }
//}
return
}
// AutoRenewJob auto renew job.
//func (s *Service) autoRenewJob() {
// defer func() {
// if x := recover(); x != nil {
// log.Error("service.autoRenewJob panic(%v)", x)
// go s.autoRenewJob()
// log.Info("service.autoRenewJob recover")
// }
// }()
// log.Info("auto renew job start.................................")
// var err error
// if err = s.autoRenews(); err != nil {
// log.Error("autoRenews error(%+v)", err)
// }
// log.Info("auto renew job end...................................")
//}
// SendMessageJob send message job.
func (s *Service) sendMessageJob() {
defer func() {
if x := recover(); x != nil {
log.Error("service.sendMessageJob panic(%v)", x)
go s.sendMessageJob()
log.Info("service.sendMessageJob recover")
}
}()
log.Info("sendMessage job start .........................")
s.willDedutionMsg()
log.Info("sendMessage job end .........................")
}

View File

@@ -0,0 +1,20 @@
package service
import (
"testing"
. "github.com/smartystreets/goconvey/convey"
)
func Test_handlerPayOrder(t *testing.T) {
Convey("handler pay order", t, func() {
s.HandlerPayOrder()
})
}
func Test_autoRenews(t *testing.T) {
Convey("autorenews ", t, func() {
err := s.autoRenews()
So(err, ShouldBeNil)
})
}

View File

@@ -0,0 +1,115 @@
package service
import (
"context"
"time"
"go-common/app/job/main/vip/model"
"go-common/library/log"
"github.com/pkg/errors"
)
const (
_fail = 2
_handlering = 2
_finish = 3
_nomarl = 0
_statusnomarl = 1
)
func (s *Service) pushDataJob() {
log.Info("push data job start..................")
if succeed := s.dao.AddTransferLock(context.TODO(), "lock:pushDatajob"); succeed {
if err := s.pushData(context.TODO()); err != nil {
log.Error("error(%+v)", err)
}
}
log.Info("push data job end.....................")
}
func (s *Service) pushData(c context.Context) (err error) {
var (
res []*model.VipPushData
pushDataMap = make(map[int64]*model.VipPushData)
pushMidsMap = make(map[int64][]int64)
maxID int
size = s.c.Property.BatchSize
vips []*model.VipUserInfo
curDate time.Time
rel *model.VipPushResq
)
now := time.Now()
format := now.Format("2006-01-02")
if curDate, err = time.ParseInLocation("2006-01-02", format, time.Local); err != nil {
err = errors.WithStack(err)
return
}
if res, err = s.dao.PushDatas(c, format); err != nil {
err = errors.WithStack(err)
return
}
if len(res) == 0 {
log.Info("not need reduce push data.........")
return
}
for _, v := range res {
pushDataMap[v.ID] = v
}
if maxID, err = s.dao.SelMaxID(c); err != nil {
err = errors.WithStack(err)
return
}
page := maxID / size
if maxID%size != 0 {
page++
}
for i := 0; i < page; i++ {
startID := i * size
endID := (i + 1) * size
if vips, err = s.dao.SelUserInfos(context.TODO(), startID, endID); err != nil {
err = errors.WithStack(err)
return
}
for _, v := range vips {
for key, val := range pushDataMap {
startDate := curDate.AddDate(0, 0, int(val.ExpiredDayStart))
endDate := curDate.AddDate(0, 0, int(val.ExpiredDayEnd))
if !(v.OverdueTime.Time().Before(startDate) || v.OverdueTime.Time().After(endDate)) && v.PayType == model.Normal && val.DisableType == _nomarl && val.Status != _fail {
mids := pushMidsMap[key]
mids = append(mids, v.Mid)
pushMidsMap[key] = mids
}
}
}
}
for key, val := range pushMidsMap {
data := pushDataMap[key]
var status int8
progressStatus := data.ProgressStatus
pushedCount := data.PushedCount
if rel, err = s.dao.PushData(context.TODO(), val, data, format); err != nil {
log.Error("push data error(%+v)", err)
continue
}
if rel.Code != 0 {
status = _fail
} else {
pushedCount++
if pushedCount == data.PushTotalCount {
progressStatus = _finish
} else {
progressStatus = _handlering
}
status = _statusnomarl
}
if err = s.dao.UpdatePushData(context.TODO(), status, progressStatus, pushedCount, rel.Code, rel.Data, data.ID); err != nil {
err = errors.WithStack(err)
return
}
}
return
}

View File

@@ -0,0 +1,349 @@
package service
import (
"context"
"fmt"
"time"
"go-common/app/job/main/vip/model"
"go-common/library/log"
"github.com/pkg/errors"
)
//ScanSalaryVideoCoupon scan all vip user to salary video coupon.
func (s *Service) ScanSalaryVideoCoupon(c context.Context) (err error) {
var (
userInfos []*model.VipInfoDB
size = 100
endID int
now = time.Now()
dv = now.Format("2006_01")
y = now.Year()
m = now.Month()
salaryDate = time.Date(y, m, s.c.Property.SalaryDay, 0, 0, 0, 0, time.Local)
)
for {
if endID, err = s.dao.SelUserInfoMaxID(context.TODO()); err != nil {
log.Error("s.dao.SelMaxID error(%v)", err)
time.Sleep(time.Minute * 2)
continue
}
break
}
page := endID / size
if endID%size != 0 {
page++
}
for i := 0; i < page; {
log.Info("salary page(%d) total(%d) ....................................", i, page)
startID := i * size
eID := (i + 1) * size
if userInfos, err = s.dao.SelEffectiveScopeVipList(context.TODO(), startID, eID); err != nil {
log.Error("s.dao.SelEffectiveScopeVipList error(%v)", err)
time.Sleep(time.Second * 5)
continue
}
i++
for _, v := range userInfos {
time.Sleep(time.Duration(s.c.Property.SalaryVideoCouponnIterval))
var (
vipType = model.NotVip
)
if v.Status != model.VipStatusNotOverTime && v.Status != model.VipStatusFrozen {
continue
}
if salaryDate.Before(v.OverdueTime.Time()) {
vipType = model.Vip
if salaryDate.Before(v.AnnualVipOverdueTime.Time()) {
vipType = model.AnnualVip
}
}
if vipType == model.NotVip {
continue
}
day := v.OverdueTime.Time().Sub(v.RecentTime.Time()).Hours() / model.DayOfHour
if day < model.VipDaysMonth {
continue
}
if err = s.salaryCoupon(c, v.Mid, model.TimingSalaryType, int8(vipType), dv, model.CouponSalaryTiming); err != nil {
err = errors.Wrapf(err, "salaryCoupon mid(%d)(%v)", v.Mid, v)
log.Error("%+v", err)
continue
}
log.Info("salary suc mid(%d) ....................................", v.Mid)
}
}
return
}
// salaryCoupon salary coupon.
func (s *Service) salaryCoupon(c context.Context, mid int64, salaryType int8, vipType int8, dv string, atonce int8) (err error) {
var (
logs []*model.VideoCouponSalaryLog
hs = map[int8]int64{} // key:coupontype value:salarycount
ms map[string]int64 // key:viptype value:salarycount
)
if logs, err = s.dao.SalaryVideoCouponList(c, mid, dv); err != nil {
err = errors.WithStack(err)
return
}
for _, v := range logs {
hs[v.CouponType] = hs[v.CouponType] + v.CouponCount
}
for _, v := range s.c.Property.SalaryCouponTypes {
ms = s.c.Property.SalaryCouponMaps[fmt.Sprintf("%d", v)]
if len(ms) != 0 {
if salaryType == model.VipSupplyType {
if hs[v] == 0 {
hs[v] = ms[fmt.Sprintf("%d", model.AnnualVip)] - ms[fmt.Sprintf("%d", model.Vip)]
} else {
hs[v] = ms[fmt.Sprintf("%d", model.AnnualVip)] - hs[v]
}
} else {
hs[v] = ms[fmt.Sprintf("%d", vipType)] - hs[v]
}
}
}
for k, count := range hs {
var (
token string
tokenfmt string
)
if count <= 0 {
continue
}
tokenfmt = s.c.Property.SalaryCouponBatchNoMaps[fmt.Sprintf("%d", k)]
if len(tokenfmt) == 0 {
continue
}
token = fmt.Sprintf(tokenfmt, atonce, dv)
if err = s.dao.SalaryCoupon(c, mid, k, count, token); err != nil {
err = errors.Wrapf(err, "s.dao.SalaryCoupon(%d)", mid)
return
}
l := &model.VideoCouponSalaryLog{
Mid: mid,
CouponCount: count,
State: model.HadSalaryState,
Type: salaryType,
CouponType: k,
}
if err = s.dao.AddSalaryLog(c, l, dv); err != nil {
err = errors.WithStack(err)
return
}
if s.c.Property.MsgOpen {
var (
title string
content string
)
title = s.c.Property.SalaryCouponMsgTitleMaps[fmt.Sprintf("%d", k)]
if len(title) == 0 {
continue
}
if salaryType == model.VipSupplyType {
content = s.c.Property.SalaryCouponMsgSupplyContentMaps[fmt.Sprintf("%d", k)]
if len(content) == 0 {
continue
}
content = fmt.Sprintf(content, count)
} else {
content = s.c.Property.SalaryCouponMsgContentMaps[fmt.Sprintf("%d", k)]
if len(content) == 0 {
continue
}
}
s.sendmessage(func() {
s.dao.SendMultipMsg(context.TODO(), fmt.Sprintf("%d", mid), content,
title, model.MsgCouponSalaryMc, model.MsgSystemNotify)
})
}
}
return
}
// SalaryVideoCouponAtOnce salary video coupon at once.
func (s *Service) SalaryVideoCouponAtOnce(c context.Context, nvip *model.VipUserInfoMsg, ovip *model.VipUserInfoMsg, act string) (res int, err error) {
if act == _insertAction {
if err = s.salaryInsertAct(c, nvip); err != nil {
err = errors.Wrapf(err, "salaryInsertAct (%v)", nvip)
return
}
} else if act == _updateAction {
if err = s.salaryUpdateAct(c, nvip, ovip); err != nil {
err = errors.Wrapf(err, "salaryInsertAct (%v)", nvip)
return
}
}
return
}
func (s *Service) salaryInsertAct(c context.Context, nvip *model.VipUserInfoMsg) (err error) {
var (
now = time.Now()
otime time.Time
aotime time.Time
vipType = model.NotVip
zeroTime = now.AddDate(-10, 0, 0)
salaryType int8
dv = now.Format("2006_01")
)
otime, err = time.ParseInLocation(model.TimeFormatSec, nvip.OverdueTime, time.Local)
if err != nil {
log.Error("time.ParseInLocation error(%v)", errors.Wrapf(err, "time(%s)", nvip.OverdueTime))
otime = zeroTime
err = nil
}
aotime, err = time.ParseInLocation(model.TimeFormatSec, nvip.AnnualVipOverdueTime, time.Local)
if err != nil {
aotime = zeroTime
err = nil
}
if nvip.Status != model.VipStatusNotOverTime && nvip.Status != model.VipStatusFrozen {
return
}
days := otime.Sub(now).Hours() / model.DayOfHour
if days < model.VipDaysMonth {
log.Info("cur user not enough send coupon (%+v)", nvip)
return
}
if now.Before(otime) {
vipType = model.Vip
if now.Before(aotime) {
vipType = model.AnnualVip
}
}
switch vipType {
case model.Vip:
salaryType = model.NormalVipSalaryType
case model.AnnualVip:
salaryType = model.AnnualVipSalaryType
default:
return
}
if err = s.salaryCoupon(c, int64(nvip.Mid), salaryType, int8(vipType), dv, model.CouponSalaryAtonce); err != nil {
err = errors.Wrapf(err, "salaryCoupon mid(%d)(%v)", nvip.Mid, nvip)
}
return
}
func (s *Service) salaryUpdateAct(c context.Context, nvip *model.VipUserInfoMsg, ovip *model.VipUserInfoMsg) (err error) {
var (
ovType int
nvType int
expire bool
now = time.Now()
zeroTime = now.AddDate(-10, 0, 0)
ntime time.Time
oatime time.Time
natime time.Time
salaryType int8
dv = now.Format("2006_01")
)
ntime, err = time.ParseInLocation(model.TimeFormatSec, nvip.OverdueTime, time.Local)
if err != nil {
log.Error("time.ParseInLocation error(%v)", errors.Wrapf(err, "time(%s)", nvip.OverdueTime))
ntime = zeroTime
err = nil
}
natime, err = time.ParseInLocation(model.TimeFormatSec, nvip.AnnualVipOverdueTime, time.Local)
if err != nil {
natime = zeroTime
err = nil
}
// check OverdueTime time.
if ntime.Before(now) {
return
}
nvType = model.Vip
// check AnnualVipOverdueTime time.
if now.Before(natime) {
nvType = model.AnnualVip
}
// check old vip info expire.
expire, _ = s.judgeVipExpire(c, ovip)
if expire {
//check open days is enough 31
days := ntime.Sub(now).Hours() / model.DayOfHour
if days < model.VipDaysMonth {
log.Info("cur user not enough send coupon (%+v)", nvip)
return
}
if nvType == model.Vip {
// expire vip -> vip
salaryType = model.NormalVipSalaryType
} else if nvType == model.AnnualVip {
// expire vip -> annual vip
salaryType = model.AnnualVipSalaryType
}
} else {
if ovip.Type == model.Vip {
ovType = model.Vip
}
oatime, err = time.ParseInLocation(model.TimeFormatSec, ovip.AnnualVipOverdueTime, time.Local)
if err != nil {
oatime = zeroTime
err = nil
}
if ovip.Type == model.AnnualVip && oatime.After(now) {
ovType = model.AnnualVip
}
if ovType == model.Vip && nvType == model.AnnualVip {
// normal vip -> annual vip
salaryType = model.VipSupplyType
}
// short vip -> normal vip
recentTime := parseTime(ovip.RecentTime)
otime := parseTime(ovip.OverdueTime)
days := otime.Sub(recentTime).Hours() / model.DayOfHour
if days < model.VipDaysMonth {
//check open days is enough 31
days := ntime.Sub(now).Hours() / model.DayOfHour
if days < model.VipDaysMonth {
log.Info("cur user not enough send coupon (%+v)", nvip)
return
}
if nvType == model.Vip {
// expire vip -> vip
salaryType = model.NormalVipSalaryType
} else if nvType == model.AnnualVip {
// expire vip -> annual vip
salaryType = model.AnnualVipSalaryType
}
}
}
switch salaryType {
case model.NormalVipSalaryType, model.AnnualVipSalaryType, model.VipSupplyType:
if err = s.salaryCoupon(c, int64(nvip.Mid), salaryType, int8(nvType), dv, model.CouponSalaryAtonce); err != nil {
err = errors.Wrapf(err, "salaryCoupon mid(%d)(%v)", int64(nvip.Mid), nvip)
}
}
return
}
// judgeVipExpire judge vip is expire.
func (s *Service) judgeVipExpire(c context.Context, v *model.VipUserInfoMsg) (expire bool, err error) {
var (
now = time.Now()
overdueTime time.Time
zeroTime = now.AddDate(-10, 0, 0)
)
if v.Status != model.VipStatusNotOverTime && v.Status != model.VipStatusFrozen {
expire = true
return
}
overdueTime, err = time.ParseInLocation(model.TimeFormatSec, v.OverdueTime, time.Local)
if err != nil {
log.Error("time.ParseInLocation error(%v)", errors.Wrapf(err, "time(%s)", v.OverdueTime))
overdueTime = zeroTime
err = nil
}
if overdueTime.Before(now) {
expire = true
return
}
return
}

View File

@@ -0,0 +1,53 @@
package service
import (
"context"
"time"
"go-common/app/job/main/vip/model"
"go-common/library/log"
"github.com/pkg/errors"
)
//ScanSalaryLog scan salary log.
func (s *Service) ScanSalaryLog(c context.Context) (err error) {
var (
dv = time.Now().Format("2006_01")
olds []*model.OldSalaryLog
size = 1000
endID = 0
)
if endID, err = s.dao.SalaryLogMaxID(context.TODO(), dv); err != nil {
err = errors.WithStack(err)
return
}
page := endID / size
if endID%size != 0 {
page++
}
for i := 0; i < page; {
startID := i * size
eID := (i + 1) * size
if olds, err = s.dao.SelOldSalaryList(context.TODO(), startID, eID, dv); err != nil {
err = errors.WithStack(err)
return
}
i++
for _, v := range olds {
l := &model.VideoCouponSalaryLog{
Mid: v.Mid,
CouponCount: v.CouponCount,
State: v.State,
Type: v.Type,
CouponType: model.SalaryCouponType,
}
if err = s.dao.AddSalaryLog(context.TODO(), l, dv); err != nil {
err = errors.WithStack(err)
log.Error("+%v", err)
continue
}
}
}
return
}

View File

@@ -0,0 +1,99 @@
package service
import (
"testing"
"time"
"go-common/app/job/main/vip/model"
. "github.com/smartystreets/goconvey/convey"
)
// go test -test.v -test.run TestServiceSalaryCoupon
func TestServiceSalaryCoupon(t *testing.T) {
Convey("TestServiceSalaryCoupon", t, func() {
var (
err error
mid int64 = 999
st int8 = model.TimingSalaryType
vt int8 = model.AnnualVip
dv = time.Now().Format("2006_01")
atonce = model.CouponSalaryTiming
)
err = s.salaryCoupon(c, mid, st, vt, dv, atonce)
So(err, ShouldBeNil)
})
}
// go test -test.v -test.run TestServiceSalaryInsertAct
func TestServiceSalaryInsertAct(t *testing.T) {
Convey("TestServiceSalaryCoupon", t, func() {
var (
err error
nvip = &model.VipUserInfoMsg{
Mid: 9995,
Status: 1,
OverdueTime: "2018-06-11 18:27:12",
AnnualVipOverdueTime: "2018-06-09 18:27:12",
}
)
err = s.salaryInsertAct(c, nvip)
So(err, ShouldBeNil)
nvip.Mid = 88881
nvip.OverdueTime = "2018-07-31 18:27:12"
nvip.AnnualVipOverdueTime = "2018-07-31 18:27:12"
err = s.salaryInsertAct(c, nvip)
So(err, ShouldBeNil)
})
}
// go test -test.v -test.run TestServiceSalaryUpdateAct
func TestServiceSalaryUpdateAct(t *testing.T) {
Convey("TestServiceSalaryUpdateAct", t, func() {
var (
err error
nvip = &model.VipUserInfoMsg{
Mid: 65,
Status: 2,
OverdueTime: "2019-06-11 18:27:12",
AnnualVipOverdueTime: "2019-06-11 18:27:12",
}
ovip = &model.VipUserInfoMsg{
Mid: 65,
Status: 2,
OverdueTime: "2018-06-16 18:27:12",
AnnualVipOverdueTime: "2018-06-09 18:27:12",
Type: 1,
}
)
// vip -> a vip
err = s.salaryUpdateAct(c, nvip, ovip)
So(err, ShouldBeNil)
// not vip -> vip
ovip.OverdueTime = "2017-06-11 18:27:12"
nvip.OverdueTime = "2018-07-31 18:27:12"
nvip.AnnualVipOverdueTime = "2018-07-31 18:27:12"
ovip.Mid = 66
nvip.Mid = 66
err = s.salaryUpdateAct(c, nvip, ovip)
So(err, ShouldBeNil)
// vip - > a vip
ovip.OverdueTime = "2018-08-19 18:27:12"
nvip.AnnualVipOverdueTime = "2019-06-11 18:27:12"
ovip.Mid = 66
nvip.Mid = 66
err = s.salaryUpdateAct(c, nvip, ovip)
So(err, ShouldBeNil)
nvip.Mid = 67
err = s.salaryInsertAct(c, nvip)
So(err, ShouldBeNil)
})
}
// go test -test.v -test.run TestServiceScanSalaryLog
func TestServiceScanSalaryLog(t *testing.T) {
Convey("TestServiceScanSalaryLog", t, func() {
err := s.ScanSalaryLog(c)
So(err, ShouldBeNil)
})
}

View File

@@ -0,0 +1,728 @@
package service
import (
"context"
"encoding/json"
xlog "log"
"strconv"
"sync"
"time"
"go-common/app/job/main/vip/conf"
"go-common/app/job/main/vip/dao"
"go-common/app/job/main/vip/model"
couponrpc "go-common/app/service/main/coupon/rpc/client"
v1 "go-common/app/service/main/vip/api"
"go-common/library/log"
"go-common/library/queue/databus"
"github.com/pkg/errors"
"github.com/robfig/cron"
)
const (
_tableUserInfo = "vip_user_info"
_tablePayOrder = "vip_pay_order"
_insertAction = "insert"
_updateAction = "update"
_deleteAction = "delete"
notifyAction = "updateVip"
_ps = 50
_defsleepmsec = 100
)
//Service vip service
type Service struct {
dao *dao.Dao
c *conf.Config
//vipRPC *client.Service
reducePayOrder map[string]*model.VipPayOrder
appMap map[int64]*model.VipAppInfo
confMap map[string]*model.VipConfig
cleanVipCache chan int64
cleanAppCache chan *model.AppCache
ds *databus.Databus
handlerFailPayOrder chan *model.VipPayOrder
handlerFailUserInfo chan *model.VipUserInfo
handlerFailRechargeOrder chan *model.VipPayOrder
handlerFailVipbuy chan *model.VipBuyResq
handlerInsertOrder chan *model.VipPayOrder
handlerUpdateOrder chan *model.VipPayOrder
handlerRechargeOrder chan *model.VipPayOrder
handlerInsertUserInfo chan *model.VipUserInfo
handlerUpdateUserInfo chan *model.VipUserInfo
handlerStationActive chan *model.VipPayOrder
handlerAutoRenewLog chan *model.VipUserInfo
handlerAddVipHistory chan *model.VipChangeHistoryMsg
handlerAddBcoinSalary chan *model.VipBcoinSalaryMsg
handlerUpdateBcoinSalary chan *model.VipBcoinSalaryMsg
handlerDelBcoinSalary chan *model.VipBcoinSalaryMsg
notifycouponchan chan func()
accLogin *databus.Databus
frozenDate time.Duration
newVipDatabus *databus.Databus
salaryCoupnDatabus *databus.Databus
accountNoitfyDatabus *databus.Databus
couponNotifyDatabus *databus.Databus
autoRenewdDatabus *databus.Databus
// waiter
waiter sync.WaitGroup
closed bool
sendmsgchan chan func()
couponRPC *couponrpc.Service
// vip service
vipgRPC v1.VipClient
}
//New new service
func New(c *conf.Config) (s *Service) {
s = &Service{
c: c,
dao: dao.New(c),
//vipRPC: client.New(c.VipRPC),
cleanVipCache: make(chan int64, 10240),
confMap: make(map[string]*model.VipConfig),
cleanAppCache: make(chan *model.AppCache, 10240),
handlerFailPayOrder: make(chan *model.VipPayOrder, 10240),
handlerFailUserInfo: make(chan *model.VipUserInfo, 10240),
handlerFailRechargeOrder: make(chan *model.VipPayOrder, 10240),
handlerInsertOrder: make(chan *model.VipPayOrder, 10240),
handlerRechargeOrder: make(chan *model.VipPayOrder, 10240),
handlerUpdateOrder: make(chan *model.VipPayOrder, 10240),
handlerInsertUserInfo: make(chan *model.VipUserInfo, 10240),
handlerUpdateUserInfo: make(chan *model.VipUserInfo, 10240),
handlerStationActive: make(chan *model.VipPayOrder, 10240),
handlerAutoRenewLog: make(chan *model.VipUserInfo, 10240),
handlerAddVipHistory: make(chan *model.VipChangeHistoryMsg, 10240),
handlerAddBcoinSalary: make(chan *model.VipBcoinSalaryMsg, 10240),
handlerUpdateBcoinSalary: make(chan *model.VipBcoinSalaryMsg, 10240),
handlerDelBcoinSalary: make(chan *model.VipBcoinSalaryMsg, 10240),
handlerFailVipbuy: make(chan *model.VipBuyResq, 10240),
notifycouponchan: make(chan func(), 10240),
ds: databus.New(c.Databus.OldVipBinLog),
newVipDatabus: databus.New(c.Databus.NewVipBinLog),
accLogin: databus.New(c.Databus.AccLogin),
frozenDate: time.Duration(c.Property.FrozenDate),
reducePayOrder: make(map[string]*model.VipPayOrder),
sendmsgchan: make(chan func(), 10240),
accountNoitfyDatabus: databus.New(c.Databus.AccountNotify),
couponRPC: couponrpc.New(c.RPCClient2.Coupon),
}
vipgRPC, err := v1.NewClient(c.VipClient)
if err != nil {
panic(err)
}
s.vipgRPC = vipgRPC
t := cron.New()
go s.loadappinfoproc()
go s.cleanappcacheretryproc()
go s.cleanvipretryproc()
go s.sendmessageproc()
go s.handlerfailpayorderproc()
go s.handlerfailrechargeorderproc()
go s.handlerfailuserinfoproc()
go s.handlerautorenewlogproc()
go s.handlerdelbcoinproc()
if c.Databus.SalaryCoupon != nil {
s.salaryCoupnDatabus = databus.New(c.Databus.SalaryCoupon)
s.waiter.Add(1)
go s.salarycouponproc()
}
if c.Databus.CouponNotify != nil {
s.couponNotifyDatabus = databus.New(c.Databus.CouponNotify)
go s.couponnotifyproc()
s.waiter.Add(1)
go s.couponnotifybinlogproc()
}
for i := 0; i < s.c.Property.HandlerThread; i++ {
go s.handlerinsertorderproc()
go s.handlerupdateorderproc()
go s.handlerinsertuserinfoproc()
go s.handlerupdateuserinfoproc()
go s.handleraddchangehistoryproc()
go s.handleraddbcoinproc()
go s.handlerupdatebcoinproc()
go s.handlerupdaterechargeorderproc()
}
for i := 0; i < s.c.Property.ReadThread; i++ {
go s.readdatabusproc()
}
go s.readnewvipdatabusproc()
if c.Property.FrozenCron != "" {
go s.accloginproc()
t.AddFunc(c.Property.FrozenCron, s.unFrozenJob)
}
t.AddFunc(c.Property.UpdateUserInfoCron, s.updateUserInfoJob)
t.AddFunc(c.Property.SalaryVideoCouponCron, s.salaryVideoCouponJob)
t.AddFunc(c.Property.PushDataCron, s.pushDataJob)
t.AddFunc(c.Property.EleEompensateCron, s.eleEompensateJob)
//t.AddFunc(c.Property.HadExpiredMsgCron, s.hadExpiredMsgJob)
//t.AddFunc(c.Property.WillExpireMsgCron, s.willExpiredMsgJob)
//t.AddFunc(c.Property.SendMessageCron, s.sendMessageJob)
//t.AddFunc(c.Property.AutoRenewCron, s.autoRenewJob)
//t.AddFunc(c.Property.SendBcoinCron, s.sendBcoinJob)
t.Start()
go s.consumercheckproc()
if c.Databus.AutoRenew != nil {
s.autoRenewdDatabus = databus.New(c.Databus.AutoRenew)
s.waiter.Add(1)
go s.retryautorenewpayproc()
}
return
}
func (s *Service) readnewvipdatabusproc() {
defer func() {
if r := recover(); r != nil {
r = errors.WithStack(r.(error))
log.Error("Runtime error caught: %+v", r)
go s.readnewvipdatabusproc()
}
}()
var err error
for msg := range s.newVipDatabus.Messages() {
val := msg.Value
if err = msg.Commit(); err != nil {
log.Error("readdatabusproc msg.commit() error(%v)", err)
msg.Commit()
}
log.Info("cur consumer new vip db message(%v)", string(msg.Value))
message := new(model.Message)
if err = json.Unmarshal(val, message); err != nil {
log.Error("readnewvipdatabusproc json.unmarshal val(%+v) error(%+v)", string(val), err)
continue
}
if message.Table == "vip_user_info" {
userInfo := new(model.VipUserInfoNewMsg)
if err = json.Unmarshal(message.New, userInfo); err != nil {
log.Error("readdatabusproc json.Unmarshal val(%v) error(%v)", string(message.New), err)
continue
}
vipUser := convertUserInfoByNewMsg(userInfo)
s.dao.DelInfoCache(context.Background(), vipUser.Mid)
if message.Action == _insertAction {
if vipUser.PayType == model.AutoRenew {
select {
case s.handlerAutoRenewLog <- vipUser:
default:
log.Error("s.handlerAutoRenewLog full!")
}
}
} else if message.Action == _updateAction {
oldUserMsg := new(model.VipUserInfoNewMsg)
if err = json.Unmarshal(message.Old, oldUserMsg); err != nil {
log.Error("readdatabusproc json.Unmarshal val(%v) error(%v)", string(message.Old), err)
continue
}
oldUser := convertUserInfoByNewMsg(oldUserMsg)
if oldUser.PayType != vipUser.PayType {
select {
case s.handlerAutoRenewLog <- vipUser:
default:
log.Error("s.handlerAutoRenewLog full update!")
}
}
}
s.pubAccountNotify(vipUser.Mid)
}
}
}
func (s *Service) readdatabusproc() {
defer func() {
if r := recover(); r != nil {
r = errors.WithStack(r.(error))
log.Error("Runtime error caught: %+v", r)
go s.readdatabusproc()
}
}()
var err error
for msg := range s.ds.Messages() {
val := msg.Value
message := new(model.Message)
if err = json.Unmarshal(val, message); err != nil {
log.Error("readdatabusproc json.Unmarshal val(%v) error(%v)", string(val), err)
if err = msg.Commit(); err != nil {
log.Error("msg.commit() error(%v)", err)
}
continue
}
if message.Table == "vip_pay_order" {
order := new(model.VipPayOrderOldMsg)
if err = json.Unmarshal(message.New, order); err != nil {
log.Error("readdatabusproc json.Unmarshal val(%v) error(%v)", string(message.New), err)
if err = msg.Commit(); err != nil {
log.Error("msg.commit() error(%v)", err)
}
continue
}
payOrder := s.convertPayOrder(order)
if message.Action == "insert" {
select {
case s.handlerInsertOrder <- payOrder:
default:
xlog.Panic("s.handlerInsertOrder full!")
}
} else if message.Action == "update" {
select {
case s.handlerUpdateOrder <- payOrder:
default:
xlog.Panic("s.handlerUpdateOrder full!")
}
}
} else if message.Table == "vip_recharge_order" {
order := new(model.VipRechargeOrderMsg)
if err = json.Unmarshal(message.New, order); err != nil {
log.Error("readdatabusproc json.Unmarshal val(%v) error(%v)", string(message.New), err)
if err = msg.Commit(); err != nil {
log.Error("msg.commit() error(%v)", err)
}
continue
}
payOrder := s.convertPayOrderByMsg(order)
if message.Action == "update" {
select {
case s.handlerRechargeOrder <- payOrder:
default:
xlog.Panic("s.handlerRechargeOrder full!")
}
} else if message.Action == "insert" {
if len(payOrder.ThirdTradeNo) > 0 {
select {
case s.handlerRechargeOrder <- payOrder:
default:
xlog.Panic("s.handlerRechargeOrder full!")
}
}
}
} else if message.Table == "vip_user_info" {
userInfo := new(model.VipUserInfoMsg)
if err = json.Unmarshal(message.New, userInfo); err != nil {
log.Error("readdatabusproc json.Unmarshal val(%v) error(%v)", string(message.New), err)
if err = msg.Commit(); err != nil {
log.Error("msg.commit() error(%v)", err)
}
continue
}
vipUser := convertMsgToUserInfo(userInfo)
if message.Action == "insert" {
select {
case s.handlerInsertUserInfo <- vipUser:
default:
xlog.Panic("s.handlerInsertUserInfo full!")
}
} else if message.Action == "update" {
oldUser := new(model.VipUserInfoMsg)
if err = json.Unmarshal(message.Old, oldUser); err != nil {
log.Error("readdatabusproc json.Unmarshal val(%v) error(%v)", string(message.Old), err)
if err = msg.Commit(); err != nil {
log.Error("msg.commit() error(%v)", err)
}
continue
}
vipUser.OldVer = oldUser.Ver
select {
case s.handlerUpdateUserInfo <- vipUser:
default:
xlog.Panic("s.handlerUpdateUserInfo full!")
}
}
if !s.grayScope(userInfo.Mid) {
s.cleanCache(userInfo.Mid)
}
} else if message.Table == "vip_change_history" {
historyMsg := new(model.VipChangeHistoryMsg)
if err = json.Unmarshal(message.New, historyMsg); err != nil {
log.Error("readdatabusproc json.Unmarshal val(%v) error(%v)", string(message.New), err)
if err = msg.Commit(); err != nil {
log.Error("msg.commit() error(%v)", err)
}
continue
}
if message.Action == "insert" {
select {
case s.handlerAddVipHistory <- historyMsg:
default:
xlog.Panic("s.handlerAddVipHistory full!")
}
}
} else if message.Table == "vip_bcoin_salary" {
bcoinMsg := new(model.VipBcoinSalaryMsg)
if err = json.Unmarshal(message.New, bcoinMsg); err != nil {
log.Error("readdatabusproc json.Unmarshal val(%v) error(%v)", string(message.New), err)
if err = msg.Commit(); err != nil {
log.Error("msg.commit() error(%v)", err)
}
continue
}
if message.Action == _insertAction {
select {
case s.handlerAddBcoinSalary <- bcoinMsg:
default:
xlog.Panic("s.handlerAddBcoinSalary full!")
}
} else if message.Action == _updateAction {
select {
case s.handlerUpdateBcoinSalary <- bcoinMsg:
default:
xlog.Panic("s.handlerUpdateBcoinSalary full!")
}
} else if message.Action == _deleteAction {
select {
case s.handlerDelBcoinSalary <- bcoinMsg:
default:
xlog.Panic("s.handlerDelBcoinSalary full!")
}
}
}
if err = msg.Commit(); err != nil {
log.Error("readdatabusproc msg.commit() error(%v)", err)
msg.Commit()
}
log.Info("cur consumer message(%v)", string(msg.Value))
}
}
func (s *Service) cleanCache(mid int64) {
var (
hv = new(model.HandlerVip)
err error
)
hv.Type = 2
hv.Days = 0
hv.Months = 0
hv.Mid = mid
if err = s.cleanCacheAndNotify(context.TODO(), hv); err != nil {
select {
case s.cleanVipCache <- hv.Mid:
default:
xlog.Panic("s.cleanVipCache full!")
}
}
s.pubAccountNotify(mid)
}
func (s *Service) pubAccountNotify(mid int64) (err error) {
data := new(struct {
Mid int64 `json:"mid"`
Action string `json:"action"`
})
data.Mid = mid
data.Action = notifyAction
if err = s.accountNoitfyDatabus.Send(context.TODO(), strconv.FormatInt(mid, 10), data); err != nil {
log.Error("send (%+v) error(%+v)", data, err)
}
log.Info("send(mid:%+v) data:%+v", mid, data)
return
}
func (s *Service) loadappinfoproc() {
defer func() {
if r := recover(); r != nil {
r = errors.WithStack(r.(error))
log.Error("Runtime error caught: %+v", r)
go s.loadappinfoproc()
}
}()
for {
s.loadAppInfo()
time.Sleep(time.Minute * 2)
}
}
func (s *Service) loadAppInfo() {
var (
res []*model.VipAppInfo
err error
)
if res, err = s.dao.SelAppInfo(context.TODO()); err != nil {
log.Error("loadAppInfo SelAppInfo error(%v)", err)
return
}
aMap := make(map[int64]*model.VipAppInfo, len(res))
for _, v := range res {
aMap[v.ID] = v
}
s.appMap = aMap
bytes, _ := json.Marshal(res)
log.Info("load app success :%v", string(bytes))
}
func (s *Service) cleanvipretryproc() {
defer func() {
if r := recover(); r != nil {
r = errors.WithStack(r.(error))
log.Error("Runtime error caught: %+v", r)
go s.cleanvipretryproc()
}
}()
for {
mid := <-s.cleanVipCache
s.cleanVipRetry(mid)
}
}
func (s *Service) cleanVipRetry(mid int64) {
hv := new(model.HandlerVip)
hv.Type = 2
hv.Days = 0
hv.Months = 0
hv.Mid = mid
s.dao.DelInfoCache(context.Background(), mid)
for i := 0; i < s.c.Property.Retry; i++ {
if err := s.cleanCacheAndNotify(context.TODO(), hv); err == nil {
break
}
s.dao.DelVipInfoCache(context.TODO(), int64(hv.Mid))
}
log.Info("handler success cache fail mid(%v)", mid)
}
func (s *Service) cleanappcacheretryproc() {
defer func() {
if r := recover(); r != nil {
r = errors.WithStack(r.(error))
log.Error("Runtime error caught: %+v", r)
go s.cleanappcacheretryproc()
}
}()
for {
ac := <-s.cleanAppCache
s.cleanAppCacheRetry(ac)
}
}
func (s *Service) cleanAppCacheRetry(ac *model.AppCache) {
appInfo := s.appMap[ac.AppID]
hv := new(model.HandlerVip)
hv.Type = 2
hv.Days = 0
hv.Months = 0
hv.Mid = ac.Mid
for i := 0; i < s.c.Property.Retry; i++ {
if err := s.dao.SendAppCleanCache(context.TODO(), hv, appInfo); err == nil {
break
}
}
log.Info("handler success cache app fail appInfo(%v)", ac)
}
func (s *Service) salarycouponproc() {
defer func() {
if r := recover(); r != nil {
r = errors.WithStack(r.(error))
log.Error("Runtime error caught: %+v", r)
go s.salarycouponproc()
}
}()
defer s.waiter.Done()
var (
err error
msg *databus.Message
msgChan = s.salaryCoupnDatabus.Messages()
ok bool
c = context.Background()
)
for {
msg, ok = <-msgChan
if !ok || s.closed {
log.Info("salary coupon msgChan closed")
return
}
msg.Commit()
v := &model.Message{}
if err = json.Unmarshal([]byte(msg.Value), v); err != nil {
log.Error("json.Unmarshal(%v) err(%v)", v, err)
continue
}
if v.Table != _tableUserInfo {
continue
}
nvip := &model.VipUserInfoMsg{}
if err = json.Unmarshal(v.New, &nvip); err != nil {
log.Error("salary new json.Unmarshal values(%v),error(%v)", string(v.New), err)
continue
}
ovip := &model.VipUserInfoMsg{}
if v.Action != _insertAction {
if err = json.Unmarshal(v.Old, &ovip); err != nil {
log.Error("salary old json.Unmarshal values(%v),error(%v)", string(v.Old), err)
continue
}
}
log.Info("salary coupon start mid(%d)", nvip.Mid)
if _, err = s.SalaryVideoCouponAtOnce(c, nvip, ovip, v.Action); err != nil {
log.Error("SalaryVideoCouponAtOnce fail(%d) nvip(%v) ovip(%v) %s error(%v)", nvip.Mid, nvip, ovip, v.Action, err)
continue
}
log.Info("salary coupon suc mid(%d)", nvip.Mid)
}
}
func (s *Service) couponnotifybinlogproc() {
defer func() {
if r := recover(); r != nil {
r = errors.WithStack(r.(error))
log.Error("Runtime error couponnotifybinlogproc caught: %+v", r)
go s.couponnotifybinlogproc()
}
}()
defer s.waiter.Done()
var (
err error
msg *databus.Message
msgChan = s.couponNotifyDatabus.Messages()
ok bool
c = context.Background()
)
for {
msg, ok = <-msgChan
if !ok || s.closed {
log.Info("coupon notify couponnotifybinlogproc msgChan closed")
return
}
if err = msg.Commit(); err != nil {
log.Error("couponnotifybinlogproc msg.Commit err(%v)", err)
continue
}
log.Info("cur consumer couponnotifybinlogproc(%v)", string(msg.Value))
v := &model.Message{}
if err = json.Unmarshal([]byte(msg.Value), v); err != nil {
log.Error("couponnotifybinlogproc json.Unmarshal(%v) err(%v)", v, err)
continue
}
if v.Table != _tablePayOrder || v.Action != _updateAction {
continue
}
newo := new(model.VipPayOrderNewMsg)
if err = json.Unmarshal(v.New, newo); err != nil {
log.Error("couponnotifybinlogproc json.Unmarshal val(%v) error(%v)", string(v.New), err)
continue
}
oldo := new(model.VipPayOrderNewMsg)
if err = json.Unmarshal(v.Old, oldo); err != nil {
log.Error("couponnotifybinlogproc json.Unmarshal val(%v) error(%v)", string(v.Old), err)
continue
}
if newo == nil || oldo == nil {
continue
}
if oldo.Status != model.PAYING {
continue
}
if newo.Status != model.SUCCESS && newo.Status != model.FAILED {
continue
}
if newo.CouponMoney <= 0 {
continue
}
s.couponnotify(func() {
s.CouponNotify(c, newo)
})
}
}
func (s *Service) updateUserInfoJob() {
log.Info("update user info job start ....................................")
s.ScanUserInfo(context.TODO())
log.Info("update user info job end ........................................")
}
func (s *Service) salaryVideoCouponJob() {
log.Info("salary video coupon job start ....................................")
var err error
if ok := s.dao.AddTransferLock(context.TODO(), "_transferLock"); !ok {
log.Info("salary video coupon job had run ....................................")
return
}
if err = s.ScanSalaryVideoCoupon(context.TODO()); err != nil {
log.Error("ScanSalaryVideoCoupon error(%v)", err)
return
}
log.Info("salary video coupon job end ........................................")
}
//Ping check db live
func (s *Service) Ping(c context.Context) (err error) {
return s.dao.Ping(c)
}
// Close all resource.
func (s *Service) Close() {
defer s.waiter.Wait()
s.closed = true
s.salaryCoupnDatabus.Close()
s.dao.Close()
s.ds.Close()
s.newVipDatabus.Close()
}
func (s *Service) sendmessageproc() {
defer func() {
if x := recover(); x != nil {
log.Error("service.sendmessageproc panic(%v)", x)
go s.sendmessageproc()
log.Info("service.sendmessageproc recover")
}
}()
for {
f := <-s.sendmsgchan
f()
}
}
func (s *Service) sendmessage(f func()) {
defer func() {
if x := recover(); x != nil {
log.Error("service.sendmessage panic(%v)", x)
}
}()
select {
case s.sendmsgchan <- f:
default:
log.Error("service.sendmessage chan full")
}
}
func (s *Service) consumercheckproc() {
for {
time.Sleep(time.Second)
log.Info("consumercheckproc chan(cleanVipCache) size: %d", len(s.cleanVipCache))
log.Info("consumercheckproc chan(cleanAppCache) size: %d", len(s.cleanAppCache))
log.Info("consumercheckproc chan(handlerFailPayOrder) size: %d", len(s.handlerFailPayOrder))
log.Info("consumercheckproc chan(handlerFailUserInfo) size: %d", len(s.handlerFailUserInfo))
log.Info("consumercheckproc chan(handlerFailRechargeOrder) size: %d", len(s.handlerFailRechargeOrder))
log.Info("consumercheckproc chan(handlerFailVipbuy) size: %d", len(s.handlerFailVipbuy))
log.Info("consumercheckproc chan(handlerInsertOrder) size: %d", len(s.handlerInsertOrder))
log.Info("consumercheckproc chan(handlerUpdateOrder) size: %d", len(s.handlerUpdateOrder))
log.Info("consumercheckproc chan(handlerRechargeOrder) size: %d", len(s.handlerRechargeOrder))
log.Info("consumercheckproc chan(handlerInsertUserInfo) size: %d", len(s.handlerInsertUserInfo))
log.Info("consumercheckproc chan(handlerUpdateUserInfo) size: %d", len(s.handlerUpdateUserInfo))
log.Info("consumercheckproc chan(handlerStationActive) size: %d", len(s.handlerStationActive))
log.Info("consumercheckproc chan(handlerAutoRenewLog) size: %d", len(s.handlerAutoRenewLog))
log.Info("consumercheckproc chan(handlerAddVipHistory) size: %d", len(s.handlerAddVipHistory))
log.Info("consumercheckproc chan(handlerAddBcoinSalary) size: %d", len(s.handlerAddBcoinSalary))
log.Info("consumercheckproc chan(handlerUpdateBcoinSalary) size: %d", len(s.handlerUpdateBcoinSalary))
log.Info("consumercheckproc chan(handlerDelBcoinSalary) size: %d", len(s.handlerDelBcoinSalary))
log.Info("consumercheckproc chan(sendmsgchan) size: %d", len(s.sendmsgchan))
log.Info("consumercheckproc chan(notifycouponchan) size: %d", len(s.notifycouponchan))
}
}

View File

@@ -0,0 +1,117 @@
package service
import (
"context"
"flag"
"testing"
"time"
"go-common/app/job/main/vip/conf"
"go-common/app/job/main/vip/model"
"go-common/library/log"
. "github.com/smartystreets/goconvey/convey"
)
var (
s *Service
c context.Context
)
func init() {
flag.Set("conf", "../cmd/vip-job-test.toml")
if err := conf.Init(); err != nil {
panic(err)
}
c = context.TODO()
log.Init(conf.Conf.Xlog)
defer log.Close()
s = New(conf.Conf)
time.Sleep(time.Second * 2)
}
func Test_ScanUserInfo(t *testing.T) {
Convey("should return true err == nil", t, func() {
err := s.ScanUserInfo(context.TODO())
So(err, ShouldBeNil)
})
}
func TestService_HadExpiredMsgJob(t *testing.T) {
Convey("had expireMsg job", t, func() {
s.hadExpiredMsgJob()
})
}
func TestService_WillExpiredMsgJob(t *testing.T) {
Convey("had expire msg job", t, func() {
s.willExpiredMsgJob()
})
}
func TestService_SendMessageJob(t *testing.T) {
Convey("send message job", t, func() {
s.sendMessageJob()
})
}
func TestService_SendBcoinJob(t *testing.T) {
Convey("send bcoin job", t, func() {
s.sendBcoinJob()
})
}
func TestSalaryVideoCouponJob(t *testing.T) {
Convey("salaryVideoCouponJob err == nil", t, func() {
s.salaryVideoCouponJob()
s.salaryVideoCouponJob()
})
}
func TestService_HandlerVipChangeHistory(t *testing.T) {
Convey("handlervip change history ", t, func() {
err := s.HandlerVipChangeHistory()
So(err, ShouldBeNil)
})
}
func TestService_HandlerBcoin(t *testing.T) {
Convey(" handler bcoin history ", t, func() {
err := s.HandlerBcoin()
So(err, ShouldBeNil)
})
}
func TestService_HandlerPayOrder(t *testing.T) {
Convey("handler pay order", t, func() {
err := s.HandlerPayOrder()
So(err, ShouldBeNil)
})
}
func Test_push(t *testing.T) {
Convey("handler push data err should be nil", t, func() {
err := s.pushData(context.TODO())
So(err, ShouldBeNil)
})
}
func TestService_CheckBcoinData(t *testing.T) {
Convey("check bcoin data", t, func() {
mids, err := s.CheckBcoinData(context.TODO())
So(mids, ShouldBeEmpty)
So(err, ShouldBeNil)
})
}
func TestService_CheckChangeHistory(t *testing.T) {
Convey("check change history", t, func() {
mids, err := s.CheckChangeHistory(context.TODO())
So(mids, ShouldBeEmpty)
So(err, ShouldBeNil)
})
}
func Test_HandlerAutoRenewLogInfo(t *testing.T) {
Convey("err should be nil", t, func() {
err := s.handlerAutoRenewLogInfo(context.TODO(), &model.VipUserInfo{Mid: 2089809, PayType: model.AutoRenew, PayChannelID: 100})
So(err, ShouldBeNil)
})
}

View File

@@ -0,0 +1,96 @@
package service
import (
"context"
"time"
"go-common/app/job/main/vip/model"
"go-common/library/log"
)
// SyncAllUser 同步旧user——info到新db.
// FIXME 切新db后删除.
func (s *Service) SyncAllUser(c context.Context) {
var (
err error
maxID int
size = s.c.Property.BatchSize
ids = []int64{}
ousers = make(map[int64]*model.VipUserInfoOld, size)
nusers = make(map[int64]*model.VipUserInfo, _ps)
updateDB = s.c.Property.UpdateDB
nu *model.VipUserInfo
ok bool
)
if maxID, err = s.dao.SelOldUserInfoMaxID(context.TODO()); err != nil {
log.Error("sync job s.dao.SelOldUserInfoMaxID err(%+v)", err)
return
}
page := maxID / size
if maxID%size != 0 {
page++
}
log.Info("sync job vip_user_info total(%d)", page)
for i := 0; i < page; i++ {
log.Info("sync job vip_user_info page index(%d) total(%d)", i, page)
startID := i * size
endID := (i + 1) * size
if endID > maxID {
endID = maxID
}
if ousers, err = s.dao.SelOldUserInfoMaps(context.TODO(), startID, endID); err != nil {
log.Error("sync job s.dao.SelOldUserInfoMaps(%d, %d) err(%+v)", startID, endID, err)
return
}
j := 1
for _, v := range ousers {
ids = append(ids, v.Mid)
if j%_ps == 0 || j == len(ousers) {
if nusers, err = s.dao.SelVipByIds(context.TODO(), ids); err != nil {
return
}
for _, mid := range ids {
var ou *model.VipUserInfoOld
if ou, ok = ousers[mid]; !ok {
log.Warn("sync job old not found %d", mid)
continue
}
if nu, ok = nusers[mid]; !ok {
log.Warn("sync job need insert to new %d, old(%+v), toNew(%+v)", mid, ou, ou.ToNew())
if updateDB {
s.dao.SyncAddUser(context.Background(), ou.ToNew())
}
continue
}
if ou.RecentTime <= 0 {
ou.RecentTime = ou.Mtime
}
if nu.Type != ou.Type ||
nu.Status != ou.Status ||
!nu.StartTime.Time().Equal(ou.StartTime.Time()) ||
!nu.OverdueTime.Time().Equal(ou.OverdueTime.Time()) ||
!nu.AnnualVipOverdueTime.Time().Equal(ou.AnnualVipOverdueTime.Time()) ||
!nu.Ctime.Time().Equal(ou.Ctime.Time()) ||
!nu.Mtime.Time().Equal(ou.Mtime.Time()) ||
nu.PayType != ou.IsAutoRenew ||
nu.PayChannelID != ou.PayChannelID ||
!nu.IosOverdueTime.Time().Equal(ou.IosOverdueTime.Time()) ||
nu.Ver != ou.Ver ||
!nu.RecentTime.Time().Equal(ou.RecentTime.Time()) {
log.Warn("sync job need update to new %d, old(%+v), new(%+v), toNew(%+v)", mid, ou, nu, ou.ToNew())
if updateDB {
s.dao.SyncUpdateUser(context.Background(), ou.ToNew(), nu.Ver)
}
continue
}
}
log.Info("sync job vip_user_info page index(%d) ids(%+v)", j, ids)
// reset
ids = []int64{}
}
j++
}
log.Info("sync job vip_user_info page index(%d) end", i)
time.Sleep(time.Millisecond * _defsleepmsec)
}
}

View File

@@ -0,0 +1,14 @@
package service
import (
"context"
"testing"
. "github.com/smartystreets/goconvey/convey"
)
func Test_SyncAllUser(t *testing.T) {
Convey("Test_SyncAllUser", t, func() {
s.SyncAllUser(context.Background())
})
}

View File

@@ -0,0 +1,595 @@
package service
import (
"context"
"crypto/md5"
"encoding/hex"
"fmt"
"time"
"go-common/app/job/main/vip/model"
"go-common/library/database/sql"
"go-common/library/log"
"go-common/library/sync/errgroup"
xtime "go-common/library/time"
"github.com/pkg/errors"
)
const (
iapChannelID = 100
)
func (s *Service) cleanCacheAndNotify(c context.Context, hv *model.HandlerVip) (err error) {
s.dao.DelInfoCache(c, hv.Mid)
if err = s.dao.SendCleanCache(c, hv); err != nil {
return
}
if err = s.dao.DelVipInfoCache(c, int64(hv.Mid)); err != nil {
log.Error("del vip info cache (mid:%v) error(%+v)", hv.Mid, err)
return
}
eg, ec := errgroup.WithContext(c)
for _, app := range s.appMap {
ta := app
eg.Go(func() error {
if err = s.dao.SendAppCleanCache(ec, hv, ta); err == nil {
log.Info("SendAppCleanCache success hv(%v) app(%v)", hv, ta)
} else {
ac := new(model.AppCache)
ac.AppID = ta.ID
ac.Mid = hv.Mid
s.cleanAppCache <- ac
}
return nil
})
}
if err = eg.Wait(); err != nil {
log.Error(" eg.Wait err(%+v)", err)
}
err = nil
return
}
//ScanUserInfo scan all userinfo update status
func (s *Service) ScanUserInfo(c context.Context) (err error) {
var (
ot = time.Now().Format("2006-01-02 15:04:05")
userInfos []*model.VipUserInfo
size = 2000
endID = 0
)
for {
if endID, err = s.dao.SelOldUserInfoMaxID(context.TODO()); err != nil {
time.Sleep(time.Minute * 2)
continue
}
break
}
page := endID / size
if endID%size != 0 {
page++
}
for i := 0; i < page; {
startID := i * size
eID := (i + 1) * size
if userInfos, err = s.dao.SelVipList(context.TODO(), startID, eID, ot); err != nil {
time.Sleep(time.Second * 5)
continue
}
i++
for _, v := range userInfos {
s.updateUserInfo(context.TODO(), v)
}
}
return
}
func (s *Service) updateUserInfo(c context.Context, v *model.VipUserInfo) (err error) {
var (
curTime = time.Now()
fType = v.Type
fStatus = v.Status
)
if v.AnnualVipOverdueTime.Time().Before(curTime) {
fType = model.Vip
}
if v.OverdueTime.Time().Before(curTime) {
fStatus = model.VipStatusOverTime
}
if fType != v.Type || fStatus != v.Status {
v.Type = fType
v.Status = fStatus
if v.Status == model.VipStatusOverTime && v.PayChannelID == iapChannelID {
v.PayType = model.Normal
}
if _, err = s.dao.UpdateVipUser(c, int64(v.Mid), v.Status, v.Type, v.PayType); err != nil {
return
}
s.dao.DelInfoCache(c, v.Mid)
s.dao.DelVipInfoCache(c, int64(v.Mid))
}
return
}
func (s *Service) handlerautorenewlogproc() {
var (
err error
)
defer func() {
if x := recover(); x != nil {
log.Error("service.handlerautorenewlogproc panic(%v)", x)
go s.handlerautorenewlogproc()
log.Info("service.handlerautorenewlogproc recover")
}
}()
for {
user := <-s.handlerAutoRenewLog
for i := 0; i <= s.c.Property.Retry; i++ {
if err = s.handlerAutoRenewLogInfo(context.TODO(), user); err == nil {
break
}
log.Error("%+v", err)
time.Sleep(2 * time.Second)
}
}
}
func (s *Service) handlerAutoRenewLogInfo(c context.Context, user *model.VipUserInfo) (err error) {
var (
payOrder *model.VipPayOrder
paylog *model.VipPayOrderLog
rlog *model.VipPayOrderLog
)
if user.PayType == model.AutoRenew {
if user.PayChannelID == iapChannelID {
if payOrder, err = s.dao.SelPayOrderByMid(c, user.Mid, model.IAPAutoRenew, model.SUCCESS); err != nil {
err = errors.WithStack(err)
return
}
if payOrder == nil {
err = errors.Errorf("订单号不能为空......")
return
}
rlog = new(model.VipPayOrderLog)
rlog.Mid = payOrder.Mid
rlog.OrderNo = payOrder.OrderNo
rlog.Status = model.SIGN
} else {
if payOrder, err = s.dao.SelPayOrderByMid(c, user.Mid, model.AutoRenew, model.SUCCESS); err != nil {
err = errors.WithStack(err)
return
}
if payOrder == nil {
err = errors.Errorf("订单号不能为空......")
return
}
rlog = new(model.VipPayOrderLog)
rlog.Mid = payOrder.Mid
rlog.OrderNo = payOrder.OrderNo
rlog.Status = model.SIGN
}
} else {
if paylog, err = s.dao.SelPayOrderLog(c, user.Mid, model.SIGN); err != nil {
err = errors.WithStack(err)
return
}
rlog = new(model.VipPayOrderLog)
rlog.Mid = paylog.Mid
rlog.Status = model.UNSIGN
rlog.OrderNo = paylog.OrderNo
}
if rlog != nil {
if _, err = s.dao.AddPayOrderLog(c, rlog); err != nil {
err = errors.WithStack(err)
return
}
}
return
}
func (s *Service) handlerinsertuserinfoproc() {
var (
err error
)
defer func() {
if x := recover(); x != nil {
log.Error("service.handlerinsertuserinfoproc panic(%v)", x)
go s.handlerinsertuserinfoproc()
log.Info("service.handlerinsertuserinfoproc recover")
}
}()
for {
userInfo := <-s.handlerInsertUserInfo
for i := 0; i < s.c.Property.Retry; i++ {
if err = s.addUserInfo(context.TODO(), userInfo); err == nil {
s.dao.DelInfoCache(context.Background(), userInfo.Mid)
s.dao.DelVipInfoCache(context.TODO(), userInfo.Mid)
if s.grayScope(userInfo.Mid) {
s.cleanCache(userInfo.Mid)
}
break
}
log.Error("add info error(%+v)", err)
}
}
}
func (s *Service) addUserInfo(c context.Context, ui *model.VipUserInfo) (err error) {
var (
tx *sql.Tx
udh *model.VipUserDiscountHistory
)
if tx, err = s.dao.StartTx(c); err != nil {
return
}
defer func() {
if err == nil {
if err = tx.Commit(); err != nil {
log.Error("commit(%+v)", err)
return
}
} else {
tx.Rollback()
}
}()
if _, err = s.dao.AddUserInfo(tx, ui); err != nil {
err = errors.WithStack(err)
return
}
if ui.AutoRenewed == 1 {
udh = new(model.VipUserDiscountHistory)
udh.DiscountID = model.VipUserFirstDiscount
udh.Status = model.DiscountUsed
udh.Mid = ui.Mid
if _, err = s.dao.DupUserDiscountHistory(tx, udh); err != nil {
err = errors.WithStack(err)
return
}
}
return
}
func (s *Service) updateVipUserInfo(c context.Context, ui *model.VipUserInfo) (err error) {
var (
tx *sql.Tx
udh *model.VipUserDiscountHistory
eff int64
)
if tx, err = s.dao.StartTx(c); err != nil {
return
}
defer func() {
if err == nil {
if err = tx.Commit(); err != nil {
log.Error("commit(%+v)", err)
return
}
} else {
tx.Rollback()
}
}()
if eff, err = s.dao.UpdateUserInfo(tx, ui); err != nil {
err = errors.WithStack(err)
return
}
if eff <= 0 {
log.Warn("update vip RowsAffected 0 vip(%+v)", ui)
return
}
if ui.AutoRenewed == 1 {
udh = new(model.VipUserDiscountHistory)
udh.DiscountID = model.VipUserFirstDiscount
udh.Status = model.DiscountUsed
udh.Mid = ui.Mid
if _, err = s.dao.DupUserDiscountHistory(tx, udh); err != nil {
err = errors.WithStack(err)
return
}
}
return
}
func (s *Service) handlerfailuserinfoproc() {
var (
err error
)
defer func() {
if x := recover(); x != nil {
log.Error("service.handlerfailuserinfoproc panic(%v)", x)
go s.handlerfailuserinfoproc()
log.Info("service.handlerfailuserinfoproc recover")
}
}()
for {
userInfo := <-s.handlerFailUserInfo
_time := 0
for {
if err = s.updateVipUserInfo(context.TODO(), userInfo); err == nil {
s.dao.DelInfoCache(context.Background(), userInfo.Mid)
s.dao.DelVipInfoCache(context.TODO(), userInfo.Mid)
if s.grayScope(userInfo.Mid) {
s.cleanCache(userInfo.Mid)
}
break
}
log.Error("info error(%+v)", err)
_time++
if _time > _maxtime {
break
}
time.Sleep(_sleep)
}
}
}
func (s *Service) handlerupdateuserinfoproc() {
var (
err error
flag bool
)
defer func() {
if x := recover(); x != nil {
log.Error("service.handlerupdateuserinfoproc panic(%v)", x)
go s.handlerupdateuserinfoproc()
log.Info("service.handlerupdateuserinfoproc recover")
}
}()
for {
userInfo := <-s.handlerUpdateUserInfo
flag = true
for i := 0; i < s.c.Property.Retry; i++ {
if err = s.updateVipUserInfo(context.TODO(), userInfo); err == nil {
s.dao.DelInfoCache(context.Background(), userInfo.Mid)
s.dao.DelVipInfoCache(context.TODO(), userInfo.Mid)
if s.grayScope(userInfo.Mid) {
s.cleanCache(userInfo.Mid)
}
flag = false
break
}
log.Error("info error(%+v)", err)
}
if flag {
s.handlerFailUserInfo <- userInfo
}
}
}
func (s *Service) handleraddchangehistoryproc() {
defer func() {
if x := recover(); x != nil {
log.Error("service.handleraddchangehistoryproc panic(%v)", x)
go s.handleraddchangehistoryproc()
log.Info("service.handleraddchangehistoryproc recover")
}
}()
for {
msg := <-s.handlerAddVipHistory
history := convertMsgToHistory(msg)
var res []*model.VipChangeHistory
res = append(res, history)
for i := 0; i < s.c.Property.Retry; i++ {
if err := s.dao.AddChangeHistoryBatch(res); err == nil {
break
}
}
}
}
func convertMsgToHistory(msg *model.VipChangeHistoryMsg) (r *model.VipChangeHistory) {
r = new(model.VipChangeHistory)
r.Mid = msg.Mid
r.Days = msg.Days
r.Month = msg.Month
r.ChangeType = msg.ChangeType
r.OperatorID = msg.OperatorID
r.RelationID = msg.RelationID
r.BatchID = msg.BatchID
r.Remark = msg.Remark
r.ChangeTime = xtime.Time(parseTime(msg.ChangeTime).Unix())
r.BatchCodeID = msg.BatchCodeID
return
}
func parseTime(timeStr string) (t time.Time) {
var err error
if t, err = time.ParseInLocation("2006-01-02 15:04:05", timeStr, time.Local); err != nil {
t = time.Now()
}
return
}
func convertMsgToUserInfo(msg *model.VipUserInfoMsg) (r *model.VipUserInfo) {
r = new(model.VipUserInfo)
r.AnnualVipOverdueTime = xtime.Time(parseTime(msg.AnnualVipOverdueTime).Unix())
r.Mid = msg.Mid
r.OverdueTime = xtime.Time(parseTime(msg.OverdueTime).Unix())
r.PayType = msg.IsAutoRenew
r.RecentTime = xtime.Time(parseTime(msg.RecentTime).Unix())
r.StartTime = xtime.Time(parseTime(msg.StartTime).Unix())
r.Status = msg.Status
r.Type = msg.Type
r.PayChannelID = msg.PayChannelID
r.AutoRenewed = msg.AutoRenewed
r.IosOverdueTime = xtime.Time(parseTime(msg.IosOverdueTime).Unix())
r.Ver = msg.Ver
return
}
func convertUserInfoByNewMsg(msg *model.VipUserInfoNewMsg) (r *model.VipUserInfo) {
r = new(model.VipUserInfo)
r.AnnualVipOverdueTime = xtime.Time(parseTime(msg.AnnualVipOverdueTime).Unix())
r.Mid = msg.Mid
r.OverdueTime = xtime.Time(parseTime(msg.VipOverdueTime).Unix())
r.PayType = msg.VipPayType
r.RecentTime = xtime.Time(parseTime(msg.VipRecentTime).Unix())
r.StartTime = xtime.Time(parseTime(msg.VipStartTime).Unix())
r.Status = msg.VipStatus
r.Type = msg.VipType
r.PayChannelID = msg.PayChannelID
r.IosOverdueTime = xtime.Time(parseTime(msg.IosOverdueTime).Unix())
r.Ver = msg.Ver
return
}
func convertOldToNew(old *model.VipUserInfoOld) (r *model.VipUserInfo) {
r = new(model.VipUserInfo)
r.AnnualVipOverdueTime = old.AnnualVipOverdueTime
r.Mid = old.Mid
r.OverdueTime = old.OverdueTime
r.PayType = old.IsAutoRenew
r.RecentTime = old.RecentTime
r.PayChannelID = old.PayChannelID
if old.RecentTime.Time().Unix() < 0 {
r.RecentTime = xtime.Time(1451577600)
}
r.StartTime = old.StartTime
r.Status = old.Status
r.Type = old.Type
r.IosOverdueTime = old.IosOverdueTime
r.Ver = old.Ver
return
}
//HandlerVipChangeHistory handler sync change history data
func (s *Service) HandlerVipChangeHistory() (err error) {
var (
newMaxID int64
oldMaxID int64
size = int64(s.c.Property.BatchSize)
startID int64
endID = size
exitMap = make(map[string]int)
)
if oldMaxID, err = s.dao.SelOldChangeHistoryMaxID(context.TODO()); err != nil {
log.Error("selOldChangeHistory error(%+v)", err)
return
}
if newMaxID, err = s.dao.SelChangeHistoryMaxID(context.TODO()); err != nil {
log.Error("selChangeHistoryMaxID error(%+v)", err)
return
}
page := newMaxID / size
if newMaxID%size != 0 {
page++
}
for i := 0; i < int(page); i++ {
startID = int64(i) * size
endID = int64((i + 1)) * size
if endID > newMaxID {
endID = newMaxID
}
var res []*model.VipChangeHistory
if res, err = s.dao.SelChangeHistory(context.TODO(), startID, endID); err != nil {
log.Error("selChangeHistory(startID:%v endID:%v) error(%+v)", startID, endID, endID)
return
}
for _, v := range res {
exitMap[s.madeChangeHistoryMD5(v)] = 1
}
}
page = oldMaxID / size
if oldMaxID%size != 0 {
page++
}
var batch []*model.VipChangeHistory
for i := 0; i < int(page); i++ {
startID = int64(i) * size
endID = int64(i+1) * size
if endID > oldMaxID {
endID = oldMaxID
}
var res []*model.VipChangeHistory
if res, err = s.dao.SelOldChangeHistory(context.TODO(), startID, endID); err != nil {
log.Error("sel old change history (startID:%v endID:%v) error(%+v)", startID, endID, err)
return
}
for _, v := range res {
v.Days = s.calcDay(v)
madeMD5 := s.madeChangeHistoryMD5(v)
if exitMap[madeMD5] == 0 {
batch = append(batch, v)
}
}
if err = s.dao.AddChangeHistoryBatch(batch); err != nil {
log.Error("add change history batch(%+v) error(%+v)", batch, err)
return
}
batch = nil
}
return
}
func (s *Service) calcDay(r *model.VipChangeHistory) int32 {
if r.Month != 0 {
year := r.Month / 12
month := r.Month % 12
return int32(year)*model.VipDaysYear + int32(month)*model.VipDaysMonth
}
return r.Days
}
func (s *Service) madeChangeHistoryMD5(r *model.VipChangeHistory) string {
str := fmt.Sprintf("%v,%v,%v,%v,%v,%v,%v,%v,%v", r.Mid, r.Remark, r.BatchID, r.RelationID, r.OperatorID, r.Days, r.ChangeTime.Time().Format("2006-01-02 15:04:05"), r.ChangeType, r.BatchCodeID)
b := []byte(str)
hash := md5.New()
hash.Write(b)
sum := hash.Sum(nil)
return hex.EncodeToString(sum)
}
//SyncUserInfoByMid sync user by mid.
func (s *Service) SyncUserInfoByMid(c context.Context, mid int64) (err error) {
var (
old *model.VipUserInfoOld
user *model.VipUserInfo
)
if old, err = s.dao.OldVipInfo(c, mid); err != nil {
err = errors.WithStack(err)
return
}
if user, err = s.dao.SelVipUserInfo(c, mid); err != nil {
err = errors.WithStack(err)
return
}
r := convertOldToNew(old)
r.OldVer = user.Ver
if err = s.updateVipUserInfo(c, r); err != nil {
err = errors.WithStack(err)
return
}
// clear cache.
s.cleanVipRetry(mid)
return
}
// ClearUserCache clear user cache.
func (s *Service) ClearUserCache(mid int64) {
s.cleanVipRetry(mid)
}
// ClearUserCache clear user cache.
func (s *Service) grayScope(mid int64) bool {
return mid%10000 < s.c.Property.GrayScope
}

View File

@@ -0,0 +1,46 @@
package service
import (
"context"
"go-common/app/job/main/vip/model"
"testing"
. "github.com/smartystreets/goconvey/convey"
)
// go test -test.v -test.run TestSyncUserInfoByMid
func TestSyncUserInfoByMid(t *testing.T) {
Convey("SyncUserInfoByMid err == nil", t, func() {
err := s.SyncUserInfoByMid(context.TODO(), 1002)
So(err, ShouldBeNil)
})
}
// go test -test.v -test.run TestUpdateDatabusUserInfo
func TestUpdateDatabusUserInfo(t *testing.T) {
Convey("TestUpdateDatabusUserInfo err == nil", t, func() {
var (
mid int64 = 2089809
old *model.VipUserInfoOld
msg = new(model.VipUserInfoMsg)
err error
)
old, err = s.dao.OldVipInfo(context.TODO(), mid)
So(err, ShouldBeNil)
msg.Mid = old.Mid
msg.Type = old.Type
msg.Status = old.Status
msg.StartTime = old.StartTime.Time().Format("2006-01-02 15:04:05")
msg.OverdueTime = old.OverdueTime.Time().Format("2006-01-02 15:04:05")
msg.AnnualVipOverdueTime = old.AnnualVipOverdueTime.Time().Format("2006-01-02 15:04:05")
msg.RecentTime = old.RecentTime.Time().Format("2006-01-02 15:04:05")
msg.Wander = old.Wander
msg.AutoRenewed = old.AutoRenewed
msg.IsAutoRenew = old.IsAutoRenew
msg.IosOverdueTime = old.IosOverdueTime.Time().Format("2006-01-02 15:04:05")
userInfo := convertMsgToUserInfo(msg)
err = s.addUserInfo(context.TODO(), userInfo)
So(err, ShouldBeNil)
})
}