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,78 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_test",
"go_library",
)
go_test(
name = "go_default_test",
srcs = [
"income_test.go",
"notice_test.go",
"service_test.go",
"withdraw_test.go",
],
embed = [":go_default_library"],
rundir = ".",
tags = ["automanaged"],
deps = [
"//app/interface/main/growup/conf:go_default_library",
"//app/interface/main/growup/model:go_default_library",
"//vendor/github.com/smartystreets/goconvey/convey:go_default_library",
],
)
go_library(
name = "go_default_library",
srcs = [
"activity.go",
"archive_stat.go",
"banner.go",
"bgm.go",
"column.go",
"exchange.go",
"income.go",
"notice.go",
"service.go",
"snow_flake.go",
"special_award.go",
"up_bill.go",
"up_status.go",
"up_year.go",
"withdraw.go",
],
importpath = "go-common/app/interface/main/growup/service",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/interface/main/growup/conf:go_default_library",
"//app/interface/main/growup/dao:go_default_library",
"//app/interface/main/growup/model:go_default_library",
"//library/database/sql:go_default_library",
"//library/ecode:go_default_library",
"//library/log:go_default_library",
"//library/net/metadata:go_default_library",
"//library/time:go_default_library",
"//library/xstr:go_default_library",
"//vendor/golang.org/x/sync/errgroup:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [
":package-srcs",
"//app/interface/main/growup/service/newbie:all-srcs",
],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,153 @@
package service
import (
"context"
"sort"
"time"
"go-common/app/interface/main/growup/model"
"go-common/library/log"
xtime "go-common/library/time"
)
const (
_canEnrol = iota // 未报名,可以报名
_hasEnrol // 已报名
_win // 中奖
_notEnrol // 不能报名
_enrolEnd // 报名已结束
)
// ShowActivity show creative_activity
func (s *Service) ShowActivity(c context.Context, mid, activityID int64) (ac *model.CActivity, err error) {
ac, err = s.dao.GetActivity(c, activityID)
if err != nil {
log.Error("s.dao.GetActivity error(%v)", err)
return
}
enrolNum, winNum, mids, upState, err := s.handleUpActivity(c, mid, activityID)
if err != nil {
log.Error("s.handleUpActivity error(%v)", err)
return
}
// 当前时间是否可以展示
now := xtime.Time(time.Now().Unix())
if ac.UpdatePage == 1 && now >= ac.ProgressStart && now <= ac.ProgressEnd {
ac.ProgressState = 1
ac.Enrollment = enrolNum
ac.WinNum = winNum
// 2: 中奖类型为排序型
if ac.WinType == 2 {
ac.Ranking, err = s.getActUpInfo(c, mids)
if err != nil {
log.Error("s.getActUpInfo error(%v)", err)
return
}
}
}
if !(ac.BonusQuery == 1 && now >= ac.BonusQueryStart && now <= ac.BonusQueryEnd) {
ac.BonusQuery = 0
}
// 获取up主当前状态
ac.SignUpState, err = s.getSignUpState(c, mid, upState, ac)
if err != nil {
log.Error("s.getSignUpState error(%v)", err)
}
return
}
// get mid name and face
func (s *Service) getActUpInfo(c context.Context, mids []int64) (upInfos []*model.ActUpInfo, err error) {
upInfoMap, err := s.dao.AccountInfos(c, mids)
if err != nil {
return
}
upInfos = make([]*model.ActUpInfo, len(upInfoMap))
for i := 0; i < len(mids); i++ {
upInfos[i] = upInfoMap[mids[i]]
}
return
}
func (s *Service) getSignUpState(c context.Context, mid int64, upState int, ac *model.CActivity) (signUpState int, err error) {
now := xtime.Time(time.Now().Unix())
// 报名未开始,不能报名
if now < ac.SignUpStart {
signUpState = _notEnrol
return
}
// 报名已结束并且未中奖
if now > ac.SignUpEnd && upState != _win {
upState = _enrolEnd
}
// 已报名
if upState >= _hasEnrol {
signUpState = upState
return
}
// 签约结束时间 >= 报名结束时间, 任何人都可以报名
if ac.SignedEnd >= ac.SignUpEnd {
signUpState = _canEnrol
} else {
var signedAt xtime.Time
signedAt, err = s.dao.GetUpSignedAt(c, "up_info_video", mid)
if err != nil {
return
}
if signedAt >= ac.SignedStart && signedAt <= ac.SignedEnd {
signUpState = _canEnrol
} else {
signUpState = _notEnrol
}
}
return
}
func (s *Service) handleUpActivity(c context.Context, mid, activityID int64) (enrol, win int, mids []int64, upState int, err error) {
ups, err := s.dao.ListUpActivity(c, activityID)
if err != nil {
return
}
sort.Slice(ups, func(i, j int) bool {
return ups[i].Rank < ups[j].Rank
})
mids = make([]int64, 0)
for _, up := range ups {
if up.State >= _win {
win++
enrol++
mids = append(mids, up.MID)
} else if up.State == _hasEnrol {
enrol++
}
if up.MID == mid {
upState = up.State
// 已发奖
if upState == 3 {
upState = 2
}
}
}
return
}
// SignUpActivity up sign up activity
func (s *Service) SignUpActivity(c context.Context, mid, activityID int64) (err error) {
nickname, _, err := s.dao.CategoryInfo(c, mid)
if err != nil {
log.Error("s.dao.CategoryInfo error(%v)", err)
return
}
upBonus := &model.UpBonus{
MID: mid,
ActivityID: activityID,
Nickname: nickname,
State: 1,
SignUpTime: xtime.Time(time.Now().Unix()),
}
if _, err = s.dao.SignUpActivity(c, upBonus); err != nil {
log.Error("s.dao.SignUpActivity error(%v)", err)
}
return
}

View File

@@ -0,0 +1,28 @@
package service
import (
"context"
"time"
"go-common/app/interface/main/growup/model"
"go-common/library/ecode"
"go-common/library/log"
)
// avStat get stat from hbase.
func (s *Service) avStat(c context.Context, mid int64, ip string) (up *model.UpBaseStat, err error) {
hbaseDate := time.Now().AddDate(0, 0, -1).Add(-12 * time.Hour).Format("20060102")
up, err = s.dao.UpStat(c, mid, hbaseDate)
if err != nil || up == nil {
log.Error("s.data.UpStat error(%v) mid(%d) up(%v) ip(%s)", err, mid, up, ip)
err = ecode.CreativeDataErr
return
}
pfl, err := s.dao.ProfileWithStat(c, mid)
if err != nil {
return
}
up.Fans = int64(pfl.Follower)
log.Info("s.data.UpStat hbaseDate(%+v) mid(%d)", up, mid)
return
}

View File

@@ -0,0 +1,13 @@
package service
import (
"context"
"time"
"go-common/app/interface/main/growup/model"
)
// GetBanner get banner for now
func (s *Service) GetBanner(c context.Context) (b *model.Banner, err error) {
return s.dao.Banner(c, time.Now().Unix())
}

View File

@@ -0,0 +1,102 @@
package service
import (
"context"
"time"
"go-common/app/interface/main/growup/model"
"go-common/library/database/sql"
"go-common/library/ecode"
"go-common/library/log"
xtime "go-common/library/time"
)
// JoinBgm join bgm
func (s *Service) JoinBgm(c context.Context, mid int64, accountType, signType int) (err error) {
id, err := s.dao.Blocked(c, mid)
if err != nil {
log.Error("s.dao.GetBlocked mid(%d) error(%v)", mid, err)
return
}
if id != 0 {
log.Info("mid(%d) is blocked", mid)
return ecode.GrowupDisabled
}
ok, err := s.checkBgmStat(c, mid)
if err != nil {
log.Info("s.checkBgmStat error(%v)", err)
return
}
if !ok {
return ecode.GrowupDisabled
}
count, err := s.dao.BGMCount(c, mid)
if err != nil {
log.Info("s.dao.BGMCount error(%v)", err)
return
}
avStat, err := s.dao.GetAccountState(c, "up_info_video", mid)
if err != nil {
return
}
if avStat >= 5 && avStat <= 7 {
return ecode.GrowupDisabled
}
columnStat, err := s.dao.GetAccountState(c, "up_info_column", mid)
if err != nil {
return
}
if columnStat >= 5 && columnStat <= 7 {
return ecode.GrowupDisabled
}
card, err := s.dao.Card(c, mid)
if err != nil {
log.Error("s.dao.Card(%d) error(%v)", mid, err)
return
}
fans, err := s.dao.Fans(c, mid)
if err != nil {
return
}
now := xtime.Time(time.Now().Unix())
// sign_type: 1.basic; 2.first publish; 0:default.
v := &model.UpInfo{
MID: mid,
Nickname: card.Name,
AccountType: accountType,
Fans: fans,
SignType: signType,
SignedAt: now,
Bgms: count,
}
var tx *sql.Tx
if tx, err = s.dao.BeginTran(c); err != nil {
return
}
if _, err = s.dao.TxInsertBgmUpInfo(tx, v); err != nil {
tx.Rollback()
return
}
if _, err = s.dao.TxInsertCreditScore(tx, mid); err != nil {
tx.Rollback()
return
}
err = tx.Commit()
if err != nil {
log.Error("tx.Commit error(%v)", err)
}
return
}

View File

@@ -0,0 +1,76 @@
package service
import (
"context"
"time"
"go-common/app/interface/main/growup/model"
"go-common/library/ecode"
"go-common/library/log"
"go-common/library/net/metadata"
xtime "go-common/library/time"
)
// JoinColumn join up to creative column
func (s *Service) JoinColumn(c context.Context, mid int64, accountType, signType int) (err error) {
id, err := s.dao.Blocked(c, mid)
if err != nil {
log.Error("s.dao.GetBlocked mid(%d) error(%v)", mid, err)
return
}
if id != 0 {
log.Info("mid(%d) is blocked", mid)
return ecode.GrowupDisabled
}
// get up view
ip := metadata.String(c, metadata.RemoteIP)
stat, err := s.dao.ArticleStat(c, mid, ip)
if err != nil {
log.Error("s.dao.ArticleStat mid(%d) error(%v)", mid, err)
return
}
if stat.View < s.conf.Threshold.LimitArticleView {
log.Info("mid(%d) view(%s) not reach standard", mid, stat.View)
return ecode.GrowupDisabled
}
// get up nickname
card, err := s.dao.Card(c, mid)
if err != nil {
log.Error("s.dao.Card(%d) error(%v)", mid, err)
return
}
fans, err := s.dao.Fans(c, mid)
if err != nil {
return
}
state, err := s.dao.GetAccountState(c, "up_info_column", mid)
if err != nil {
return
}
// if account state is 2 3 4 5 6 7 return
if state >= 2 && state < 8 {
return
}
now := xtime.Time(time.Now().Unix())
// sign_type: 1.basic; 2.first publish; 0:default.
v := &model.UpInfo{
MID: mid,
Nickname: card.Name,
AccountType: accountType,
MainCategory: 0,
Fans: fans,
AccountState: 2,
SignType: signType,
ApplyAt: now,
TotalPlayCount: stat.View,
}
_, err = s.dao.InsertUpInfo(c, "up_info_column", "total_view_count", v)
return
}

View File

