You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
779 lines
23 KiB
779 lines
23 KiB
package service |
|
|
|
import ( |
|
"context" |
|
"encoding/json" |
|
"fmt" |
|
"github.com/jinzhu/gorm" |
|
"github.com/pkg/errors" |
|
"go-common/app/admin/main/growup/conf" |
|
"go-common/app/admin/main/growup/dao" |
|
"go-common/app/admin/main/growup/dao/shell" |
|
"go-common/app/admin/main/growup/model/offlineactivity" |
|
"go-common/library/ecode" |
|
"go-common/library/log" |
|
"go-common/library/net/http/blademaster" |
|
"io/ioutil" |
|
"math/rand" |
|
"sort" |
|
"strconv" |
|
"strings" |
|
"time" |
|
) |
|
|
|
const ( |
|
uploadFilePath = "/data/uploadfiles/" |
|
dateFmt = "20060102" |
|
exportMaxCount = 1000 |
|
) |
|
|
|
func (s *Service) offlineactivityCheckSendDbProc() { |
|
defer func() { |
|
if r := recover(); r != nil { |
|
r = errors.WithStack(r.(error)) |
|
log.Error("write stat data Runtime error caught, try recover: %+v", r) |
|
go s.offlineactivityCheckSendDbProc() |
|
} |
|
}() |
|
var timer = time.NewTicker(60 * time.Second) |
|
for { |
|
select { |
|
case <-timer.C: |
|
case <-s.chanCheckDb: |
|
} |
|
s.checkResultNotSendingOrder() |
|
} |
|
} |
|
|
|
func (s *Service) offlineactivityCheckShellOrderProc() { |
|
defer func() { |
|
if r := recover(); r != nil { |
|
r = errors.WithStack(r.(error)) |
|
log.Error("write stat data Runtime error caught, try recover: %+v", r) |
|
go s.offlineactivityCheckShellOrderProc() |
|
} |
|
}() |
|
var timer = time.NewTimer(10 * time.Second) |
|
var timerStart = false |
|
var resultIds []int64 |
|
for { |
|
select { |
|
case orderID := <-s.chanCheckShellOrder: |
|
var all = []*offlineactivity.OfflineActivityResult{orderID} |
|
for { |
|
drain: |
|
for { |
|
select { |
|
case orderID := <-s.chanCheckShellOrder: |
|
all = append(all, orderID) |
|
if len(all) >= 100 { |
|
break drain |
|
} |
|
default: |
|
break drain |
|
} |
|
} |
|
s.checkShellOrder(context.Background(), all) |
|
if len(all) < 100 { |
|
break |
|
} |
|
all = nil |
|
} |
|
case resultID := <-s.chanCheckActivity: |
|
if !timerStart { |
|
timer.Reset(10 * time.Second) |
|
timerStart = true |
|
} |
|
resultIds = append(resultIds, resultID) |
|
case <-timer.C: |
|
// 去检查对应活动的状态 |
|
timerStart = false |
|
var ids []int64 |
|
ids = append(ids, resultIds...) |
|
s.checkActivityState(context.Background(), ids) |
|
// clear map |
|
resultIds = nil |
|
} |
|
} |
|
} |
|
|
|
//PreAddOfflineActivity just test add resutl |
|
func (s *Service) PreAddOfflineActivity(ctx context.Context, arg *offlineactivity.AddActivityArg) (res *offlineactivity.PreAddActivityResult, err error) { |
|
if arg == nil { |
|
return |
|
} |
|
res = new(offlineactivity.PreAddActivityResult) |
|
|
|
var bonusList = arg.BonusList |
|
var hasBonus = false |
|
res.BonusType = arg.BonusType |
|
for _, bonus := range bonusList { |
|
if bonus.TotalMoney <= 0 { |
|
err = fmt.Errorf("bonus money < 0, money=%f", bonus.TotalMoney) |
|
log.Error(err.Error()) |
|
return |
|
} |
|
res.TotalMoney += bonus.TotalMoney |
|
if bonus.Filename != "" { |
|
var fullpath = uploadFilePath + bonus.Filename |
|
var filecontent []byte |
|
filecontent, err = ioutil.ReadFile(fullpath) |
|
if err != nil { |
|
log.Error("read file fail, path=%s", fullpath) |
|
return |
|
} |
|
bonus.Mids = string(filecontent) |
|
bonus.MidList = dao.ParseMidsFromString(bonus.Mids) |
|
bonus.MemberCount = int64(len(bonus.MidList)) |
|
if bonus.MemberCount == 0 { |
|
log.Warn("no mid for this bonus, bonus:money=%d", bonus.TotalMoney) |
|
continue |
|
} |
|
} else { |
|
bonus.MidList = dao.ParseMidsFromString(bonus.Mids) |
|
bonus.MemberCount = int64(len(bonus.MidList)) |
|
if bonus.MemberCount == 0 { |
|
log.Warn("no mid for this bonus, bonus:money=%d", bonus.TotalMoney) |
|
continue |
|
} |
|
} |
|
res.MemberCount += bonus.MemberCount |
|
hasBonus = true |
|
} |
|
// 如果一个bonus都没有,则什么也不做 |
|
if !hasBonus { |
|
log.Error("no bonus") |
|
err = fmt.Errorf("没有获奖人员信息/解析文件失败(请上传csv/txt等文本格式文件)") |
|
return |
|
} |
|
|
|
return |
|
} |
|
|
|
//AddOfflineActivity add offline activity |
|
func (s *Service) AddOfflineActivity(ctx context.Context, arg *offlineactivity.AddActivityArg) (res *offlineactivity.AddActivityResult, err error) { |
|
if arg == nil { |
|
return |
|
} |
|
var bonusList = arg.BonusList |
|
var hasBonus = false |
|
var bc = ctx.(*blademaster.Context) |
|
var cookie, _ = bc.Request.Cookie("username") |
|
if cookie != nil { |
|
arg.Creator = cookie.Value |
|
} |
|
for _, bonus := range bonusList { |
|
if bonus.TotalMoney <= 0 { |
|
err = fmt.Errorf("bonus money < 0, money=%f", bonus.TotalMoney) |
|
log.Error(err.Error()) |
|
return |
|
} |
|
if bonus.Filename != "" { |
|
var fullpath = uploadFilePath + bonus.Filename |
|
var filecontent []byte |
|
filecontent, err = ioutil.ReadFile(fullpath) |
|
if err != nil { |
|
log.Error("read file fail, path=%s", fullpath) |
|
return |
|
} |
|
bonus.Mids = string(filecontent) |
|
} else { |
|
bonus.MidList = dao.ParseMidsFromString(bonus.Mids) |
|
bonus.MemberCount = int64(len(bonus.MidList)) |
|
if bonus.MemberCount == 0 { |
|
log.Warn("no mid for this bonus, bonus:money=%d", bonus.TotalMoney) |
|
continue |
|
} |
|
} |
|
|
|
hasBonus = true |
|
} |
|
// 如果一个bonus都没有,则什么也不做 |
|
if !hasBonus { |
|
log.Error("no bonus") |
|
err = fmt.Errorf("no bonus info") |
|
return |
|
} |
|
|
|
go func() { |
|
err = s.dao.OfflineActivityAddActivity(context.Background(), arg) |
|
if err != nil { |
|
log.Error("offline add fail") |
|
return |
|
} |
|
log.Info("offline add ok") |
|
if len(s.chanCheckDb) == 0 { |
|
s.chanCheckDb <- 1 |
|
} |
|
}() |
|
|
|
return |
|
} |
|
|
|
//ShellCallback shell callback |
|
func (s *Service) ShellCallback(ctx context.Context, arg *shell.OrderCallbackParam) (err error) { |
|
if arg == nil { |
|
log.Error("arg is nil") |
|
return ecode.RequestErr |
|
} |
|
var result = shell.OrderCallbackJSON{} |
|
if err = json.Unmarshal([]byte(arg.MsgContent), &result); err != nil { |
|
log.Error("msgid=%s, unmarshal msg content fail, err=%s, msgcontent=%s", err, arg.MsgID, string(arg.MsgContent)) |
|
return |
|
} |
|
log.Info("order id=%s, handle shell callback, status=%s", result.ThirdOrderNo, result.Status) |
|
if result.CustomerID != conf.Conf.ShellConf.CustomID { |
|
log.Error("order id=%s, customerid not the same, give=%s, expect=%s", result.ThirdOrderNo, result.CustomerID, conf.Conf.ShellConf.CustomID) |
|
return |
|
} |
|
orderInfo, err := s.dao.ShellCallbackUpdate(ctx, &result, arg.MsgID) |
|
if err == nil { |
|
s.queueToUpdateActivityState(orderInfo.ResultID) |
|
} |
|
return |
|
} |
|
|
|
//OfflineActivityQueryActivity query activity |
|
func (s *Service) OfflineActivityQueryActivity(ctx context.Context, arg *offlineactivity.QueryActivityByIDArg) (res *offlineactivity.QueryActivityResult, err error) { |
|
var db = s.dao.OfflineActivityGetDB() |
|
// make sure it's valid |
|
var limit, offset = arg.CheckPageValidation() |
|
if arg.ExportFormat() != "" { |
|
limit = exportMaxCount |
|
} |
|
var now = time.Now() |
|
if arg.FromDate == "" { |
|
arg.FromDate = now.AddDate(0, -1, 0).Format(dateFmt) |
|
} |
|
if arg.ToDate == "" { |
|
arg.ToDate = now.Format(dateFmt) |
|
} |
|
// 1.查询 OfflineActivityInfo 与 OfflineActivityBonus 数据,然后聚合 |
|
var activityList []*offlineactivity.OfflineActivityInfo |
|
var total = 1 |
|
if arg.ID != 0 { |
|
if err = db.Where("id=?", arg.ID).Find(&activityList).Error; err != nil && err != gorm.ErrRecordNotFound { |
|
log.Error("fail to get from db, err=%s", err) |
|
return |
|
} |
|
total = len(activityList) |
|
} else { |
|
if arg.FromDate == "" || arg.ToDate == "" { |
|
log.Error("request error, fromdate or todate is nill") |
|
err = ecode.RequestErr |
|
return |
|
} |
|
|
|
var todate, e = time.Parse(dateFmt, arg.ToDate) |
|
err = e |
|
if err != nil { |
|
log.Error("todate format err, todate=%s, err=%s", arg.ToDate, err) |
|
return |
|
} |
|
todate = todate.AddDate(0, 0, 1) |
|
var todatestr = todate.Format(dateFmt) |
|
if err = db.Table(offlineactivity.TableOfflineActivityInfo).Where("ctime>=? and ctime<=?", arg.FromDate, todatestr).Count(&total).Error; err != nil { |
|
log.Error("fail to get from db, err=%s", err) |
|
return |
|
} |
|
|
|
if err = db.Where("ctime>=? and ctime<=?", arg.FromDate, todatestr).Order("id desc").Offset(offset).Limit(limit).Find(&activityList).Error; err != nil { |
|
log.Error("fail to get from db, err=%s", err) |
|
return |
|
} |
|
} |
|
|
|
var activityIDs []int64 |
|
for _, v := range activityList { |
|
activityIDs = append(activityIDs, v.ID) |
|
} |
|
if len(activityList) == 0 { |
|
log.Warn("0 activity list") |
|
return |
|
} |
|
|
|
// 查询bonus info |
|
var bonusList []*offlineactivity.OfflineActivityBonus |
|
if err = db.Where("activity_id in (?)", activityIDs).Find(&bonusList).Error; err != nil { |
|
log.Error("fail to get from db, err=%s", err) |
|
return |
|
} |
|
|
|
res = new(offlineactivity.QueryActivityResult) |
|
var activityMap = make(map[int64]*offlineactivity.QueryActivityInfo, len(activityList)) |
|
for _, v := range activityList { |
|
var info = new(offlineactivity.QueryActivityInfo) |
|
info.CopyFromActivityDB(v) |
|
activityMap[info.ID] = info |
|
} |
|
|
|
for _, v := range bonusList { |
|
var info, _ = activityMap[v.ActivityID] |
|
if info == nil { |
|
continue |
|
} |
|
info.TotalMoney += offlineactivity.GetMoneyFromDb(v.TotalMoney) |
|
info.MemberCount += v.MemberCount |
|
} |
|
|
|
for _, v := range activityList { |
|
if info, ok := activityMap[v.ID]; ok { |
|
res.Result = append(res.Result, info) |
|
} |
|
} |
|
|
|
res.PageResult = arg.ToPageResult(total) |
|
return |
|
} |
|
|
|
// OfflineActivityQueryUpBonusSummary query up bonus info |
|
func (s *Service) OfflineActivityQueryUpBonusSummary(ctx context.Context, arg *offlineactivity.QueryUpBonusByMidArg) (res *offlineactivity.QueryUpBonusByMidResult, err error) { |
|
var limit, offset = arg.CheckPageValidation() |
|
if arg.ExportFormat() != "" { |
|
limit = exportMaxCount |
|
} |
|
// 查询所有的 result for mid |
|
// 区分已结算、未结算 |
|
// 已结算= 成功, 未结算= 初始、发送、等待 |
|
upResult, total, err := s.dao.OfflineActivityGetUpBonusResult(ctx, true, limit, offset, "mid=?", arg.Mid) |
|
if err != nil { |
|
log.Error("get from up result fail, err=%s", err) |
|
return |
|
} |
|
res = new(offlineactivity.QueryUpBonusByMidResult) |
|
res.PageResult = arg.ToPageResult(total) |
|
|
|
var bonusMap = make(map[int64]*offlineactivity.UpSummaryBonusInfo) |
|
for _, v := range upResult { |
|
var bonusInfo, ok = bonusMap[v.Mid] |
|
if !ok { |
|
bonusInfo = &offlineactivity.UpSummaryBonusInfo{ |
|
Mid: v.Mid, |
|
} |
|
bonusMap[v.Mid] = bonusInfo |
|
} |
|
switch offlineactivity.ActivityState(v.State) { |
|
case offlineactivity.ActivityStateSucess: |
|
bonusInfo.BilledMoney += offlineactivity.GetMoneyFromDb(v.BonusMoney) |
|
if bonusInfo.TmpBillTime < v.MTime { |
|
bonusInfo.TmpBillTime = v.MTime |
|
} |
|
case offlineactivity.ActivityStateInit, offlineactivity.ActivityStateSending, offlineactivity.ActivityStateWaitResult: |
|
bonusInfo.UnbilledMoney += offlineactivity.GetMoneyFromDb(v.BonusMoney) |
|
} |
|
} |
|
|
|
for _, v := range bonusMap { |
|
v.Finish() |
|
res.Result = append(res.Result, v) |
|
} |
|
return |
|
} |
|
|
|
//OfflineActivityQueryUpBonusByActivity get bonus info group by activity |
|
func (s *Service) OfflineActivityQueryUpBonusByActivity(ctx context.Context, arg *offlineactivity.QueryUpBonusByMidArg) (res *offlineactivity.QueryUpBonusByActivityResult, err error) { |
|
var limit, offset = arg.CheckPageValidation() |
|
if arg.ExportFormat() != "" { |
|
limit = exportMaxCount |
|
} |
|
// 查询所有的 result for mid |
|
// 区分已结算、未结算 |
|
// 已结算= 成功, 未结算= 初始、发送、等待 |
|
upResult, total, err := s.dao.OfflineActivityGetUpBonusByActivityResult(ctx, limit, offset, arg.Mid) |
|
if err != nil { |
|
log.Error("get from up result fail, err=%s", err) |
|
return |
|
} |
|
res = new(offlineactivity.QueryUpBonusByActivityResult) |
|
res.PageResult = arg.ToPageResult(total) |
|
|
|
// [mid][activity_id], 按Activity做聚合 |
|
var bonusMap = make(map[int64]map[int64]*offlineactivity.UpSummaryBonusInfo) |
|
for _, v := range upResult { |
|
var bonusActivityMap, ok = bonusMap[v.Mid] |
|
if !ok { |
|
bonusActivityMap = make(map[int64]*offlineactivity.UpSummaryBonusInfo) |
|
bonusMap[v.Mid] = bonusActivityMap |
|
} |
|
bonusInfo, ok := bonusActivityMap[v.ActivityID] |
|
if !ok { |
|
bonusInfo = &offlineactivity.UpSummaryBonusInfo{ |
|
Mid: v.Mid, |
|
ActivityID: v.ActivityID, |
|
} |
|
bonusActivityMap[v.ActivityID] = bonusInfo |
|
} |
|
var bonusMoney = offlineactivity.GetMoneyFromDb(v.BonusMoney) |
|
switch offlineactivity.ActivityState(v.State) { |
|
case offlineactivity.ActivityStateSucess: |
|
bonusInfo.BilledMoney += bonusMoney |
|
if bonusInfo.TmpBillTime < v.MTime { |
|
bonusInfo.TmpBillTime = v.MTime |
|
} |
|
bonusInfo.TotalBonusMoney += bonusMoney |
|
case offlineactivity.ActivityStateInit, offlineactivity.ActivityStateSending, offlineactivity.ActivityStateWaitResult: |
|
bonusInfo.UnbilledMoney += bonusMoney |
|
bonusInfo.TotalBonusMoney += bonusMoney |
|
} |
|
} |
|
|
|
for _, activityMap := range bonusMap { |
|
for _, v := range activityMap { |
|
v.Finish() |
|
res.Result = append(res.Result, v) |
|
} |
|
} |
|
return |
|
} |
|
|
|
//OfflineActivityQueryActivityByMonth activity by month |
|
func (s *Service) OfflineActivityQueryActivityByMonth(ctx context.Context, arg *offlineactivity.QueryActvityMonthArg) (res *offlineactivity.QueryActivityMonthResult, err error) { |
|
var db = s.dao.OfflineActivityGetDB() |
|
|
|
var bonusInfo []*offlineactivity.OfflineActivityBonus |
|
var limit, offset = 100, 0 |
|
var lastCount = limit |
|
for limit == lastCount { |
|
var bonusInfoTmp []*offlineactivity.OfflineActivityBonus |
|
if err = db.Find(&bonusInfoTmp).Error; err != nil { |
|
log.Error("get from db fail, err=%s", err) |
|
return |
|
} |
|
bonusInfo = append(bonusInfo, bonusInfoTmp...) |
|
lastCount = len(bonusInfoTmp) |
|
offset += lastCount |
|
} |
|
|
|
var now = time.Now() |
|
var dateStr = now.Format(dateFmt) |
|
var monthDataMap = make(map[string]*offlineactivity.ActivityMonthInfo) |
|
for _, v := range bonusInfo { |
|
var date = v.CTime.Time().Format("200601") |
|
var monthData, ok = monthDataMap[date] |
|
if !ok { |
|
monthData = &offlineactivity.ActivityMonthInfo{ |
|
CreateTime: date, |
|
} |
|
monthDataMap[date] = monthData |
|
monthData.GenerateDay = dateStr |
|
} |
|
monthData.AddBonus(v) |
|
} |
|
|
|
var monthInfoS []*offlineactivity.ActivityMonthInfo |
|
for _, v := range monthDataMap { |
|
monthInfoS = append(monthInfoS, v) |
|
v.Finish() |
|
} |
|
|
|
sort.Slice(monthInfoS, func(i, j int) bool { |
|
return monthInfoS[i].CreateTime > monthInfoS[j].CreateTime |
|
}) |
|
|
|
var lastIndex = len(monthInfoS) - 1 |
|
if lastIndex >= 0 { |
|
monthInfoS[lastIndex].TotalMoneyAccumulate = monthInfoS[lastIndex].TotalBonusMoneyMonth |
|
} |
|
|
|
for i := len(monthInfoS) - 1; i > 0; i-- { |
|
monthInfoS[i-1].TotalMoneyAccumulate = monthInfoS[i].TotalMoneyAccumulate + monthInfoS[i-1].TotalBonusMoneyMonth |
|
} |
|
|
|
res = new(offlineactivity.QueryActivityMonthResult) |
|
res.Result = monthInfoS |
|
return |
|
} |
|
|
|
/* |
|
查询对应的activity_result表state=0 && bonus_type = 1 && ctime <= 86400 |
|
for each item state=0 and activity_id = ? limit 100 |
|
if 没有order_id, |
|
生成order_id,写入数据库, result表与shellorder表 |
|
写入成功,记录等待发送 |
|
if 有order id, |
|
// 说明已经经历过上一步,但是发送失败?那么加入到检查order id的队列 |
|
记录等待发送 |
|
|
|
批量进行发送-> shell |
|
if 发送成功 |
|
更新所有的item状态 -> 1 |
|
else if 失败: |
|
错误码是8002999997->order_id有重复 |
|
更新所有的item order id = '',等待重试 |
|
*/ |
|
func (s *Service) checkResultNotSendingOrder() { |
|
var db, err = gorm.Open("mysql", conf.Conf.ORM.Growup.DSN) |
|
if err != nil { |
|
log.Error("open db fail, dsn=%s", conf.Conf.ORM.Growup.DSN) |
|
return |
|
} |
|
defer db.Close() |
|
db.LogMode(false) |
|
// 查询对应的activity_result表state=0 && bonus_type = 1 && diff(ctime) <= 86400 |
|
var minTime = time.Now().Add(-time.Hour * 24) |
|
var limit = 100 |
|
var lastCount = limit |
|
var lastID int64 |
|
for limit == lastCount { |
|
var needSendResult = make([]*offlineactivity.OfflineActivityResult, limit) |
|
if err = db.Select("id, mid, bonus_money, order_id, activity_id, bonus_id"). |
|
Where("state=0 and bonus_type=1 and ctime>=? and id>?", minTime, lastID).Limit(limit). |
|
Find(&needSendResult).Error; err != nil { |
|
log.Error("get result fail, err=%s", err) |
|
return |
|
} |
|
|
|
lastCount = len(needSendResult) |
|
var now = time.Now() |
|
var sendRequestResult []*offlineactivity.OfflineActivityResult |
|
for _, activityResult := range needSendResult { |
|
if activityResult.ID > lastID { |
|
lastID = activityResult.ID |
|
} |
|
// if 没有order_id, |
|
if activityResult.OrderID == "" { |
|
activityResult.OrderID = generateOrderID(activityResult, now) |
|
if err = s.offlineActivityUpdateResultForSend(db, activityResult); err != nil { |
|
log.Warn("fail update result, err=%s, will retry next time", err) |
|
continue |
|
} |
|
log.Info("update result ok, mid=%d, activity_id=%d, order_id=%s", activityResult.Mid, activityResult.ActivityID, activityResult.OrderID) |
|
sendRequestResult = append(sendRequestResult, activityResult) |
|
} else { |
|
// if 有order id, |
|
go func(res *offlineactivity.OfflineActivityResult) { |
|
s.chanCheckShellOrder <- res |
|
log.Info("order has id, need to check, order id=%s, mid=%d, activityid=%d, bonusid=%d", |
|
res.OrderID, res.Mid, res.ActivityID, res.BonusID) |
|
}(activityResult) |
|
} |
|
if len(sendRequestResult) >= 10 { |
|
err = s.sendRequestAndUpdate(db, sendRequestResult) |
|
if err != nil { |
|
log.Error("send request err, err=%s", err) |
|
} else { |
|
log.Info("send to shell ok, length=%d", len(sendRequestResult)) |
|
} |
|
|
|
sendRequestResult = nil |
|
} |
|
} |
|
if len(sendRequestResult) > 0 { |
|
err = s.sendRequestAndUpdate(db, sendRequestResult) |
|
if err != nil { |
|
log.Error("send request err, err=%s", err) |
|
} else { |
|
log.Info("send to shell ok, length=%d", len(sendRequestResult)) |
|
} |
|
} |
|
} |
|
|
|
} |
|
|
|
func (s *Service) sendRequestAndUpdate(db *gorm.DB, sendRequestResult []*offlineactivity.OfflineActivityResult) (err error) { |
|
switch { |
|
default: |
|
var res *shell.OrderResponse |
|
if res, err = s.sendShellOrder(context.Background(), sendRequestResult); err != nil || res == nil { |
|
if err != nil { |
|
log.Error("fail to send to shell order, err=%s", err) |
|
} |
|
break |
|
} |
|
var ids []int64 |
|
for _, v := range sendRequestResult { |
|
ids = append(ids, v.ID) |
|
} |
|
// 需要区分是否返回 错误码是8002999997,表示有重复 |
|
if res.Errno == 8002999997 { |
|
log.Error("fail to send request, err=%d, msg=%s,重复!", res.Errno, res.Msg) |
|
// 将order id 更新为"" |
|
if err = db.Table(offlineactivity.TableOfflineActivityResult).Where("id in (?)", ids). |
|
Update("order_id=''").Error; err != nil { |
|
log.Error("fail to update order id for duplicate order, id=%v", ids) |
|
break |
|
} |
|
return |
|
} else if res.Errno != 0 { |
|
log.Error("fail to send request, err=%d, msg=%s", res.Errno, res.Msg) |
|
return |
|
} |
|
|
|
if err = db.Table(offlineactivity.TableOfflineActivityResult).Where("id in (?)", ids). |
|
Update("state", offlineactivity.ActivityStateWaitResult).Error; err != nil { |
|
log.Error("fail to update state, id=%v", ids) |
|
break |
|
} |
|
log.Info("send request to shell order") |
|
} |
|
return |
|
} |
|
|
|
func generateOrderID(result *offlineactivity.OfflineActivityResult, tm time.Time) string { |
|
var order = fmt.Sprintf("%s%04d%010d%04d", time.Now().Format("20060102150405"), (result.ActivityID*100+result.BonusID)%10000, result.Mid, rand.Int()%10000) |
|
if len(order) > 32 { |
|
order = order[:32] |
|
} |
|
return order |
|
} |
|
|
|
func (s *Service) offlineActivityUpdateResultForSend(db *gorm.DB, result *offlineactivity.OfflineActivityResult) (err error) { |
|
if result == nil { |
|
err = fmt.Errorf("nil pointer") |
|
return |
|
} |
|
var tx = db.Begin() |
|
defer func() { |
|
if r := recover(); r != nil || err != nil { |
|
tx.Rollback() |
|
} |
|
}() |
|
// create |
|
var query = tx.Select("order_id").Where("order_id=''").Save(result) |
|
err = db.Error |
|
if err != nil { |
|
log.Error("err save offline result, err=%s", err) |
|
return |
|
} |
|
if query.RowsAffected == 0 { |
|
var msg = fmt.Sprintf("update order fail, it may already be sent, result_id=%d, mid=%d, activity_id=%d", result.ID, result.Mid, result.ActivityID) |
|
log.Warn(msg) |
|
err = fmt.Errorf(msg) |
|
return |
|
} |
|
var shellOrder = offlineactivity.OfflineActivityShellOrder{ |
|
OrderID: result.OrderID, |
|
ResultID: result.ID, |
|
} |
|
if err = tx.Save(&shellOrder).Error; err != nil { |
|
log.Error("err insert offline shell order, err=%s", err) |
|
return |
|
} |
|
|
|
return tx.Commit().Error |
|
} |
|
|
|
func (s *Service) sendShellOrder(ctx context.Context, needSendResult []*offlineactivity.OfflineActivityResult) (res *shell.OrderResponse, err error) { |
|
if len(needSendResult) == 0 { |
|
log.Warn("no need to send, len=0") |
|
return |
|
} |
|
var nowtime = time.Now() |
|
var now = time.Now().UnixNano() / int64(time.Millisecond) |
|
var request = shell.OrderRequest{ |
|
ProductName: "活动奖励", |
|
NotifyURL: conf.Conf.ShellConf.CallbackURL, |
|
Rate: "1.0", |
|
SignType: "MD5", |
|
Timestamp: strconv.Itoa(int(beginOfDay(nowtime).UnixNano() / int64(time.Millisecond))), |
|
} |
|
for _, item := range needSendResult { |
|
var money = fmt.Sprintf("%0.2f", float64(offlineactivity.GetMoneyFromDb(item.BonusMoney))) |
|
var orderInfo = shell.OrderInfo{ |
|
Mid: item.Mid, |
|
Brokerage: money, |
|
ThirdCoin: money, |
|
ThirdOrderNo: item.OrderID, |
|
ThirdCtime: strconv.Itoa(int(now)), |
|
} |
|
request.Data = append(request.Data, orderInfo) |
|
} |
|
//log.Info("request=%+v", request) |
|
res, err = s.shellClient.SendOrderRequest(ctx, &request) |
|
if err != nil || res == nil { |
|
log.Error("fail to send request, err=%s", err) |
|
} else { |
|
log.Info("send shell request, msg=%s, errno=%d", res.Msg, res.Errno) |
|
} |
|
return |
|
} |
|
|
|
/* |
|
2.定单查询 - 查询定单的状态, |
|
for each state=0 and 存在order id |
|
去贝壳查询该定单状态 |
|
if 定单不存在 |
|
设置order id='', state=0 // 清除定单,等待重新发送 |
|
else if 定单成功 |
|
设置state=10 |
|
else if 定单失败 |
|
设置state=11 |
|
*/ |
|
// 最大不要超过100个订单 |
|
func (s *Service) checkShellOrder(ctx context.Context, values []*offlineactivity.OfflineActivityResult) (err error) { |
|
if len(values) == 0 { |
|
err = fmt.Errorf("values slice 0 length") |
|
return |
|
} |
|
var orderMap = make(map[string]*offlineactivity.OfflineActivityResult, len(values)) |
|
var orderIds []string |
|
for i, v := range values { |
|
if i > 99 { |
|
break |
|
} |
|
orderIds = append(orderIds, v.OrderID) |
|
orderMap[v.OrderID] = v |
|
} |
|
var orderIDString = strings.Join(orderIds, ",") |
|
var orderCheckRequest = shell.OrderCheckRequest{ |
|
Timestamp: time.Now().UnixNano() / int64(time.Millisecond), |
|
ThirdOrderNos: orderIDString, |
|
} |
|
res, err := s.shellClient.SendCheckOrderRequest(ctx, &orderCheckRequest) |
|
if err != nil { |
|
log.Error("fail to check order, order id=%s, err=%s", orderIDString, err) |
|
return |
|
} |
|
// 区分找到的订单和未找到的订单 |
|
// 订单找到,更新状态 |
|
for _, v := range res.Orders { |
|
log.Info("order find, orderid=%s, current status=%s", v.ThirdOrderNo, v.Status) |
|
// 删除已找到的订单 |
|
delete(orderMap, v.ThirdOrderNo) |
|
var resultJSON = shell.OrderCallbackJSON{ |
|
Status: v.Status, |
|
ThirdOrderNo: v.ThirdOrderNo, |
|
Mid: v.Mid, |
|
} |
|
// 更新订单结果 |
|
var orderInfo, e = s.dao.ShellCallbackUpdate(ctx, &resultJSON, "checkorder") |
|
if e == nil { |
|
s.queueToUpdateActivityState(orderInfo.ID) |
|
} |
|
} |
|
// 订单未找到,重新发送请求 |
|
var needSendRequest []*offlineactivity.OfflineActivityResult |
|
for _, v := range orderMap { |
|
needSendRequest = append(needSendRequest, v) |
|
} |
|
err = s.sendRequestAndUpdate(s.dao.OfflineActivityGetDB(), needSendRequest) |
|
if err != nil { |
|
log.Error("send order request fail, err=%s", err) |
|
return |
|
} |
|
return |
|
} |
|
func (s *Service) checkActivityState(ctx context.Context, values []int64) (err error) { |
|
if len(values) == 0 { |
|
log.Warn("no activity need to check") |
|
return |
|
} |
|
// |
|
upResult, err := s.dao.OfflineActivityGetUpBonusResultSelect(ctx, "distinct(activity_id) as activity_id", "id in (?)", values) |
|
for _, v := range upResult { |
|
var _, e = s.dao.UpdateActivityState(ctx, v.ActivityID) |
|
if e != nil { |
|
log.Error("err when update activity state, err=%s", e) |
|
} |
|
} |
|
return |
|
} |
|
|
|
func (s *Service) queueToUpdateActivityState(resultID int64) { |
|
s.chanCheckActivity <- resultID |
|
} |
|
|
|
func beginOfDay(t time.Time) time.Time { |
|
year, month, day := t.Date() |
|
return time.Date(year, month, day, 0, 0, 0, 0, t.Location()) |
|
}
|
|
|