@@ -0,0 +1,250 @@
package service
import (
"context"
"fmt"
"sort"
"time"
"go-common/app/interface/main/growup/dao"
"go-common/app/interface/main/growup/model"
"go-common/library/ecode"
"go-common/library/log"
xtime "go-common/library/time"
)
var (
_display = 2
_bigVipType = 1
_remark = "激励兑换"
)
// GoodsState get goods new state
func (s *Service) GoodsState(c context.Context) (data interface{}, err error) {
var newDisplay string
goods, err := s.dao.GetDisplayGoods(c, _display)
if err != nil {
log.Error("s.dao.GetDisplayGoods error(%v)", err)
return
}
if len(goods) > 0 {
sort.Slice(goods, func(i, j int) bool {
return goods[i].DisplayOnTime > goods[j].DisplayOnTime
})
newDisplay = fmt.Sprintf("%s+%d+%d", goods[0].ProductID, goods[0].GoodsType, goods[0].DisplayOnTime)
}
data = map[string]interface{}{
"new_display": newDisplay,
"open_exchange": time.Now().Day() >= 8, // 1-7号不开放兑换
}
return
}
// GoodsShow show goods
func (s *Service) GoodsShow(c context.Context, mid int64) (goods []*model.GoodsInfo, err error) {
goods, err = s.dao.GetDisplayGoods(c, _display)
if err != nil {
log.Error("s.dao.GetDisplayGoods error(%v)", err)
return
}
vips, err := s.dao.ListVipProducts(c, mid)
if err != nil {
log.Error("s.dao.ListVipProducts error(%v)", err)
return
}
// get vip price
for _, g := range goods {
if g.GoodsType != _bigVipType {
continue
}
if v, ok := vips[g.ProductID]; ok {
g.Month = v.Month
g.ProductName = v.ProductName
g.OriginPrice = v.OriginPrice
g.CurrentPrice = int64(dao.Round(dao.Mul(float64(v.OriginPrice), float64(g.Discount)/float64(100)), 0))
}
}
sort.Slice(goods, func(i, j int) bool {
return goods[i].Month < goods[j].Month
})
return
}
// GoodsRecord get goods record
func (s *Service) GoodsRecord(c context.Context, mid int64, page, size int) (monthOrder map[string][]*model.GoodsOrder, count int, err error) {
monthOrder = make(map[string][]*model.GoodsOrder)
if page == 0 {
page = 1
}
start, end := (page-1)*size, page*size
count, err = s.dao.GetGoodsOrderCount(c, mid)
if err != nil {
log.Error("s.dao.GetGoodsRecordCount error(%v)", err)
return
}
orders, err := s.dao.GetGoodsOrders(c, mid, start, end)
if err != nil {
log.Error("s.dao.GetGoodsOrders error(%v)", err)
return
}
for i := 0; i < len(orders); i++ {
month := orders[i].OrderTime.Time().Format("2006-01")
if _, ok := monthOrder[month]; !ok {
monthOrder[month] = make([]*model.GoodsOrder, 0)
}
monthOrder[month] = append(monthOrder[month], orders[i])
}
return
}
// GoodsBuy buy goods from creative
func (s *Service) GoodsBuy(c context.Context, mid int64, productID string, goodsType int, price int64) (err error) {
date := time.Now()
// 1-7号不开放兑换
if date.Day() < 8 {
err = ecode.GrowupGoodsTimeErr
return
}
p, err := s.getProduct(c, mid, productID, goodsType)
if err != nil {
log.Error("s.getProduct error(%v)", err)
return
}
p.CurrentPrice = int64(dao.Round(dao.Mul(float64(p.OriginPrice), float64(p.Discount)/float64(100)), 0))
if price != p.CurrentPrice {
err = ecode.GrowupPriceErr
return
}
// check upwithdraw_income
upAccount, totalUnwithdraw, err := s.getUpTotalUnwithdraw(c, mid)
if err != nil {
log.Error("s.getUpTotalUnwithdraw error(%v)", err)
return
}
if totalUnwithdraw < p.CurrentPrice {
err = ecode.GrowupPriceNotEnough
return
}
uuid, err := s.sf.Generate()
if err != nil {
return
}
order := &model.GoodsOrder{
MID: mid,
OrderNo: fmt.Sprintf("DHY-%s-%d-%d", date.Format("20060102150405"), uuid, mid),
OrderTime: xtime.Time(date.Unix()),
GoodsType: p.GoodsType,
GoodsID: productID,
GoodsName: p.ProductName,
GoodsPrice: p.CurrentPrice,
GoodsCost: p.OriginPrice,
}
// 清除up_summary redis缓存
if err = s.dao.DelCacheKey(c, fmt.Sprintf("growup-up-summary:%d", mid)); err != nil {
log.Error("s.dao.DelCacheKey error(%v)", err)
return
}
tx, err := s.dao.BeginTran(c)
if err != nil {
log.Error("s.dao.BeginTran error(%v)", err)
return
}
// update up account
rows, err := s.dao.TxUpdateUpAccountExchangeIncome(tx, mid, p.CurrentPrice, upAccount.Version)
if err != nil {
tx.Rollback()
log.Error("s.dao.TxUpdateUpAccountExchangeIncome error(%v)", err)
return
}
if rows != 1 {
tx.Rollback()
log.Error("s.dao.TxUpdateUpAccountExchangeIncome update rows(%d) != 1")
err = ecode.GrowupBuyErr
return
}
// generate order
rows, err = s.dao.TxInsertGoodsOrder(tx, order)
if err != nil {
tx.Rollback()
log.Error("s.dao.InsertGoodsOrder error(%v)", err)
return
}
if rows != 1 {
tx.Rollback()
log.Error("s.dao.InsertGoodsOrder insert rows(%d) != 1", rows)
err = ecode.GrowupBuyErr
return
}
// use vip batch info
if err = s.dao.ExchangeBigVIP(c, mid, p.ResourceID, uuid, _remark); err != nil {
tx.Rollback()
log.Error("s.dao.ExchangeBigVIP error(%v)", err)
err = ecode.GrowupBuyErr
return
}
if err = tx.Commit(); err != nil {
log.Error("tx.Commit error")
}
return
}
func (s *Service) getProduct(c context.Context, mid int64, productID string, goodsType int) (p *model.GoodsInfo, err error) {
p, err = s.dao.GetGoodsByProductID(c, productID, goodsType)
if err != nil {
log.Error("s.dao.GetGoodsByProductID error(%v)", err)
return
}
vips, err := s.dao.ListVipProducts(c, mid)
if err != nil {
log.Error("s.dao.ListVipProducts error(%v)", err)
return
}
// check ResourceID
if p.ResourceID == 0 || len(vips) == 0 {
err = ecode.GrowupGoodsNotExist
return
}
vip, ok := vips[productID]
if !ok {
err = ecode.GrowupGoodsNotExist
return
}
p.OriginPrice = vip.OriginPrice
p.ProductName = vip.ProductName
return
}
func (s *Service) getUpTotalUnwithdraw(c context.Context, mid int64) (upAccount *model.UpAccount, unwithdraw int64, err error) {
lastDay := time.Now().AddDate(0, 0, -1)
upIncomes, err := s.dao.ListUpIncome(c, mid, "up_income", lastDay.Format(_layout), lastDay.Format(_layout))
if err != nil {
log.Error("s.dao.ListUpIncome error(%v)", err)
return
}
var lastDayIncome int64
for _, up := range upIncomes {
if up.Date.Time().Format(_layout) == lastDay.Format(_layout) {
lastDayIncome = up.Income
}
}
upAccount, err = s.dao.ListUpAccount(c, mid)
if err != nil {
log.Error("s.dao.ListUpAccount error(%v)", err)
return
}
if upAccount == nil {
err = ecode.GrowupPriceNotEnough
return
}
unwithdraw = upAccount.TotalUnwithdrawIncome - lastDayIncome
return
}

View File

@@ -0,0 +1,834 @@
package service
import (
"context"
"crypto/md5"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"sort"
"strconv"
"time"
"go-common/app/interface/main/growup/model"
"go-common/library/log"
xtime "go-common/library/time"
"go-common/library/xstr"
)
const (
_video = iota
_audio
_column
_bgm
_up
)
var (
_layout = "2006-01-02"
)
// GetUpCharge get up daily sum(charge) last 30 days
func (s *Service) GetUpCharge(c context.Context, mid int64, t time.Time) (total int, err error) {
begin := t.AddDate(0, 0, -30).Format(_layout)
incs, err := s.dao.GetUpDailyCharge(c, mid, begin)
if err != nil {
return
}
for _, inc := range incs {
total += inc
}
total = int(float64(total) * float64(0.6))
return
}
// ArchiveIncome get archive income by mid
func (s *Service) ArchiveIncome(c context.Context, mid int64, typ, page, size, all int) (data map[string]interface{}, err error) {
redisKey := fmt.Sprintf("growup-archive-income:%d+%d+%d+%d+%d", typ, mid, all, page, size)
data, err = s.dao.GetIncomeCache(c, redisKey)
if err != nil {
log.Error("s.dao.GetIncomeCache error(%v)", err)
return
}
if data != nil {
return
}
data, err = s.archiveIncome(c, mid, typ, page, size, all)
if err != nil {
log.Error("s.archiveIncome error(%v)", err)
return
}
err = s.dao.SetIncomeCache(c, redisKey, data)
if err != nil {
log.Error("s.dao.SetIncomeCache error(%v)", err)
}
return
}
// archiveIncome get archive income by mid
func (s *Service) archiveIncome(c context.Context, mid int64, typ, page, size, all int) (data map[string]interface{}, err error) {
data = make(map[string]interface{})
if page == 0 {
page = 1
}
start, end := (page-1)*size, page*size
date := time.Now().AddDate(0, 0, -2)
startMonth := time.Date(date.Year(), date.Month(), 1, 0, 0, 0, 0, time.Local).Format(_layout)
if all == 1 {
startMonth = "2017-12-01"
}
var (
archives []*model.ArchiveIncome
total int
)
switch typ {
case _video:
archives, total, err = s.listAvIncome(c, mid, startMonth, date.Format(_layout), start, end)
case _audio:
case _column:
archives, total, err = s.listColumnIncome(c, mid, startMonth, date.Format(_layout), start, end)
case _bgm:
archives, total, err = s.listBgmIncome(c, mid, startMonth, date.Format(_layout), start, end)
}
if err != nil {
log.Error("s.archiveIncome error(%v)", err)
return nil, err
}
data["data"] = archives
data["total_count"] = total
data["page"] = page
return
}
func (s *Service) listAvIncome(c context.Context, mid int64, startTime, endTime string, start, end int) (archiveList []*model.ArchiveIncome, total int, err error) {
avs, err := s.dao.ListAvIncome(c, mid, startTime, endTime)
if err != nil {
log.Error("s.dao.ListAvIncome error(%v)", err)
return
}
return s.handleArchiveIncome(c, avs, start, end, _video)
}
func (s *Service) listColumnIncome(c context.Context, mid int64, startTime, endTime string, start, end int) (archiveList []*model.ArchiveIncome, total int, err error) {
columns, err := s.dao.ListColumnIncome(c, mid, startTime, endTime)
if err != nil {
log.Error("s.dao.ListColumnIncome error(%v)", err)
return
}
return s.handleArchiveIncome(c, columns, start, end, _column)
}
func (s *Service) listBgmIncome(c context.Context, mid int64, startTime, endTime string, start, end int) (archiveList []*model.ArchiveIncome, total int, err error) {
bgms, err := s.dao.ListBgmIncome(c, mid, startTime, endTime)
if err != nil {
log.Error("s.dao.ListBgmIncome error(%v)", err)
return
}
return s.handleArchiveIncome(c, bgms, start, end, _bgm)
}
func calArchiveIncome(archives []*model.ArchiveIncome, avBMap map[int64]struct{}) (archiveMap map[int64]*model.ArchiveIncome) {
endTime := time.Now().AddDate(0, 0, -2)
archiveMap = make(map[int64]*model.ArchiveIncome)
archMonthIncome := make(map[int64]int64)
for _, arch := range archives {
if _, ok := avBMap[arch.ArchiveID]; ok {
continue
}
archiveDate := arch.Date.Time()
// cal month income
if archiveDate.Month() == endTime.Month() {
archMonthIncome[arch.ArchiveID] += arch.Income
}
if archiveDate.Format(_layout) != endTime.Format(_layout) {
arch.Income = 0
}
if old, ok := archiveMap[arch.ArchiveID]; !ok {
archiveMap[arch.ArchiveID] = arch
} else {
if old.Date < arch.Date {
archiveMap[arch.ArchiveID] = arch
}
}
archiveMap[arch.ArchiveID].MonthIncome = archMonthIncome[arch.ArchiveID]
}
return
}
func (s *Service) handleArchiveIncome(c context.Context, archives []*model.ArchiveIncome, start, end, typ int) (archiveList []*model.ArchiveIncome, total int, err error) {
archiveList = make([]*model.ArchiveIncome, 0)
if len(archives) == 0 {
return
}
aIDMap := make(map[int64]struct{})
aIDList := []int64{}
for _, arch := range archives {
if _, ok := aIDMap[arch.ArchiveID]; !ok {
aIDMap[arch.ArchiveID] = struct{}{}
aIDList = append(aIDList, arch.ArchiveID)
}
}
avBMap, err := s.dao.ListAvBlackList(c, aIDList, typ)
if err != nil {
log.Error("s.dao.ListAvBlackList error(%v)", err)
return
}
avsMap := calArchiveIncome(archives, avBMap)
for _, av := range avsMap {
archiveList = append(archiveList, av)
}
sort.Slice(archiveList, func(i, j int) bool {
if archiveList[i].Income == archiveList[j].Income {
if archiveList[i].MonthIncome == archiveList[j].MonthIncome {
return archiveList[i].TotalIncome > archiveList[j].TotalIncome
}
return archiveList[i].MonthIncome > archiveList[j].MonthIncome
}
return archiveList[i].Income > archiveList[j].Income
})
total = len(archiveList)
if end > total {
end = total
}
if start >= total || start > end {
return
}
archiveList = archiveList[start:end]
avIDs := []int64{}
for _, av := range archiveList {
avIDs = append(avIDs, av.ArchiveID)
}
titles := make(map[int64]string)
switch typ {
case _video:
titles, err = s.getAvTitle(c, avIDs)
if err != nil {
log.Error("s.getAvTitle error(%v)", err)
return
}
case _column:
titles, err = s.getColumnTitle(c, avIDs)
if err != nil {
log.Error("s.getColumnTitle error(%v)", err)
return
}
case _bgm:
titles, err = s.getBgmTitle(c, avIDs)
if err != nil {
log.Error("s.getBgmTitle error(%v)", err)
return
}
}
icons, err := s.getAvIcon(c, avIDs)
if err != nil {
log.Error("s.getAvIcon error(%v)", err)
return
}
breachs, err := s.getAvBreach(c, avIDs, typ)
if err != nil {
log.Error("s.getAvBreach error(%v)", err)
return
}
for _, av := range archiveList {
av.Title = titles[av.ArchiveID]
av.Icon = icons[av.ArchiveID]
av.Breach = breachs[av.ArchiveID]
}
return
}
func (s *Service) getColumnTitle(c context.Context, avs []int64) (titles map[int64]string, err error) {
return s.dao.GetColumnTitle(c, avs)
}
func (s *Service) getBgmTitle(c context.Context, avs []int64) (titles map[int64]string, err error) {
return s.dao.GetBgmTitle(c, avs)
}
func (s *Service) getAvTitle(c context.Context, avs []int64) (titles map[int64]string, err error) {
req, err := http.NewRequest("GET", s.conf.Host.ArchiveURI, nil)
if err != nil {
log.Error("http.NewRequest error(%v)", err)
return
}
q := req.URL.Query()
q.Add("aids", xstr.JoinInts(avs))
q.Add("appkey", s.conf.AppConf.Key)
now := time.Now().Unix()
q.Add("ts", strconv.FormatInt(now, 10))
sign := q.Encode() + s.conf.AppConf.Secret
q.Add("sign", fmt.Sprintf("%x", md5.Sum([]byte(sign))))
req.URL.RawQuery = q.Encode()
resp, err := http.DefaultClient.Do(req)
if err != nil {
log.Error("http.DefaultClient.Do error(%v)", err)
return
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Error("ioutil.ReadAll error(%v)", err)
return
}
res := model.ArchiveRes{}
err = json.Unmarshal(body, &res)
if err != nil {
log.Error("json.Unmarshal body %s error(%v)", string(body), err)
return
}
titles = make(map[int64]string)
for _, archive := range res.Data {
titles[archive.AID] = archive.Title
}
return
}
func (s *Service) getAvIcon(c context.Context, avs []int64) (acM map[int64]string, err error) {
if len(avs) == 0 {
return
}
activeM, err := s.dao.ListActiveInfo(c, avs)
if err != nil {
log.Error("s.dao.ListActiveInfo error(%v)", err)
return
}
tagIDM := make(map[int64]struct{})
for _, tagID := range activeM {
tagIDM[tagID] = struct{}{}
}
tagIDList := make([]int64, 0)
for tagID := range tagIDM {
tagIDList = append(tagIDList, tagID)
}
if len(tagIDList) == 0 {
return
}
tagIconM, err := s.dao.ListTagInfo(c, tagIDList)
if err != nil {
log.Error("s.dao.ListTagInfo error(%v)", err)
return
}
acM = make(map[int64]string)
for avID, tagID := range activeM {
if _, ok := tagIconM[tagID]; ok {
acM[avID] = tagIconM[tagID].Icon
}
}
return
}
func (s *Service) getAvBreach(c context.Context, avs []int64, typ int) (breachs map[int64]*model.AvBreach, err error) {
if len(avs) == 0 {
return
}
return s.dao.GetAvBreachs(c, avs, typ)
}
// UpSummary summary up income
func (s *Service) UpSummary(c context.Context, mid int64) (data interface{}, err error) {
redisKey := fmt.Sprintf("growup-up-summary:%d", mid)
res, err := s.dao.GetIncomeCache(c, redisKey)
if err != nil {
log.Error("s.dao.GetIncomeCache error(%v)", err)
return
}
if res != nil {
data = res["data"]
return
}
data, err = s.upSummary(c, mid)
if err != nil {
log.Error("s.upSummary error(%v)", err)
return
}
err = s.dao.SetIncomeCache(c, redisKey, map[string]interface{}{"data": data})
if err != nil {
log.Error("s.dao.SetIncomeCache error(%v)", err)
}
return
}
func (s *Service) upSummary(c context.Context, mid int64) (data interface{}, err error) {
summary := new(struct {
DayIncome string `json:"day_income"`
Date string `json:"date"`
Income string `json:"income"`
TotalIncome string `json:"total_income"`
WaitWithdraw string `json:"wait_withdraw"`
BreachMoney string `json:"breach_money"`
UnwithdrawDate string `json:"unwithdraw_date"`
})
summary.DayIncome, summary.Income, summary.TotalIncome, summary.WaitWithdraw, summary.BreachMoney = "0", "0", "0", "0", "0"
now := time.Now().AddDate(0, 0, -2)
nowMonth := time.Date(now.Year(), now.Month(), 1, 0, 0, 0, 0, time.Local)
count, err := s.dao.GetUpIncomeCount(c, now.Format(_layout))
if err != nil {
log.Error("s.dao.GetUpIncomeCount error(%v)", err)
return
}
if count <= 0 {
summary.DayIncome, summary.Income, summary.TotalIncome, summary.WaitWithdraw, summary.BreachMoney = "-1", "-1", "-1", "-1", "-1"
data = summary
return
}
upIncomes, err := s.dao.ListUpIncome(c, mid, "up_income", nowMonth.Format(_layout), now.AddDate(0, 0, 1).Format(_layout))
if err != nil {
log.Error("s.dao.ListUpIncome error(%v)", err)
return
}
var monthIncome, lastDayIncome, dayIncome int64
for _, up := range upIncomes {
if up.Date.Time().Format(_layout) == now.Format(_layout) {
dayIncome = up.Income
}
if up.Date.Time().Format(_layout) == now.AddDate(0, 0, 1).Format(_layout) {
lastDayIncome = up.Income
}
monthIncome += up.Income
}
upAccount, err := s.dao.ListUpAccount(c, mid)
if err != nil {
log.Error("s.dao.ListUpAccount error(%v)", err)
return
}
if upAccount == nil {
data = summary
return
}
breachs, err := s.dao.ListAvBreach(c, mid, nowMonth.Format(_layout), time.Now().Format(_layout))
if err != nil {
log.Error("s.dao.ListAvBreach error(%v)", err)
return
}
var breachMoney int64
for _, b := range breachs {
breachMoney += b.Money
}
summary.DayIncome = fmt.Sprintf("%.2f", fromYuanToFen(dayIncome))
summary.BreachMoney = fmt.Sprintf("%.2f", fromYuanToFen(breachMoney))
summary.TotalIncome = fmt.Sprintf("%.2f", fromYuanToFen(upAccount.TotalIncome-lastDayIncome))
summary.Date = now.Format(_layout)
wdv, err := time.Parse("2006-01", upAccount.WithdrawDateVersion)
if err != nil {
log.Error("time.Parse error(%v)", err)
return
}
summary.UnwithdrawDate = time.Date(wdv.Year(), wdv.Month()+1, 1, 0, 0, 0, 0, time.Local).Format("2006-01")
// 如果T-1的month不等于T-2的month 当月新增不需要减去那一天的收入:lastDayIncome = 0
if now.AddDate(0, 0, 1).Month() == now.Month() {
monthIncome -= lastDayIncome
}
summary.Income = fmt.Sprintf("%.2f", fromYuanToFen(monthIncome))
// 当月未提现,待结算不能减去昨天收入,当月已提现,需要减去昨日收入
preMonth := time.Date(nowMonth.Year(), nowMonth.Month()-1, 1, 0, 0, 0, 0, time.Local).Format("2006-01")
if preMonth != upAccount.WithdrawDateVersion || now.AddDate(0, 0, 1).Month() != now.Month() {
lastDayIncome = 0
}
summary.WaitWithdraw = fmt.Sprintf("%.2f", fromYuanToFen(upAccount.TotalUnwithdrawIncome-lastDayIncome))
data = summary
return
}
func fromYuanToFen(income int64) float64 {
return float64(income) / float64(100)
}
// ArchiveSummary get archive summary
func (s *Service) ArchiveSummary(c context.Context, typ int, mid int64) (data interface{}, err error) {
redisKey := fmt.Sprintf("growup-archive-summary:%d+%d", typ, mid)
res, err := s.dao.GetIncomeCache(c, redisKey)
if err != nil {
log.Error("s.dao.GetIncomeCache error(%v)", err)
return
}
if res != nil {
data = res["data"]
return
}
data, err = s.archiveSummary(c, typ, mid)
if err != nil {
log.Error("s.archiveSummary error(%v)", err)
return
}
err = s.dao.SetIncomeCache(c, redisKey, map[string]interface{}{"data": data})
if err != nil {
log.Error("s.dao.SetIncomeCache error(%v)", err)
}
return
}
func (s *Service) archiveSummary(c context.Context, typ int, mid int64) (data interface{}, err error) {
date := time.Now().AddDate(0, 0, -2)
startMonth := xtime.Time(time.Date(date.Year(), date.Month(), 1, 0, 0, 0, 0, time.Local).Unix())
upIncomes, err := s.dao.ListUpIncome(c, mid, "up_income", "2017-12-01", date.Format(_layout))
if err != nil {
log.Error("s.dao.ListUpIncome error(%v)", err)
return
}
summary := new(struct {
DayIncome int64 `json:"day_income"`
Date string `json:"date"`
MonthIncome int64 `json:"income"`
TotalIncome int64 `json:"total_income"`
Breach int64 `json:"breach_money"`
})
if len(upIncomes) == 0 {
data = summary
return
}
sort.Slice(upIncomes, func(i, j int) bool {
return upIncomes[i].Date > upIncomes[j].Date
})
summary.Date = date.Format(_layout)
if upIncomes[0].Date.Time().Format(_layout) == summary.Date {
switch typ {
case _video:
summary.DayIncome = upIncomes[0].AvIncome
case _column:
summary.DayIncome = upIncomes[0].ColumnIncome
case _bgm:
summary.DayIncome = upIncomes[0].BgmIncome
}
}
var breachType []int64
for _, up := range upIncomes {
if up.Date >= startMonth {
switch typ {
case _video:
summary.MonthIncome += up.AvIncome
case _column:
summary.MonthIncome += up.ColumnIncome
case _bgm:
summary.MonthIncome += up.BgmIncome
}
}
switch typ {
case _video:
breachType = []int64{0}
summary.TotalIncome += up.AvIncome
case _column:
breachType = []int64{2}
summary.TotalIncome += up.ColumnIncome
case _bgm:
breachType = []int64{3}
summary.TotalIncome += up.BgmIncome
}
}
breach, err := s.dao.GetAvBreachByType(c, mid, "2017-12-01", time.Now().Format(_layout), breachType)
if err != nil {
log.Error("s.dao.GetAvBreachByType error(%v)", err)
return
}
for d, money := range breach {
if d >= startMonth {
summary.Breach += money
}
summary.TotalIncome -= money
}
if summary.TotalIncome < 0 {
summary.TotalIncome = 0
}
data = summary
return
}
// ArchiveDetail cal archive detail
func (s *Service) ArchiveDetail(c context.Context, typ int, archiveID int64) (data interface{}, err error) {
redisKey := fmt.Sprintf("growup-archive-detail:%d+%d", typ, archiveID)
res, err := s.dao.GetIncomeCache(c, redisKey)
if err != nil {
log.Error("s.dao.GetIncomeCache error(%v)", err)
return
}
if res != nil {
data = res["data"]
return
}
data, err = s.archiveDetail(c, typ, archiveID)
if err != nil {
log.Error("s.archiveDetail error(%v)", err)
return
}
err = s.dao.SetIncomeCache(c, redisKey, map[string]interface{}{"data": data})
if err != nil {
log.Error("s.dao.SetIncomeCache error(%v)", err)
}
return
}
func (s *Service) archiveDetail(c context.Context, typ int, archiveID int64) (archives []*model.ArchiveIncome, err error) {
archives = make([]*model.ArchiveIncome, 0)
endTime := time.Now().AddDate(0, 0, -2).Format(_layout)
switch typ {
case _video:
archives, err = s.dao.ListAvIncomeByID(c, archiveID, endTime)
if err != nil {
log.Error("s.dao.ListAvIncomeByID error(%v)", err)
return
}
case _audio:
case _column:
archives, err = s.dao.ListColumnIncomeByID(c, archiveID, endTime)
if err != nil {
log.Error("s.dao.ListColumnIncomeByID error(%v)", err)
return
}
case _bgm:
archives, err = s.listBgmIncomeByID(c, archiveID, endTime)
if err != nil {
return
}
}
sort.Slice(archives, func(i, j int) bool {
return archives[i].Date > archives[j].Date
})
return
}
func (s *Service) listBgmIncomeByID(c context.Context, archiveID int64, endTime string) (archives []*model.ArchiveIncome, err error) {
as, err := s.dao.ListBgmIncomeByID(c, archiveID, endTime)
if err != nil {
log.Error("s.dao.ListBgmIncomeByID error(%v)", err)
return
}
am := make(map[xtime.Time][]*model.ArchiveIncome)
for _, a := range as {
if _, ok := am[a.Date]; ok {
am[a.Date] = append(am[a.Date], a)
} else {
am[a.Date] = []*model.ArchiveIncome{a}
}
}
archives = make([]*model.ArchiveIncome, 0)
for date, ars := range am {
a := &model.ArchiveIncome{}
a.Date = date
for _, ar := range ars {
a.Income += ar.Income
a.Avs = append(a.Avs, ar.ArchiveID)
}
archives = append(archives, a)
}
return
}
// ArchiveBreach get av_breach_record
func (s *Service) ArchiveBreach(c context.Context, mid int64, typ, page, size, all int) (data interface{}, err error) {
if page == 0 {
page = 1
}
start, end := (page-1)*size, page*size
date := time.Now().AddDate(0, 0, -2)
startMonth := time.Date(date.Year(), date.Month(), 1, 0, 0, 0, 0, time.Local).Format(_layout)
if all == 1 {
startMonth = "2017-12-01"
}
archives, err := s.dao.ListAvBreach(c, mid, startMonth, date.Format(_layout))
if err != nil {
log.Error("s.dao.ListAvBreach error(%v)", err)
return
}
typBreach := make([]*model.AvBreach, 0)
for _, arch := range archives {
if arch.CType == typ {
typBreach = append(typBreach, arch)
}
}
if len(typBreach) == 0 {
return
}
breachs, err := s.breachInBlacklist(c, typBreach, typ)
if err != nil {
log.Error("s.breachInBlacklist error(%v)", err)
return
}
sort.Slice(breachs, func(i, j int) bool {
return breachs[i].CDate > breachs[j].CDate
})
if end > len(breachs) {
end = len(breachs)
}
list := breachs[start:end]
aIDs := make([]int64, 0)
for _, b := range list {
aIDs = append(aIDs, b.AvID)
}
var titles map[int64]string
switch typ {
case _video:
titles, err = s.getAvTitle(c, aIDs)
if err != nil {
log.Error("s.getAvTitle error(%v)", err)
return
}
case _column:
titles, err = s.getColumnTitle(c, aIDs)
if err != nil {
log.Error("s.getColumnTitle error(%v)", err)
return
}
case _bgm:
titles, err = s.getBgmTitle(c, aIDs)
if err != nil {
log.Error("s.getBgmTitle error(%v)", err)
return
}
}
for _, b := range list {
b.Title = titles[b.AvID]
}
data = map[string]interface{}{
"data": list,
"total_count": len(breachs),
"page": page,
}
return
}
func (s *Service) breachInBlacklist(c context.Context, avs []*model.AvBreach, typ int) (breachs []*model.AvBreach, err error) {
aIDList := make([]int64, 0)
for _, a := range avs {
aIDList = append(aIDList, a.AvID)
}
avBMap, err := s.dao.ListAvBlackList(c, aIDList, typ)
if err != nil {
return
}
breachs = make([]*model.AvBreach, 0)
for _, av := range avs {
if _, ok := avBMap[av.AvID]; ok {
breachs = append(breachs, av)
}
}
return
}
// UpIncomeStat get up income stat by month
func (s *Service) UpIncomeStat(c context.Context, typ int, mid int64, date time.Time) (data interface{}, err error) {
redisKey := fmt.Sprintf("growup-income-stat:%d+%d", typ, mid)
res, err := s.dao.GetIncomeCache(c, redisKey)
if err != nil {
log.Error("s.dao.GetIncomeCache error(%v)", err)
return
}
if res != nil {
data = res["data"]
return
}
data, err = s.upIncomeStat(c, typ, mid, date)
if err != nil {
return
}
err = s.dao.SetIncomeCache(c, redisKey, map[string]interface{}{"data": data})
if err != nil {
log.Error("s.dao.SetIncomeCache error(%v)", err)
}
return
}
func (s *Service) upIncomeStat(c context.Context, typ int, mid int64, date time.Time) (stats []*model.UpIncomeStat, err error) {
et := date.AddDate(0, 0, -2)
end := et.Format(_layout)
// last 30 days
begin := et.AddDate(0, 0, -30).Format(_layout)
upIncomes, err := s.dao.ListUpIncome(c, mid, "up_income", begin, end)
if err != nil {
log.Error("s.dao.ListUpIncome error(%v)", err)
return
}
stats = make([]*model.UpIncomeStat, 0)
if len(upIncomes) == 0 {
return
}
var breachType []int64
for _, up := range upIncomes {
var income, baseIncome int64
switch typ {
case _video:
income, baseIncome = up.AvIncome, up.AvBaseIncome
breachType = []int64{0}
case _column:
income, baseIncome = up.ColumnIncome, up.ColumnBaseIncome
breachType = []int64{2}
case _bgm:
income, baseIncome = up.BgmIncome, up.BgmBaseIncome
breachType = []int64{3}
case _up:
income, baseIncome = up.Income, up.BaseIncome
breachType = []int64{0, 1, 2, 3}
}
extra := income - baseIncome
if extra < 0 {
extra = 0
}
stats = append(stats, &model.UpIncomeStat{
MID: up.MID,
Income: income,
BaseIncome: baseIncome,
ExtraIncome: extra,
Date: up.Date,
})
}
rs, err := s.dao.GetAvBreachByType(c, mid, begin, end, breachType)
if err != nil {
log.Error("s.dao.GetAvBreachByType error(%v)", err)
return
}
for _, stat := range stats {
if _, ok := rs[stat.Date]; ok {
stat.Breach = rs[stat.Date]
delete(rs, stat.Date)
}
}
for date, breach := range rs {
stats = append(stats, &model.UpIncomeStat{
MID: mid,
Date: date,
Breach: breach,
})
}
sort.Slice(stats, func(i, j int) bool {
return stats[i].Date < stats[j].Date
})
return
}

View File

@@ -0,0 +1,29 @@
package service
import (
"context"
"testing"
. "github.com/smartystreets/goconvey/convey"
)
func Test_ArchiveIncome(t *testing.T) {
var (
mid = int64(1011)
typ = 0
page, size = 0, 10
all = 0
)
Convey("ArchiveIncome", t, WithService(func(s *Service) {
_, err := s.ArchiveIncome(context.Background(), mid, typ, page, size, all)
So(err, ShouldBeNil)
}))
}
func Test_UpSummary(t *testing.T) {
Convey("UpSummary", t, WithService(func(s *Service) {
var mid int64 = 1011
_, err := s.UpSummary(context.Background(), mid)
So(err, ShouldBeNil)
}))
}

View File

@@ -0,0 +1,56 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)
go_library(
name = "go_default_library",
srcs = [
"letter.go",
"service.go",
],
importpath = "go-common/app/interface/main/growup/service/newbie",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/interface/main/growup/conf:go_default_library",
"//app/interface/main/growup/dao/newbiedao:go_default_library",
"//app/interface/main/growup/model:go_default_library",
"//app/service/main/account/api:go_default_library",
"//library/ecode:go_default_library",
"//library/log:go_default_library",
"//library/sync/errgroup.v2: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"],
)
go_test(
name = "go_default_test",
srcs = [
"letter_test.go",
"service_test.go",
],
embed = [":go_default_library"],
tags = ["automanaged"],
deps = [
"//app/interface/main/growup/conf:go_default_library",
"//app/interface/main/growup/model:go_default_library",
"//vendor/github.com/smartystreets/goconvey/convey:go_default_library",
],
)

View File

@@ -0,0 +1,174 @@
package newbie
import (
"context"
"go-common/app/interface/main/growup/conf"
"go-common/app/interface/main/growup/dao/newbiedao"
"go-common/app/interface/main/growup/model"
accApi "go-common/app/service/main/account/api"
"go-common/library/ecode"
"go-common/library/log"
"go-common/library/sync/errgroup.v2"
"strconv"
"time"
)
// Letter newbie letter
func (s *Service) Letter(c context.Context, req *model.NewbieLetterReq) (*model.NewbieLetterRes, error) {
var (
group *errgroup.Group
recUps = make(map[int64]*model.RecommendUp)
NewbieConf = conf.Conf.Newbie
category *model.Category
recUpMidList []int64
activities []*model.Activity
archive *model.VideoUpArchive
accInfo *accApi.InfoReply
i = 0
ok bool
err error
)
res := new(model.NewbieLetterRes)
log.Info("req: %+v", req)
group = errgroup.WithCancel(c)
// get up info
group.Go(func(ctx context.Context) error {
accInfo, err = s.dao.GetInfo(ctx, req.Mid)
return err
})
// get activities
group.Go(func(ctx context.Context) error {
activities, err = s.dao.GetActivities(ctx)
return err
})
// get video up, and set talent
group.Go(func(ctx context.Context) error {
archive, err = s.dao.GetVideoUp(ctx, req.Aid)
return err
})
err = group.Wait()
if err != nil {
return nil, err
}
// data validation, deal with default data
if req.Mid != archive.Mid {
log.Error("The archive is not yours, mid: %d, archive.Mid: %v", req.Mid, archive.Mid)
return nil, ecode.GrowupArchiveNotYours
}
if category, ok = newbiedao.Categories[archive.Tid]; !ok {
log.Error("not found the sub tid, sub tid: %d, Categories: %v", archive.Tid, newbiedao.Categories)
return nil, ecode.GrowupSubTidNotExist
}
if _, ok = newbiedao.Categories[category.Pid]; !ok {
log.Error("not found the tid, tid: %d, Categories: %v", archive.Tid, newbiedao.Categories)
return nil, ecode.GrowupTidNotExist
}
res.Area = newbiedao.Categories[category.Pid].Name
log.Info("sub tid: %d, tid: %d", archive.Tid, category.Pid)
sTid := strconv.FormatInt(archive.Tid, 10)
if res.Talent, ok = NewbieConf.Talents[sTid]; !ok {
res.Talent = NewbieConf.DefaultTalent
}
for _, activity := range activities {
if i >= NewbieConf.ActivityCount {
break
}
if activity.Type != NewbieConf.ActivityShotType {
continue
}
if activity.Cover == "" {
activity.Cover = NewbieConf.DefaultCover
}
res.Activities = append(res.Activities, activity)
i++
}
if len(res.Activities) < NewbieConf.ActivityCount {
log.Error("activity count is not enough %d", NewbieConf.ActivityCount)
return nil, ecode.GrowupActivityCountNotEnough
}
res.UperInfo = new(model.NewbieLetterUpInfo)
res.UperInfo.Mid = accInfo.Info.Mid
res.UperInfo.Name = accInfo.Info.Name
res.Archive = new(model.NewbieLetterArchive)
res.Archive.Title = archive.Title
res.Archive.PTime = time.Unix(archive.PTime, 0).Format(model.TimeLayout)
log.Info("after data validation: data(%+v)", res)
// get recommend up list
if _, ok := newbiedao.RecommendUpList[category.Pid]; !ok {
for _, lists := range newbiedao.RecommendUpList {
for recUpMid, recUp := range lists {
recUps[recUpMid] = recUp
break
}
}
log.Info("Not found recommend up list, system random get them : %+v", recUps)
} else {
recUps = newbiedao.RecommendUpList[category.Pid]
log.Info("found recommend up list : %+v", recUps)
}
// get relations
i = 0
for recUpMid := range recUps {
if i >= NewbieConf.RecommendUpPoolCount {
break
}
if recUpMid == req.Mid {
continue
}
recUpMidList = append(recUpMidList, recUpMid)
i++
}
log.Info("recUpMidList: %+v", recUpMidList)
relations, err := s.dao.GetRelations(c, req.Mid, recUpMidList)
if err != nil {
return nil, err
}
log.Info("relations: %+v", relations)
// get ups info
infosReply, err := s.dao.GetInfos(c, recUpMidList)
if err != nil {
err = ecode.GrowupRecommendUpNotExist
return nil, err
}
log.Info("recUpInfos: %+v", infosReply.Infos)
// select 3 ups
i = 0
for recUpMid := range recUps {
if i >= NewbieConf.RecommendUpCount {
break
}
if _, ok := infosReply.Infos[recUpMid]; !ok {
continue
}
if _, ok := relations[recUpMid]; !ok {
relations[recUpMid] = &model.Relation{
Mid: recUpMid,
Attribute: -1,
}
}
relations[recUpMid].Face = infosReply.Infos[recUpMid].Face
relations[recUpMid].Name = infosReply.Infos[recUpMid].Name
res.Relations = append(res.Relations, relations[recUpMid])
i++
}
log.Info("res.Relations: %+v", res.Relations)
return res, nil
}

View File

@@ -0,0 +1,25 @@
package newbie
import (
"context"
"go-common/app/interface/main/growup/model"
"testing"
"github.com/smartystreets/goconvey/convey"
)
func TestNewbieLetter(t *testing.T) {
convey.Convey("Letter", t, func(ctx convey.C) {
var (
c = context.Background()
req = &model.NewbieLetterReq{Aid: 10110467, Mid: 27515398}
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
p1, err := s.Letter(c, req)
ctx.Convey("Then err should be nil.p1 should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(p1, convey.ShouldNotBeNil)
})
})
})
}

View File

@@ -0,0 +1,33 @@
package newbie
import (
"context"
"go-common/app/interface/main/growup/conf"
"go-common/app/interface/main/growup/dao/newbiedao"
)
// Service is growup service
type Service struct {
conf *conf.Config
dao *newbiedao.Dao
}
// New fn
func New(c *conf.Config) (s *Service) {
s = &Service{
conf: c,
dao: newbiedao.New(c),
}
return s
}
// Ping fn
func (s *Service) Ping(c context.Context) (err error) {
return s.dao.Ping(c)
}
// Close dao
func (s *Service) Close() {
s.dao.Close()
}

View File

@@ -0,0 +1,22 @@
package newbie
import (
"flag"
"go-common/app/interface/main/growup/conf"
"os"
"testing"
)
var (
s *Service
)
func TestMain(m *testing.M) {
flag.Set("conf", "../../cmd/growup-interface.toml")
flag.Parse()
if err := conf.Init(); err != nil {
panic(err)
}
s = New(conf.Conf)
os.Exit(m.Run())
}

View File

@@ -0,0 +1,27 @@
package service
import (
"context"
"fmt"
"go-common/app/interface/main/growup/model"
)
// LatestNotice latest notice
func (s *Service) LatestNotice(c context.Context, platform int) (notice *model.Notice, err error) {
return s.dao.LatestNotice(c, platform)
}
// GetNotices get notice
func (s *Service) GetNotices(c context.Context, typ int, platform int, offset, limit int) (notices []*model.Notice, total int64, err error) {
typStr := ""
if typ > 0 {
typStr = fmt.Sprintf("type=%d AND", typ)
}
total, err = s.dao.NoticeCount(c, typStr, platform)
if err != nil {
return
}
notices, err = s.dao.Notices(c, typStr, platform, offset, limit)
return
}

View File

@@ -0,0 +1,21 @@
package service
import (
"context"
"testing"
. "github.com/smartystreets/goconvey/convey"
)
func Test_GetNotices(t *testing.T) {
var (
typ = 0
platform = 1
offset, limit = 0, 10
)
Convey("GetNotices", t, WithService(func(s *Service) {
res, _, err := s.GetNotices(context.Background(), typ, platform, offset, limit)
So(err, ShouldBeNil)
So(len(res), ShouldBeGreaterThan, 0)
}))
}

View File

@@ -0,0 +1,35 @@
package service
import (
"context"
"go-common/app/interface/main/growup/conf"
"go-common/app/interface/main/growup/dao"
)
// Service is growup service
type Service struct {
conf *conf.Config
dao *dao.Dao
sf *SnowFlake
}
// New fn
func New(c *conf.Config) (s *Service) {
s = &Service{
conf: c,
dao: dao.New(c),
sf: NewSnowFlake(),
}
return s
}
// Ping fn
func (s *Service) Ping(c context.Context) (err error) {
return s.dao.Ping(c)
}
// Close dao
func (s *Service) Close() {
s.dao.Close()
}

View File

@@ -0,0 +1,65 @@
package service
import (
"context"
"flag"
"path/filepath"
"testing"
"time"
"go-common/app/interface/main/growup/conf"
. "github.com/smartystreets/goconvey/convey"
)
var (
srv *Service
)
func init() {
dir, _ := filepath.Abs("../cmd/growup-interface.toml")
flag.Set("conf", dir)
conf.Init()
srv = New(conf.Conf)
time.Sleep(time.Second)
}
func WithService(f func(s *Service)) func() {
return func() {
// Reset(func() { CleanCache() })
f(srv)
}
}
func Test_GetUpStatus(t *testing.T) {
var (
mid = int64(1011)
)
Convey("interface", t, WithService(func(s *Service) {
_, err := s.GetUpStatus(context.Background(), mid, "127.0.0.1")
So(err, ShouldBeNil)
}))
}
func Test_JoinAv(t *testing.T) {
var (
accountType = 1
mid = int64(1011)
signType = 2
)
Convey("interface", t, WithService(func(s *Service) {
err := s.JoinAv(context.Background(), accountType, mid, signType)
So(err, ShouldBeNil)
}))
}
func Test_Quit(t *testing.T) {
var (
mid = int64(1011)
reason = "quit"
)
Convey("interface", t, WithService(func(s *Service) {
err := s.Quit(context.Background(), mid, reason)
So(err, ShouldBeNil)
}))
}

View File

@@ -0,0 +1,65 @@
package service
import (
"errors"
"sync"
"time"
)
const (
// MaxSequence max sequence in one time
MaxSequence = 1000
// WorkerBit worker bit
WorkerBit = 10
// SequenceBit sequence bit
SequenceBit = 10
)
// SnowFlake snow flake
type SnowFlake struct {
sync.Mutex
lastTimestamp int64
sequence int64
workerID int64
}
// NewSnowFlake new
func NewSnowFlake() *SnowFlake {
return &SnowFlake{
workerID: time.Now().UnixNano() % 1000,
}
}
// Generate generate
func (s *SnowFlake) Generate() (int64, error) {
s.Lock()
defer s.Unlock()
now := time.Now().UnixNano() / 1e6
if now == s.lastTimestamp {
s.sequence = (s.sequence + 1) % MaxSequence
if s.sequence == 0 {
now = s.waitNextMill(now)
}
} else {
s.sequence = 0
}
if now < s.lastTimestamp {
return 0, errors.New("inner time error")
}
s.lastTimestamp = now
return s.generate(), nil
}
func (s *SnowFlake) generate() int64 {
return (s.lastTimestamp << (WorkerBit + SequenceBit)) | (s.workerID << SequenceBit) | s.sequence
}
func (s *SnowFlake) waitNextMill(t int64) int64 {
for t == s.lastTimestamp {
time.Sleep(100 * time.Microsecond)
t = time.Now().UnixNano() / 1e6
}
return t
}

View File

@@ -0,0 +1,381 @@
package service
import (
"context"
"fmt"
"sort"
"time"
"go-common/app/interface/main/growup/model"
"go-common/library/ecode"
"go-common/library/log"
xtime "go-common/library/time"
)
const (
ceilings = 50000
rule = 1
detail = 3
question = 5
answer = 6
)
// GetAwardUpStatus get award up status
func (s *Service) GetAwardUpStatus(c context.Context, awardID, mid int64) (status *model.AwardUpStatus, err error) {
joined, err := s.isJoined(c, mid, awardID)
if err != nil {
return
}
fans, err := s.getUpFans(c, mid)
if err != nil {
return
}
status = &model.AwardUpStatus{
Joined: joined,
Qualified: checkQualification(fans),
}
return
}
// GetWinningRecord get winning record
func (s *Service) GetWinningRecord(c context.Context, mid int64) (rs []*model.WinningRecord, err error) {
awardIDs, err := s.dao.GetAwardJoinRecord(c, mid)
if err != nil {
return
}
if len(awardIDs) == 0 {
return
}
as := make([]int64, 0)
for awardID := range awardIDs {
as = append(as, awardID)
}
awards, err := s.dao.JoinedSpecialAwards(c, as)
if err != nil {
return
}
sort.Slice(awards, func(i, j int) bool {
return awards[i].CycleStart < awards[j].CycleStart
})
// am map[award_id]prize_id
am, err := s.dao.AwardIDsByWinner(c, mid)
if err != nil {
return
}
now := time.Now().Unix()
rs = make([]*model.WinningRecord, 0)
for _, award := range awards {
// in selection
if now > int64(award.CycleEnd) && award.OpenStatus == 1 {
rs = append(rs, &model.WinningRecord{
AwardID: award.AwardID,
AwardName: award.AwardName,
State: 2,
})
continue
}
// finished
if award.OpenStatus == 2 {
wr := &model.WinningRecord{
AwardID: award.AwardID,
AwardName: award.AwardName,
}
if prizeID, ok := am[award.AwardID]; ok {
wr.PrizeID = prizeID
wr.State = 1
}
rs = append(rs, wr)
}
}
return
}
// GetWinningPoster get prize winning poster
func (s *Service) GetWinningPoster(c context.Context, mid int64, awardID, prizeID int64) (poster *model.Poster, err error) {
accs, err := s.dao.AccountInfos(c, []int64{mid})
if err != nil {
return
}
if len(accs) <= 0 {
return
}
award, err := s.dao.GetAwardSchedule(c, awardID)
if err != nil {
return
}
// am map[award_id]division_name
names, err := s.dao.DivisionName(c, mid)
if err != nil {
return
}
bonus, err := s.dao.AwardBonus(c, awardID, prizeID)
if err != nil {
return
}
poster = &model.Poster{
AwardName: award.AwardName,
Nickname: accs[mid].Nickname,
Face: accs[mid].Face,
PrizeName: fmt.Sprintf("最佳%s新秀奖", names[awardID]),
Date: award.CycleEnd.Time().Format("2006-01"),
Bonus: bonus,
}
return
}
// JoinAward sign up award
func (s *Service) JoinAward(c context.Context, mid int64, awardID int64) (err error) {
joined, err := s.isJoined(c, mid, awardID)
if err != nil {
return
}
if joined {
err = ecode.GrowupSpecialAwardJoined
return
}
fans, err := s.getUpFans(c, mid)
if err != nil {
return
}
if !checkQualification(fans) {
err = ecode.GrowupSpecialAwardUnqualified
return
}
_, err = s.dao.AddToAwardRecord(c, mid, awardID)
return
}
// if fans count >= ceilings, no qualification
func checkQualification(fans int64) bool {
return fans < ceilings
}
// if joined special award
func (s *Service) isJoined(c context.Context, mid, awardID int64) (joined bool, err error) {
count, err := s.dao.JoinedCount(c, mid, awardID)
if err != nil {
return
}
joined = count != 0
return
}
// AwardList award_id: award_name
func (s *Service) AwardList(c context.Context) (as []*model.SimpleSpecialAward, err error) {
as, err = s.dao.PastAwards(c)
if err != nil {
return
}
sort.Slice(as, func(i, j int) bool {
return as[i].CycleStart < as[j].CycleStart
})
return
}
// Winners get winners by award id
func (s *Service) Winners(c context.Context, awardID int64) (as []*model.Account, err error) {
mids, err := s.dao.GetWinners(c, awardID)
if err != nil {
return
}
infos, err := s.dao.AccountInfos(c, mids)
if err != nil {
return
}
for _, mid := range mids {
a := &model.Account{Mid: mid}
as = append(as, a)
if info, ok := infos[mid]; ok {
a.Name = info.Nickname
a.Face = info.Face
}
}
return
}
// AwardDetail get award detail include schedule & resource
func (s *Service) AwardDetail(c context.Context, awardID int64) (data map[string]interface{}, err error) {
schedule, err := s.dao.GetAwardSchedule(c, awardID)
if err != nil {
return
}
// rs map[resource_type]map[index]content
rs, err := s.dao.GetResources(c, awardID)
if err != nil {
return
}
qas := make([]*model.QA, len(rs[question]))
for i := 0; i < len(rs[question]); i++ {
qas[i] = &model.QA{}
}
res := map[string]interface{}{
"qa": qas,
"rule": "",
"detail": "",
}
for rt, cs := range rs {
if rt == rule {
res["rule"] = cs[1]
}
if rt == detail {
res["detail"] = cs[1]
}
if rt == question || rt == answer {
for index, content := range cs {
if index < 0 || index > len(qas) {
continue
}
qa := qas[index-1]
if rt == question {
qa.Question = content
}
if rt == answer {
qa.Answer = content
}
}
}
}
data = map[string]interface{}{
"schedule": schedule,
"resource": res,
}
return
}
// SpecialAwardInfo special award info
func (s *Service) SpecialAwardInfo(c context.Context, mid int64) (data map[string]interface{}, err error) {
var (
nowTime = xtime.Time(time.Now().Unix())
winRecord []string
upStates []*model.UpAwardState
)
awards, nowAward, nextAward, err := s.getRecentSpecialAward(c, nowTime)
if err != nil {
log.Error("s.getRecentSpecialAward error(%v)", err)
return
}
if mid > 0 {
winRecord, err = s.getAwardWinRecord(c, mid, awards)
if err != nil {
log.Error("s.getAwardWinRecord error(%v)", err)
return
}
upStates, err = s.getUpAwardState(c, mid, awards)
if err != nil {
log.Error("s.getUpAwardState error(%v)", err)
return
}
}
data = map[string]interface{}{
"win_record": winRecord,
"now": nowAward,
"next": nextAward,
"up_states": upStates,
}
return
}
// get now and next special award
func (s *Service) getRecentSpecialAward(c context.Context, nowTime xtime.Time) (awards []*model.SpecialAward, nowAward, nextAward *model.SpecialAward, err error) {
awards, err = s.dao.GetSpecialAwards(c)
if err != nil {
log.Error("s.dao.GetSpecialAwards error(%v)", err)
return
}
sort.Slice(awards, func(i, j int) bool {
return awards[i].CycleStart < awards[j].CycleStart
})
for i := 0; i < len(awards); i++ {
if awards[i].CycleStart <= nowTime && awards[i].CycleEnd >= nowTime {
nowAward = awards[i]
} else if awards[i].CycleStart > nowTime {
nextAward = awards[i]
break
}
}
if nowAward != nil {
nowAward.Duration = int64(nowAward.CycleEnd - nowTime)
nowAward.Divisions, err = s.dao.GetSpecialAwardDivision(c, nowAward.AwardID)
if err != nil {
return
}
}
if nextAward != nil {
nextAward.Duration = int64(nextAward.CycleStart - nowTime)
nextAward.Divisions, err = s.dao.GetSpecialAwardDivision(c, nextAward.AwardID)
if err != nil {
return
}
}
return
}
func (s *Service) getAwardWinRecord(c context.Context, mid int64, awards []*model.SpecialAward) (awardNames []string, err error) {
awardIDs, err := s.dao.GetAwardWinRecord(c, mid)
if err != nil {
log.Error("s.dao.GetAwardWinRecord error(%v)", err)
return
}
awardNames = make([]string, 0)
for i := len(awards) - 1; i >= 0; i-- {
if awardIDs[awards[i].AwardID] {
awardNames = append(awardNames, awards[i].AwardName)
}
}
return
}
func (s *Service) getUpAwardState(c context.Context, mid int64, awards []*model.SpecialAward) (upStates []*model.UpAwardState, err error) {
upStates = make([]*model.UpAwardState, 0)
now := xtime.Time(time.Now().Unix())
awardIDs, err := s.dao.GetAwardJoinRecord(c, mid)
if err != nil {
log.Error("s.dao.GetAwardJoinRecord error(%v)", err)
return
}
winIDs, err := s.dao.GetAwardWinRecord(c, mid)
if err != nil {
log.Error("s.dao.GetAwardWinRecord error(%v)", err)
return
}
for i := 0; i < len(awards); i++ {
upState := &model.UpAwardState{AwardName: awards[i].AwardName}
date := awards[i].AnnounceDate.Time()
doubleCreativeStart := time.Date(date.Year(), date.Month()+1, 15, 0, 0, 0, 0, time.Local)
doubleCreativeEnd := doubleCreativeStart.AddDate(0, 0, 14)
if now > awards[i].CycleEnd && now <= awards[i].AnnounceDate && awardIDs[awards[i].AwardID] { // 评选中
upState.State = 1
} else if now > awards[i].AnnounceDate && now < xtime.Time(doubleCreativeStart.Unix()) && winIDs[awards[i].AwardID] { // 双倍即将开始
upState.State = 2
} else if now >= xtime.Time(doubleCreativeStart.Unix()) && now <= xtime.Time(doubleCreativeEnd.Unix()) && winIDs[awards[i].AwardID] { // 双倍中
upState.State = 3
}
if upState.State != 0 {
upStates = append(upStates, upState)
}
}
return
}

View File

@@ -0,0 +1,98 @@
package service
import (
"context"
"fmt"
"time"
"go-common/app/interface/main/growup/model"
"go-common/library/database/sql"
"go-common/library/log"
xtime "go-common/library/time"
)
// UpBill get up bill
func (s *Service) UpBill(c context.Context, mid int64) (data interface{}, err error) {
redisKey := fmt.Sprintf("growup-up-bill-v1:%d", mid)
res, err := s.dao.GetIncomeCache(c, redisKey)
if err != nil {
log.Error("s.dao.GetIncomeCache error(%v)", err)
return
}
if res != nil {
data = res["data"]
return
}
data, err = s.upBill(c, mid)
if err != nil {
log.Error("s.upBill error(%v)", err)
return
}
err = s.dao.SetIncomeCache(c, redisKey, map[string]interface{}{"data": data})
if err != nil {
log.Error("s.dao.SetIncomeCache error(%v)", err)
}
return
}
func (s *Service) upBill(c context.Context, mid int64) (data interface{}, err error) {
up := &model.UpBill{}
// 判断up主是否在创作激励
signedAt, err := s.dao.GetUpSignedAt(c, "up_info_video", mid)
if err != nil {
log.Error("s.dao.GetUpSignedAt error(%v)", err)
return
}
if signedAt == 0 {
up.Join = false
data = up
return
}
up.Join = true
endAt := time.Date(2018, 10, 31, 0, 0, 0, 0, time.Local)
if signedAt >= xtime.Time(endAt.AddDate(0, 0, 1).Unix()) {
up.SignedAt = signedAt
up.EndAt = xtime.Time(endAt.Unix())
data = up
return
}
up, err = s.dao.GetUpBill(c, mid)
up.Join = true
if err == sql.ErrNoRows {
err = nil
up.SignedAt = signedAt
up.EndAt = xtime.Time(endAt.Unix())
data = up
return
}
if err != nil {
log.Error("s.dao.GetUpBill error(%v)", err)
return
}
title, err := s.getAvTitle(c, []int64{up.AvID})
if err != nil {
log.Error("s.getAvTitle error(%v)", err)
return
}
up.AvTitle = title[up.AvID]
upsMap, err := s.dao.AccountInfos(c, []int64{mid})
if err != nil {
log.Error("s.dao.AccountInfos error(%v)", err)
return
}
if up.Title == "流量王" {
up.Title = "激励101"
}
if info, ok := upsMap[mid]; ok {
up.Nickname = info.Nickname
up.Face = info.Face
}
data = up
return
}

View File

@@ -0,0 +1,290 @@
package service
import (
"context"
"time"
"go-common/app/interface/main/growup/model"
"go-common/library/database/sql"
"go-common/library/ecode"
"go-common/library/log"
"go-common/library/net/metadata"
xtime "go-common/library/time"
"golang.org/x/sync/errgroup"
)
func (s *Service) getUpFans(c context.Context, mid int64) (fans int64, err error) {
pfl, err := s.dao.ProfileWithStat(c, mid)
if err != nil {
return
}
fans = int64(pfl.Follower)
return
}
// GetUpStatus status of user in growup plan by mid
func (s *Service) GetUpStatus(c context.Context, mid int64, ip string) (status *model.UpStatus, err error) {
id, err := s.dao.Blocked(c, mid)
if err != nil {
log.Error("s.dao.Blocked mid(%d) error(%v)", mid, err)
return
}
status = &model.UpStatus{}
status.Blocked = id != 0
if status.Blocked {
return
}
status.Status = make([]*model.BusinessStatus, 3)
var g errgroup.Group
g.Go(func() (err error) {
stat, err := s.dao.AvUpStatus(c, mid)
if err != nil {
log.Error("s.dao.AvUpStatus mid(%d) error(%v)", mid, err)
return
}
if stat.AccountState == 0 {
if stat.QuitAt.After(stat.CTime) {
stat.AccountState = 8
}
}
// check av threshold
if stat.AccountState == 3 {
stat.ShowPanel = true
} else {
stat.ShowPanel, err = s.checkAvStat(c, mid, ip)
if err != nil {
log.Error("s.checkAvStat mid(%d) error(%v)", mid, err)
return
}
}
stat.IsWhite = true
stat.Type = 0
status.Status[0] = stat
return
})
g.Go(func() (err error) {
stat, err := s.dao.ColumnUpStatus(c, mid)
if err != nil {
log.Error("s.dao.ColumnUpStatus mid(%d) error(%v)", mid, err)
return
}
stat.IsWhite = true
if stat.AccountState == 3 {
stat.ShowPanel = true
} else {
stat.ShowPanel, err = s.checkArticleStat(c, mid, ip)
if err != nil {
log.Error("s.checkArticleStat mid(%d) error(%v)", mid, err)
return
}
}
stat.Type = 1
status.Status[1] = stat
return
})
g.Go(func() (err error) {
stat, err := s.dao.BgmUpStatus(c, mid)
if err != nil {
return
}
if stat.AccountState == 3 {
stat.ShowPanel = true
} else {
stat.ShowPanel, err = s.checkBgmStat(c, mid)
if err != nil {
return
}
}
stat.Type = 2
status.Status[2] = stat
return
})
err = g.Wait()
return
}
func (s *Service) checkAvStat(c context.Context, mid int64, ip string) (ok bool, err error) {
identify, err := s.dao.UpBusinessInfos(c, mid)
if err != nil {
log.Error("s.dao.UpBusinessInfos mid(%d) error(%v)", mid, err)
return
}
if identify.Archive != 1 {
ok = false
return
}
stat, err := s.avStat(c, mid, ip)
if err != nil {
log.Error("s.dao.AvStat mid(%d) error(%v)", mid, err)
return
}
if stat.Fans >= s.conf.Threshold.LimitFanCnt || stat.View >= s.conf.Threshold.LimitTotalClick {
ok = true
} else {
ok = false
}
return
}
func (s *Service) checkArticleStat(c context.Context, mid int64, ip string) (ok bool, err error) {
stat, err := s.dao.ArticleStat(c, mid, ip)
if err != nil {
log.Error("s.dao.ArticleStat mid(%d) error(%v)", mid, err)
return
}
if stat.View >= s.conf.Threshold.LimitArticleView {
ok = true
} else {
ok = false
}
return
}
func (s *Service) checkBgmStat(c context.Context, mid int64) (ok bool, err error) {
count, err := s.dao.BgmUpCount(c, mid)
if err != nil {
return
}
if count > 0 {
ok = true
return
}
count, err = s.dao.BgmWhiteList(c, mid)
if err != nil {
return
}
if count > 0 {
ok = true
}
return
}
// JoinAv add user to growup plan (video)
func (s *Service) JoinAv(c context.Context, accountType int, mid int64, signType int) (err error) {
id, err := s.dao.Blocked(c, mid)
if err != nil {
log.Error("s.dao.GetBlocked mid(%d) error(%v)", mid, err)
return
}
if id != 0 {
log.Info("mid(%d) is blocked", mid)
return ecode.GrowupDisabled
}
ip := metadata.String(c, metadata.RemoteIP)
ok, err := s.checkAvStat(c, mid, ip)
if err != nil {
log.Error("s.checkAvStat mid(%d) error(%v)", mid, err)
return
}
if !ok {
log.Info("mid(%d) video not reach standard", mid)
return ecode.GrowupDisabled
}
nickname, categoryID, err := s.dao.CategoryInfo(c, mid)
if err != nil {
return
}
fans, err := s.dao.Fans(c, mid)
if err != nil {
return
}
state, err := s.dao.GetAccountState(c, "up_info_video", mid)
if err != nil {
return
}
// if account state is 2 3 4 5 6 7 return
if state >= 2 && state < 8 {
return
}
now := xtime.Time(time.Now().Unix())
// sign_type: 1.basic; 2.first publish; 0:default.
v := &model.UpInfo{
MID: mid,
Nickname: nickname,
AccountType: accountType,
MainCategory: categoryID,
Fans: fans,
AccountState: 2,
SignType: signType,
ApplyAt: now,
}
_, err = s.dao.InsertUpInfo(c, "up_info_video", "total_play_count", v)
return
}
// Quit user quit growup plan
func (s *Service) Quit(c context.Context, mid int64, reason string) (err error) {
var (
tx *sql.Tx
now = time.Now().Unix()
quitAt = xtime.Time(now)
expiredIn = xtime.Time(now + 86400*3)
)
if tx, err = s.dao.BeginTran(c); err != nil {
return
}
nickname, err := s.dao.Nickname(c, mid)
if err != nil {
return
}
current, err := s.dao.CreditScore(c, mid)
if err != nil {
return
}
_, err = s.dao.TxQuit(tx, "up_info_video", mid, quitAt, expiredIn, reason)
if err != nil {
tx.Rollback()
return
}
_, err = s.dao.TxQuit(tx, "up_info_column", mid, quitAt, expiredIn, reason)
if err != nil {
tx.Rollback()
return
}
_, err = s.dao.TxQuit(tx, "up_info_bgm", mid, quitAt, expiredIn, reason)
if err != nil {
tx.Rollback()
return
}
cr := &model.CreditRecord{
MID: mid,
OperateAt: xtime.Time(now),
Operator: nickname,
Reason: 5,
Deducted: 1,
Remaining: current - 1,
}
_, err = s.dao.TxInsertCreditRecord(tx, cr)
if err != nil {
tx.Rollback()
return
}
// quit deduct 1
_, err = s.dao.TxDeductCreditScore(tx, 1, mid)
if err != nil {
tx.Rollback()
return
}
if err = tx.Commit(); err != nil {
log.Error("tx.Commit error(%v)", err)
}
return
}

View File

@@ -0,0 +1,204 @@
package service
import (
"context"
"fmt"
"time"
"go-common/library/log"
xtime "go-common/library/time"
)
var (
_upLevel = map[int]string{
0: "光腚激大励",
1: "奶瓶激大励",
2: "幼稚园激大励",
3: "小学生激大励",
4: "中学生激大励",
5: "社会人激大励",
6: "筋肉激大励",
}
_downshiftUps = map[int64]bool{
148834382: true,
3647169: true,
100238104: true,
42932619: true,
331782289: true,
40305856: true,
19419058: true,
4397552: true,
234891605: true,
310562646: true,
11793131: true,
316737962: true,
5326066: true,
669622: true,
312663583: true,
34028169: true,
288220100: true,
344838590: true,
25729281: true,
96506011: true,
312508662: true,
8888364: true,
238674714: true,
337061658: true,
321230684: true,
337376429: true,
85738972: true,
10094840: true,
291595967: true,
300676862: true,
298484242: true,
40426408: true,
2336206: true,
78839625: true,
291682397: true,
300576557: true,
61086273: true,
318078497: true,
291051169: true,
27174089: true,
274284147: true,
306198844: true,
}
)
// UpYear up year
func (s *Service) UpYear(c context.Context, mid int64) (data interface{}, err error) {
redisKey := fmt.Sprintf("growup-up-year:%d", mid)
res, err := s.dao.GetIncomeCache(c, redisKey)
if err != nil {
log.Error("s.dao.GetIncomeCache error(%v)", err)
return
}
if res != nil {
data = res["data"]
return
}
data, err = s.upYear(c, mid)
if err != nil {
log.Error("s.upYear error(%v)", err)
return
}
err = s.dao.SetIncomeCache(c, redisKey, map[string]interface{}{"data": data})
if err != nil {
log.Error("s.dao.SetIncomeCache error(%v)", err)
}
return
}
func (s *Service) upYear(c context.Context, mid int64) (data interface{}, err error) {
up := new(struct {
Name string `json:"name"`
Face string `json:"face"`
IsJoin bool `json:"is_join"`
SignedAt xtime.Time `json:"signed_at"`
FirstTime xtime.Time `json:"first_time"`
HasIncome bool `json:"has_income"`
Title string `json:"title"`
Level int `json:"level"`
TagIncome []int64 `json:"tag_income"`
})
defer func() {
data = up
}()
// has signed
up.SignedAt, err = s.getUpFirstSignedAt(c, mid)
if err != nil {
log.Error("s.dao.getUpFirstSignedAt error(%v)", err)
return
}
if up.SignedAt == 0 {
up.IsJoin = false
return
}
up.IsJoin = true
// first income
up.FirstTime, err = s.dao.GetFirstUpIncome(c, mid)
if err != nil {
log.Error("s.dao.GetFirstUpIncome error(%v)", err)
return
}
if up.FirstTime != 0 {
up.HasIncome = true
}
earliestTime := xtime.Time(time.Date(2018, 2, 1, 0, 0, 0, 0, time.Local).Unix())
if up.SignedAt < earliestTime {
up.SignedAt = earliestTime
}
if up.FirstTime < earliestTime {
up.FirstTime = earliestTime
}
// tag income
var totalIncome int64
up.TagIncome, totalIncome, err = s.dao.GetUpYearTag(c, mid)
if err != nil {
log.Error("s.dao.GetUpYearTag error(%v)", err)
return
}
upInfos, err := s.dao.AccountInfos(c, []int64{mid})
if err != nil {
log.Error("s.dao.AccountInfos error(%v)", err)
return
}
if info, ok := upInfos[mid]; ok {
up.Name = info.Nickname
up.Face = info.Face
}
switch {
case totalIncome == 0:
up.Level = 0
case totalIncome > 0 && totalIncome <= 10000:
up.Level = 1
case totalIncome > 10000 && totalIncome <= 100000:
up.Level = 2
case totalIncome > 100000 && totalIncome <= 1000000:
up.Level = 3
case totalIncome > 1000000 && totalIncome <= 5000000:
up.Level = 4
case totalIncome > 5000000 && totalIncome <= 10000000:
up.Level = 5
case totalIncome > 10000000:
up.Level = 6
}
if up.Level > 0 && _downshiftUps[mid] {
up.Level--
}
up.Title = _upLevel[up.Level]
return
}
func (s *Service) getUpFirstSignedAt(c context.Context, mid int64) (signedAt xtime.Time, err error) {
video, err := s.dao.GetUpSignedAt(c, "up_info_video", mid)
if err != nil {
return
}
signedAt = video
column, err := s.dao.GetUpSignedAt(c, "up_info_column", mid)
if err != nil {
return
}
if column != 0 && (signedAt > column || signedAt == 0) {
signedAt = column
}
bgm, err := s.dao.GetUpSignedAt(c, "up_info_bgm", mid)
if err != nil {
return
}
if bgm != 0 && (signedAt > bgm || signedAt == 0) {
signedAt = bgm
}
return
}

View File

@@ -0,0 +1,225 @@
package service
import (
"context"
"fmt"
"strconv"
"time"
"go-common/app/interface/main/growup/model"
"go-common/library/log"
)
var (
_withdrawing = 1 // 处理中
_withdrawSuccess = 2 // 提现成功
// _withdrawFail = 3 // 提现失败
)
// GetWithdraw get up withdraw
func (s *Service) GetWithdraw(c context.Context, dateVersion string, from, limit int) (count int, withdrawVos []*model.WithdrawVo, err error) {
count, upAccounts, err := s.UpWithdraw(c, dateVersion, from, limit)
if err != nil {
log.Error("s.UpWithdraw error(%v)", err)
return
}
mids := make([]int64, len(upAccounts))
for i, up := range upAccounts {
mids[i] = up.MID
}
withdrawVos = make([]*model.WithdrawVo, 0)
if len(mids) == 0 {
return
}
upIncomeWithdrawMap, err := s.dao.QueryUpWithdrawByMids(c, mids, dateVersion)
if err != nil {
log.Error("s.dao.QueryUpWithdrawByMids error(%v)", err)
return
}
for _, up := range upAccounts {
if upIncomeWithdraw, ok := upIncomeWithdrawMap[up.MID]; ok && upIncomeWithdraw.State == _withdrawing {
vo := &model.WithdrawVo{
MID: up.MID,
ThirdCoin: float64(up.TotalUnwithdrawIncome) * float64(0.01),
ThirdOrderNo: strconv.FormatInt(upIncomeWithdraw.ID, 10),
CTime: time.Unix(int64(upIncomeWithdraw.CTime), 0).Format("2006-01-02 15:04:05"),
NotifyURL: "http://up-profit.bilibili.co/allowance/api/x/internal/growup/up/withdraw/success",
}
withdrawVos = append(withdrawVos, vo)
}
}
return
}
// UpWithdraw get up withdraw
func (s *Service) UpWithdraw(c context.Context, dateVersion string, from, limit int) (count int, upAccounts []*model.UpAccount, err error) {
count, err = s.dao.GetUpAccountCount(c, dateVersion)
if err != nil {
log.Error("s.dao.GetUpAccountCount error(%v)", err)
return
}
if count <= 0 {
return
}
upAccounts, err = s.dao.QueryUpAccountByDate(c, dateVersion, from, limit)
if err != nil {
log.Error("s.dao.QueryUpAccountByDate error(%v)", err)
return
}
if len(upAccounts) == 0 {
return
}
mids := make([]int64, len(upAccounts))
for i, up := range upAccounts {
mids[i] = up.MID
}
// get up_income_withdraw by mids and date
upIncomeWithdrawMap, err := s.dao.QueryUpWithdrawByMids(c, mids, dateVersion)
if err != nil {
log.Error("s.dao.QueryUpWithdrawByMids error(%v)", err)
return
}
for _, up := range upAccounts {
if _, ok := upIncomeWithdrawMap[up.MID]; !ok {
upIncomeWithdraw := &model.UpIncomeWithdraw{
MID: up.MID,
WithdrawIncome: up.TotalUnwithdrawIncome,
DateVersion: dateVersion,
State: _withdrawing,
}
err = s.InsertUpWithdrawRecord(c, upIncomeWithdraw)
if err != nil {
log.Error("s.InsertUpWithdrawRecord error(%v)", err)
return
}
}
}
return
}
// InsertUpWithdrawRecord insert up_withdraw_income record
func (s *Service) InsertUpWithdrawRecord(c context.Context, upIncomeWithdraw *model.UpIncomeWithdraw) (err error) {
result, err := s.dao.InsertUpWithdrawRecord(c, upIncomeWithdraw)
if err != nil {
log.Error("s.dao.InsertUpIncomeWithdraw error(%v)", err)
return
}
if result < 1 {
log.Error("s.dao.InsertUpIncomeWithdraw error mid(%d), dateVersion(%s)", upIncomeWithdraw.MID, upIncomeWithdraw.DateVersion)
return
}
return
}
// WithdrawSuccess withdraw success callback
func (s *Service) WithdrawSuccess(c context.Context, orderNo int64, tradeStatus int) (err error) {
upWithdraw, err := s.dao.QueryUpWithdrawByID(c, orderNo)
if err != nil {
log.Error("s.dao.QueryUpWithdrawByID error(%v)", err)
return
}
if tradeStatus != _withdrawSuccess {
log.Info("param tradeStatus(%d) != withdraw success(2)", tradeStatus)
return
}
if upWithdraw.State == _withdrawSuccess {
log.Info("withdraw has successed already")
return
}
tx, err := s.dao.BeginTran(c)
if err != nil {
log.Error("s.dao.BeginTran error(%v)", err)
return
}
// update up_income_withdraw state
rows, err := s.dao.TxUpdateUpWithdrawState(tx, orderNo, _withdrawSuccess)
if err != nil {
tx.Rollback()
log.Error("s.dao.UpdateUpWithdrawState error(%v)", err)
return
}
if rows != 1 {
tx.Rollback()
log.Error("s.dao.UpdateUpWithdrawState Update withdraw record error id(%d)", orderNo)
return
}
// update up_account withdraw
rows, err = s.dao.TxUpdateUpAccountWithdraw(tx, upWithdraw.MID, upWithdraw.WithdrawIncome)
if err != nil {
tx.Rollback()
log.Error("s.dao.UpdateUpAccountWithdraw error(%v)", err)
return
}
if rows != 1 {
tx.Rollback()
log.Error("s.dao.UpdateUpAccountWithdraw Update up account record error id(%d)", orderNo)
return
}
maxUpWithdrawDateVersion, err := s.dao.TxQueryMaxUpWithdrawDateVersion(tx, upWithdraw.MID)
if err != nil {
tx.Rollback()
log.Error("s.dao.QueryMaxUpWithdrawDateVersion error(%v)", err)
return
}
time := 0
var version int64
for {
version, err = s.dao.TxQueryUpAccountVersion(tx, upWithdraw.MID)
if err != nil {
tx.Rollback()
log.Error("s.dao.QueryUpAccountVersion error(%v)", err)
return
}
if maxUpWithdrawDateVersion == "" {
maxUpWithdrawDateVersion = upWithdraw.DateVersion
}
rows, err = s.dao.TxUpdateUpAccountUnwithdrawIncome(tx, upWithdraw.MID, maxUpWithdrawDateVersion, version)
if err != nil {
tx.Rollback()
log.Error("s.dao.UpdateUpAccountUnwithdrawIncome error(%v)", err)
return
}
if rows == 1 {
if err = tx.Commit(); err != nil {
log.Error("tx.Commit error")
return err
}
break
}
time++
if time >= 10 {
tx.Rollback()
log.Info("try to synchronize unwithdraw income 10 times error mid(%d)", upWithdraw.MID)
err = fmt.Errorf("try to synchronize unwithdraw income 10 times error mid(%d)", upWithdraw.MID)
break
}
}
return
}
// WithdrawDetail get withdraw detail
func (s *Service) WithdrawDetail(c context.Context, mid int64) (upWithdraws []*model.UpIncomeWithdraw, err error) {
return s.dao.QueryUpWithdrawByMID(c, mid)
}

View File

@@ -0,0 +1,66 @@
package service
import (
"context"
"testing"
"go-common/app/interface/main/growup/model"
. "github.com/smartystreets/goconvey/convey"
)
func Test_GetWithdraw(t *testing.T) {
var (
dateVersion = "2018-04"
offset, limit = 0, 10
)
Convey("GetWithdraw", t, WithService(func(s *Service) {
_, res, err := s.GetWithdraw(context.Background(), dateVersion, offset, limit)
So(err, ShouldBeNil)
So(len(res), ShouldBeGreaterThan, 0)
}))
}
func Test_UpWithdraw(t *testing.T) {
var (
dateVersion = "2018-04"
offset, limit = 0, 10
)
Convey("UpWithdraw", t, WithService(func(s *Service) {
_, res, err := s.UpWithdraw(context.Background(), dateVersion, offset, limit)
So(err, ShouldBeNil)
So(len(res), ShouldBeGreaterThan, 0)
}))
}
func Test_InsertUpWithdrawRecord(t *testing.T) {
var (
a = &model.UpIncomeWithdraw{MID: int64(1001), WithdrawIncome: 10, DateVersion: "2018-05", State: 1}
)
Convey("Test_InsertUpWithdrawRecord", t, WithService(func(s *Service) {
err := s.InsertUpWithdrawRecord(context.Background(), a)
So(err, ShouldBeNil)
}))
}
func Test_WithdrawSuccess(t *testing.T) {
var (
orderNo int64 = 10
tradeStatus = 10
)
Convey("WithdrawSuccess", t, WithService(func(s *Service) {
err := s.WithdrawSuccess(context.Background(), orderNo, tradeStatus)
So(err, ShouldBeNil)
}))
}
func Test_WithdrawDetail(t *testing.T) {
var (
mid int64 = 10
)
Convey("WithdrawDetail", t, WithService(func(s *Service) {
res, err := s.WithdrawDetail(context.Background(), mid)
So(err, ShouldBeNil)
So(len(res), ShouldBeGreaterThan, 0)
}))
}