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,77 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_test",
"go_library",
)
go_test(
name = "go_default_test",
srcs = [
"answer_test.go",
"answercheck_test.go",
"common_test.go",
"geetest_test.go",
"service_test.go",
"user_info_test.go",
],
embed = [":go_default_library"],
rundir = ".",
tags = ["automanaged"],
deps = [
"//app/interface/main/answer/conf:go_default_library",
"//vendor/github.com/smartystreets/goconvey/convey:go_default_library",
],
)
go_library(
name = "go_default_library",
srcs = [
"answer.go",
"answercheck.go",
"common.go",
"geetest.go",
"rank_share.go",
"service.go",
"user_info.go",
],
importpath = "go-common/app/interface/main/answer/service",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/interface/main/answer/conf:go_default_library",
"//app/interface/main/answer/dao:go_default_library",
"//app/interface/main/answer/dao/account:go_default_library",
"//app/interface/main/answer/dao/geetest:go_default_library",
"//app/interface/main/answer/model:go_default_library",
"//app/service/main/account/api:go_default_library",
"//app/service/main/member/api/gorpc:go_default_library",
"//app/service/main/member/model:go_default_library",
"//library/ecode:go_default_library",
"//library/log:go_default_library",
"//library/log/anticheat:go_default_library",
"//library/log/infoc:go_default_library",
"//library/net/metadata:go_default_library",
"//library/queue/databus/report:go_default_library",
"//library/stat/prom:go_default_library",
"//library/sync/pipeline/fanout:go_default_library",
"//library/text/translate/chinese:go_default_library",
"//vendor/github.com/pkg/errors:go_default_library",
"@org_golang_x_net//context:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,819 @@
package service
import (
"crypto/md5"
"encoding/hex"
"encoding/json"
"fmt"
"math"
"math/rand"
"strconv"
"strings"
"time"
"unicode/utf8"
"go-common/app/interface/main/answer/model"
"go-common/library/ecode"
"go-common/library/log"
"go-common/library/text/translate/chinese"
"github.com/pkg/errors"
"golang.org/x/net/context"
)
const (
_hashSalt = "bilirqeust"
// _ansURI = "/answer/img?qs_id=%v&ans1_hash=%s&ans2_hash=%s&ans3_hash=%s&ans4_hash=%s"
_baseTypeID = 36 // 官方基础题库
_rankBtn = 7 * 24 * time.Hour
_minType = 3
_maxType = 10
)
var (
_typeIdsMapping = map[int][]int{
100001: {15, 16, 17},
100002: {29, 30},
100003: {12, 13},
// 22: 21,
// 24: 23,
// 35: 31, 36: 31,
// 32: 30, 33: 30, 34: 30,
// 29: 28, 37: 28, 7: 28, 8: 28,
// 41: 5, 42: 5, 43: 5, 44: 5, 45: 5, 46: 5, 47: 5, 48: 5, 49: 5, 50: 5, 51: 5, 52: 5,
}
// 对外展示方式1
_typeMap1 = []*model.TypeInfo{
{Name: "游戏", Subs: []*model.SubType{
{ID: 8, Name: "动作射击"},
{ID: 9, Name: "冒险格斗"},
{ID: 100003, Name: "策略模拟"},
// {ID: 13, Name: "策略模拟"},
{ID: 14, Name: "音乐体育"},
}},
{Name: "影视", Subs: []*model.SubType{
{ID: 15, Name: "纪录片"},
{ID: 16, Name: "电影"},
{ID: 17, Name: "电视剧"},
}},
{Name: "科技", Subs: []*model.SubType{
{ID: 18, Name: "军事"},
{ID: 19, Name: "地理"},
{ID: 20, Name: "历史"},
{ID: 21, Name: "文学"},
{ID: 22, Name: "数学"},
{ID: 23, Name: "物理"},
{ID: 24, Name: "化学"},
{ID: 25, Name: "生物"},
{ID: 26, Name: "数码科技"},
}},
{Name: "动画", Subs: []*model.SubType{
{ID: 27, Name: "国创"},
{ID: 28, Name: "番剧"},
}},
{Name: "艺术", Subs: []*model.SubType{
{ID: 100002, Name: "音乐"},
// {ID: 30, Name: "音乐"},
{ID: 31, Name: "绘画"},
}},
{Name: "流行前线", Subs: []*model.SubType{
{ID: 32, Name: "娱乐"},
{ID: 33, Name: "时尚"},
{ID: 34, Name: "运动"},
}},
{Name: "鬼畜", Subs: []*model.SubType{
{ID: 35, Name: "鬼畜"},
}},
}
// 推荐分区映射
_recTypeIDMap = map[int]map[string][]int{
124: {"main_tid": []int{1, 167}, "sub_tid": []int{3, 129}},
127: {"main_tid": []int{1, 167}, "sub_tid": []int{3, 4}},
126: {"main_tid": []int{3, 119}, "sub_tid": []int{1}},
123: {"main_tid": []int{3}, "sub_tid": []int{129, 119}},
121: {"main_tid": []int{36, 177}, "sub_tid": []int{160}},
125: {"main_tid": []int{4}, "sub_tid": []int{129}},
129: {"main_tid": []int{36, 177}, "sub_tid": []int{160}},
130: {"main_tid": []int{23, 11}, "sub_tid": []int{160}},
128: {"main_tid": []int{119}, "sub_tid": []int{160}},
}
)
// BaseQ base question.
func (s *Service) BaseQ(c context.Context, mid int64, lang string, mobile bool) (res *model.AnsQueDetailList, err error) {
var aqs *model.AnsQuesList
if aqs, err = s.BaseQs(c, mid, lang, mobile); err != nil {
err = errors.Wrapf(err, "s.ansRPC.BaseQs(%d,%t)", mid, mobile)
return
}
res = s.convertModel(aqs)
return
}
// BaseQs get base question
func (s *Service) BaseQs(c context.Context, mid int64, lang string, mobile bool) (rqs *model.AnsQuesList, err error) {
var (
ids []int64
now = time.Now()
)
if s.checkAnswerBlock(c, mid) {
err = ecode.AnswerBlock
return
}
h, err := s.history(c, mid)
if err == nil && h != nil {
if h.StartTime.Add(s.answerDuration()).After(now) && h.Score == 0 {
if h.StepExtraCompleteTime != 0 {
err = ecode.AnswerProNoPass // extra question pass
return
}
err = ecode.AnswerExtraNoPass
return
}
if h.Score > 0 && h.IsPassCaptcha == 0 {
err = ecode.AnswerCaptchaNoPassed
return
}
}
ids, err = s.answerDao.IdsCache(c, mid, model.Q)
if err != nil || len(ids) != s.c.Answer.BaseNum {
ids, err = s.answerDao.QidByType(c, _baseTypeID, uint8(s.c.Answer.BaseNum))
if err != nil {
log.Error("s.answerDao.QidByType(%d,%d) error(%v)", _baseTypeID, s.c.Answer.BaseNum, err)
return
}
if len(ids) == 0 {
err = ecode.AnswerQsNumErr
log.Error("qidByType ids len(%d) is 0", len(ids))
return
}
}
rqs, err = s.concatData(c, mid, ids, lang, mobile, s.c.Answer.BaseNum)
if err != nil {
log.Error("BaseQs s.concatData(%d, %d, %d) error(%v)", c, mid, ids, err)
return
}
at := &model.AnswerTime{
Stime: now,
Etimes: 0,
}
err = s.answerDao.SetExpireCache(c, mid, at)
rqs.CurrentTime = at.Stime
rqs.EndTime = at.Stime.Add(s.answerDuration())
s.answerDao.DelIdsCache(c, mid, model.BaseExtraPassQ)
s.answerDao.DelIdsCache(c, mid, model.BaseExtraNoPassQ)
return
}
// ConvertExtraQs extra question.
func (s *Service) ConvertExtraQs(c context.Context, mid int64, lang string, mobile bool) (res *model.AnsQueDetailList, err error) {
var ans *model.AnsQuesList
if ans, err = s.ExtraQs(c, mid, lang, mobile); err != nil {
err = errors.Wrapf(err, "s.ansRPC.ExtraQues(%d,%t)", mid, mobile)
return
}
res = s.convertExtraModel(ans)
return
}
// ExtraQs extra question.
func (s *Service) ExtraQs(c context.Context, mid int64, lang string, mobile bool) (rqs *model.AnsQuesList, err error) {
var (
ids []int64
passids []int64
npassids []int64
now = time.Now()
)
if s.checkAnswerBlock(c, mid) {
err = ecode.AnswerBlock
return
}
h, err := s.checkExtraState(c, mid, now)
if err != nil {
return
}
// keep on answer
passids, _ = s.answerDao.IdsCache(c, mid, model.BaseExtraPassQ)
npassids, err = s.answerDao.IdsCache(c, mid, model.BaseExtraNoPassQ)
if err != nil || len(passids) != s.c.Answer.BaseExtraPassNum || len(npassids) != s.c.Answer.BaseExtraNoPassNum {
var (
ok bool
)
ok, passids, npassids = s.extraQueByBigData(c, mid, "")
if !ok {
// if bigdata get extra mid faild
passids, err = s.answerDao.ExtraQidByType(c, model.BaseExtraPassQ, uint8(s.c.Answer.BaseExtraPassNum))
if err != nil {
log.Error("s.answerDao.ExtraQidByType(%d, %d, %d) error(%v)", model.BaseExtraPassQ, s.c.Answer.BaseExtraPassNum, len(passids), err)
return
}
if len(passids) != s.c.Answer.BaseExtraPassNum {
err = ecode.AnswerQsNumErr
log.Warn("passids lenth(%d) neq BaseExtraPassNum(%d)", len(passids), s.c.Answer.BaseExtraPassNum)
return
}
npassids, err = s.answerDao.ExtraQidByType(c, model.BaseExtraNoPassQ, uint8(s.c.Answer.BaseExtraNoPassNum))
if err != nil {
log.Error("s.answerDao.ExtraQidByType(%d, %d, %d) error(%v)", model.BaseExtraNoPassQ, s.c.Answer.BaseExtraNoPassNum, len(npassids), err)
return
}
if len(npassids) != s.c.Answer.BaseExtraNoPassNum {
err = ecode.AnswerQsNumErr
log.Warn("npassids lenth(%d) neq BaseExtraNoPassNum(%d)", len(npassids), s.c.Answer.BaseExtraNoPassNum)
return
}
}
}
ids = append(passids, npassids...)
rqs, err = s.concatExtraData(c, mid, ids, passids, npassids, lang, mobile, s.c.Answer.BaseExtraPassNum+s.c.Answer.BaseExtraNoPassNum)
if err != nil {
log.Error("BaseExtraQs s.concatExtraData(%d, %d, %d) error(%v)", c, mid, ids, err)
return
}
rqs.CurrentTime = now
rqs.EndTime = h.StartTime.Add(s.answerDuration())
if _, err = s.answerDao.UpdateExtraStartTime(c, h.ID, mid, now); err != nil {
log.Error("s.answerDao.UpdateExtraStartTime( %d, %d) error(%v)", h.ID, mid, err)
return
}
h.StepExtraStartTime = now
h.Mtime = now
s.userActionLog(mid, model.ExtraStartTime, h)
s.answerDao.DelHistoryCache(c, mid)
return
}
func (s *Service) checkExtraState(c context.Context, mid int64, now time.Time) (h *model.AnswerHistory, err error) {
h, err = s.history(c, mid)
if err != nil {
log.Error("s.history(%v) is nil error(%v)", h, err)
err = ecode.AnswerBaseNotPassed
return
}
if h != nil {
if h.Score > 0 && h.IsPassCaptcha == 0 {
err = ecode.AnswerCaptchaNoPassed
return
}
// if base pass
if h.StartTime.Add(s.answerDuration()).After(now) && h.Score == 0 {
if h.StepExtraCompleteTime != 0 {
err = ecode.AnswerProNoPass
}
return
}
err = ecode.AnswerBaseNotPassed
return
}
err = ecode.AnswerBaseNotPassed
return
}
// ProTypes get promotion types.
func (s *Service) proTypes(c context.Context, mid int64) (res *model.ProTypes, err error) {
var (
repro bool
ah *model.AnswerHistory
now = time.Now()
)
if s.checkAnswerBlock(c, mid) {
err = ecode.AnswerBlock
return
}
if ah, err = s.checkBase(c, mid, now); err != nil {
return
}
qsidsMc, err := s.answerDao.IdsCache(c, mid, model.Q)
if err == nil && len(qsidsMc) == s.c.Answer.ProNum {
repro = true
}
res = &model.ProTypes{List: _typeMap1, EndTime: ah.StartTime.Add(s.answerDuration()), CurrentTime: now, Repro: repro}
return
}
// ProType type.
func (s *Service) ProType(c context.Context, mid int64, lang string) (res *model.AnsProType, err error) {
var (
repro string
list = []*model.AnsTypeList{}
)
rpcRes, err := s.proTypes(c, mid)
if err != nil {
log.Error("s.proTypes(%+d) error (%v)", mid, err)
return
}
for _, vt := range rpcRes.List {
var sub = []*model.AnsType{}
for _, vst := range vt.Subs {
ansType := &model.AnsType{ID: vst.ID, Name: vst.Name}
if lang == model.LangZhTW {
ansType.Name = chinese.Convert(c, ansType.Name)
}
sub = append(sub, ansType)
}
ansTypeList := &model.AnsTypeList{Name: vt.Name, Fields: sub}
if lang == model.LangZhTW {
ansTypeList.Name = chinese.Convert(c, ansTypeList.Name)
}
list = append(list, ansTypeList)
}
if rpcRes.Repro {
repro = "yes"
} else {
repro = "no"
}
res = &model.AnsProType{List: list, CurrentTime: rpcRes.CurrentTime.Unix(), EndTime: rpcRes.EndTime.Unix(), Repro: repro}
return
}
// ConvertProQues pro question.
func (s *Service) ConvertProQues(c context.Context, mid int64, tIds string, lang string, mobile bool) (res []*model.AnsQueDetail, err error) {
var (
ans *model.AnsQuesList
ansdl *model.AnsQueDetailList
)
if ans, err = s.ProQues(c, mid, tIds, lang, mobile); err != nil {
err = errors.Wrapf(err, "s.ProQues(%d,%v,%t)", mid, tIds, mobile)
return
}
ansdl = s.convertModel(ans)
res = ansdl.QuesList
return
}
// ProQues question info.
func (s *Service) ProQues(c context.Context, mid int64, qtsStr string, lang string, mobile bool) (rqs *model.AnsQuesList, err error) {
var (
ah *model.AnswerHistory
now = time.Now()
allQids []int64
tIds, realTIDs []int
)
if s.checkAnswerBlock(c, mid) {
err = ecode.AnswerBlock
return
}
if ah, err = s.checkBase(c, mid, now); err != nil {
return
}
allQids, err = s.answerDao.IdsCache(c, mid, model.Q)
if err != nil || len(allQids) != s.c.Answer.ProNum {
tIDStrArr := strings.Split(qtsStr, ",")
if len(tIDStrArr) < _minType || len(tIDStrArr) > _maxType {
err = ecode.AnswerTypeIDsErr
return
}
if tIds, err = sliceAtoi(tIDStrArr); err != nil {
err = ecode.AnswerTypeIDsErr
return
}
for _, qt := range tIds {
if qt <= 0 {
err = ecode.RequestErr
return
}
if mapIDS, ok := _typeIdsMapping[qt]; ok {
realTIDs = append(realTIDs, mapIDS...)
continue
}
realTIDs = append(realTIDs, qt)
}
num := math.Ceil(float64(s.c.Answer.ProNum) / float64(len(realTIDs)))
log.Warn("realTIDs:%v", realTIDs)
for _, qt := range realTIDs {
var t []int64
t, err = s.answerDao.QidByType(c, qt, uint8(num))
if err != nil {
log.Error("s.answerDao.QidByType(%d, %f, %d) error(%+v)", qt, num, len(t), err)
return
}
if len(t) == 0 {
log.Error("mid:%d the QidByType(%d, %f, %d) of len is 0", mid, qt, num, len(t))
err = ecode.AnswerMidDBQueErr
return
}
allQids = append(allQids, t...)
}
if len(allQids) == 0 || len(allQids) < s.c.Answer.ProNum {
log.Error("ProQues allQids len is 0 or allQids len less(%d, %d, %f, %v, %d)", len(allQids), s.c.Answer.ProNum, num, realTIDs, mid)
err = ecode.NothingFound
return
}
}
if rqs, err = s.concatData(c, mid, allQids, lang, mobile, s.c.Answer.ProNum); err != nil {
log.Error("ProQues s.concatData(%d, %d, %d) error(%v)", c, mid, allQids, err)
return
}
if _, err = s.answerDao.UpdateStepTwoTime(c, ah.ID, mid, now); err != nil {
return
}
ah.StepTwoStartTime = now
ah.Mtime = now
s.userActionLog(mid, model.ProQues, ah)
s.answerDao.DelHistoryCache(c, mid)
return
}
func (s *Service) checkBase(c context.Context, mid int64, now time.Time) (ah *model.AnswerHistory, err error) {
ah, err = s.history(c, mid)
if err != nil || ah == nil || ah.StartTime.Add(s.answerDuration()).Before(now) || ah.Score != 0 || ah.StepOneCompleteTime == 0 {
err = ecode.AnswerBaseNotPassed
log.Error("checkBase(%d, %v) AnswerExpire error(%v)", mid, now, err)
return
}
if ah.StepExtraCompleteTime == 0 {
err = ecode.AnswerExtraNoPass
return
}
if ah.Score > 0 && ah.IsPassCaptcha == 0 {
err = ecode.AnswerCaptchaNoPassed
}
return
}
func (s *Service) checkTime(c context.Context, mid int64, now time.Time) (at *model.AnswerTime, rs bool) {
var err error
if at, err = s.answerDao.ExpireCache(c, mid); err != nil {
return
}
if at == nil || at.Stime.Add(s.answerDuration()).Before(now) {
return
}
rs = true
return
}
func (s *Service) concatData(c context.Context, mid int64, ids []int64, lang string, mobile bool, qs int) (rqs *model.AnsQuesList, err error) {
var (
list []*model.AnsQue
qm map[int64]*model.Question
)
if qm, err = s.answerDao.ByIds(c, ids); err != nil {
log.Error("s.answerDao.ByIds(%v) error(%v)", ids, err)
err = ecode.NothingFound
return
}
for _, d := range ids {
i := qm[d]
rq := s.imgPosition(c, i, mid, lang, mobile)
list = append(list, rq)
}
if len(list) > qs {
list = list[:qs]
}
rqs = &model.AnsQuesList{QuesList: list}
if err := s.answerDao.SetIdsCache(c, mid, ids, model.Q); err != nil {
log.Error("s.answerDao.SetIdsCache(%d, %d) error(%v)", mid, ids, err)
}
log.Info("s.concatData load que success(%d, %v, %v, %d)", mid, ids, mobile, qs)
return
}
func (s *Service) concatExtraData(c context.Context, mid int64, ids []int64, passids []int64, nopassids []int64, lang string, mobile bool, qs int) (rqs *model.AnsQuesList, err error) {
var (
list []*model.AnsQue
qm map[int64]*model.ExtraQst
)
if qm, err = s.answerDao.ExtraByIds(c, ids); err != nil || len(qm) < qs {
log.Error("s.answerDao.ExtraByIds(%v) error(%+v)", ids, err)
return
}
for _, d := range ids {
i := qm[d]
rq := s.imgExtraPosition(c, i, mid, lang, mobile)
list = append(list, rq)
}
if len(list) > qs {
list = list[:qs]
}
rqs = &model.AnsQuesList{QuesList: list}
if err = s.answerDao.SetIdsCache(c, mid, passids, model.BaseExtraPassQ); err != nil {
log.Error("s.answerDao.SetIdsCache(%d, %d) error(%v)", mid, passids, err)
return
}
if err = s.answerDao.SetIdsCache(c, mid, nopassids, model.BaseExtraNoPassQ); err != nil {
log.Error("s.answerDao.SetIdsCache(%d, %d) error(%v)", mid, nopassids, err)
return
}
log.Info("s.concatData extra load que success(%d, %v, %v, %d)", mid, ids, mobile, qs)
return
}
// ansHash get answer hash.
func (s *Service) ansHash(mid int64, ans string) (ansHash string) {
h := md5.New()
h.Write([]byte(fmt.Sprintf("%s%d%s", ans, mid, _hashSalt)))
return hex.EncodeToString(h.Sum(nil))
}
func (s *Service) imgPosition(c context.Context, qs *model.Question, mid int64, lang string, mobile bool) (rq *model.AnsQue) {
var (
y float64
qsLineLength float64 = 36
questionFontSize float64 = 10
questionTitleSize float64 = 12
ans = make([]*model.AnsPosition, 4)
imgStr = "v3_%s_A-%s_B-%s_C-%s_D-%s_%s"
p = map[bool]string{true: "H5", false: "PC"}
bfsHost = "https://i0.hdslb.com/bfs/member/"
as [4]string
)
rq = &model.AnsQue{ID: qs.ID}
if mobile {
qsLineLength = 11
questionFontSize = 12
questionTitleSize = 16
}
qsLength := utf8.RuneCountInString(qs.Question)
if float64(qsLength) > qsLineLength {
line := math.Ceil(float64(qsLength) / qsLineLength)
rq.Height = 2 * line * questionTitleSize
rq.PositionY = y
} else {
rq.Height = 2 * questionTitleSize
}
y = rq.Height
if lang == model.LangZhTW {
qs.Question = chinese.Convert(c, qs.Question)
qs.Ans[0] = chinese.Convert(c, qs.Ans[0])
qs.Ans[1] = chinese.Convert(c, qs.Ans[1])
qs.Ans[2] = chinese.Convert(c, qs.Ans[2])
qs.Ans[3] = chinese.Convert(c, qs.Ans[3])
}
idx := rand.Perm(4)
for i := range qs.Ans {
ans[i] = &model.AnsPosition{
AnsHash: s.ansHash(mid, qs.Ans[idx[i]]),
Height: 2 * questionFontSize,
PositionY: y,
}
y += 2 * questionFontSize
as[i] = qs.Ans[idx[i]]
}
m := md5.New()
m.Write([]byte(fmt.Sprintf(imgStr, strconv.FormatInt(qs.ID, 10), as[0], as[1], as[2], as[3], p[mobile])))
fname := hex.EncodeToString(m.Sum(nil)) + ".jpg"
if s.c.Answer.Debug {
fname = fmt.Sprintf("debug_%s", fname)
}
rq.Img = bfsHost + fname
rq.Ans = ans
return
}
func (s *Service) imgExtraPosition(c context.Context, qs *model.ExtraQst, mid int64, lang string, mobile bool) (rq *model.AnsQue) {
var (
y float64
qsLineLength float64 = 36
questionFontSize float64 = 10
questionTitleSize float64 = 12
ans = make([]*model.AnsPosition, 2)
imgStr = "%s_A-%s_B-%s_%s"
p = map[bool]string{true: "H5", false: "PC"}
bfsHost = "https://i0.hdslb.com/bfs/member/"
as [2]string
)
rq = &model.AnsQue{ID: qs.ID}
if mobile {
qsLineLength = 11
questionFontSize = 12
questionTitleSize = 16
}
qsLength := utf8.RuneCountInString(qs.Question)
if float64(qsLength) > qsLineLength {
line := math.Ceil(float64(qsLength) / qsLineLength)
rq.Height = 2 * line * questionTitleSize
rq.PositionY = y
} else {
rq.Height = 2 * questionTitleSize
}
y = rq.Height
if lang == model.LangZhTW {
as = [2]string{chinese.Convert(c, model.ExtraAnsA), chinese.Convert(c, model.ExtraAnsB)}
} else {
as = [2]string{model.ExtraAnsA, model.ExtraAnsB}
}
for k, v := range as {
ans[k] = &model.AnsPosition{
AnsHash: s.ansHash(mid, v),
Height: 2 * questionFontSize,
PositionY: y,
}
y += 2 * questionFontSize
}
m := md5.New()
m.Write([]byte(fmt.Sprintf(imgStr, strconv.FormatInt(qs.OriginID, 10), as[0], as[1], p[mobile])))
fname := hex.EncodeToString(m.Sum(nil)) + ".jpg"
if s.c.Answer.Debug {
fname = fmt.Sprintf("debug_%s", fname)
}
rq.Img = bfsHost + fname
rq.Ans = ans
return
}
func (s *Service) loadQidsCache() {
qs, err := s.answerDao.QidsByState(context.Background(), model.PassCheck)
if len(qs) == 0 || err != nil {
log.Error("s.answerDao.loadQidsCache(%d) size is zero error(%v)", model.PassCheck, err)
}
qmap := map[int8][]int64{}
for _, q := range qs {
qmap[q.TypeID] = append(qmap[q.TypeID], q.ID)
}
for k, v := range qmap {
s.answerDao.DelQidsCache(context.Background(), int(k))
s.answerDao.SetQids(context.Background(), v, int(k))
}
log.Info("s.answerDao.loadQidsCache suc(%v)", qmap)
}
func (s *Service) loadExtraQidsCache() {
qs, err := s.answerDao.QidsExtraByState(context.Background(), model.MaxLoadQueSize)
if len(qs) == 0 || err != nil {
log.Error("s.answerDao.QidsExtraByState(%d) size is zero error(%v)", model.MaxLoadQueSize, err)
return
}
qmap := map[int8][]int64{}
for _, q := range qs {
qmap[q.Ans] = append(qmap[q.Ans], q.ID)
}
for k, v := range qmap {
s.answerDao.DelExtraQidsCache(context.Background(), k)
s.answerDao.SetExtraQids(context.Background(), v, k)
}
log.Info("s.answerDao.loadExtraQidsCache suc(%v)", qmap)
}
// Cool .
func (s *Service) Cool(c context.Context, hid, mid int64) (cool *model.AnsCool, err error) {
var (
his *model.AnswerHistory
types []*model.TypeInfo
li = []*model.CoolPower{
{Name: "动画", Num: 0},
{Name: "艺术", Num: 0},
{Name: "游戏", Num: 0},
{Name: "科技", Num: 0},
{Name: "影视", Num: 0},
{Name: "鬼畜", Num: 0},
}
completeResult = make(map[int8]int64)
)
his, err = s.historyByHid(c, hid)
if err != nil {
return
}
cool = &model.AnsCool{
Score: his.Score,
IsSameUser: his.Mid == mid,
IsFirstPass: his.IsFirstPass,
Level: his.PassedLevel,
Share: &model.CoolShare{},
VideoInfo: &model.CoolVideo{},
Rank: &model.CoolRank{},
}
cool.CanShowRankBtn = his.Score >= 85 && his.Mtime.Before(time.Now().Add(_rankBtn))
r := _pendantIDNameMap[int(his.RankID)]
if r != "" {
if rid, ok := _oldPIDToNewMap[his.RankID]; ok {
his.RankID = rid
}
rs := _rankShire[his.RankID]
idx := rand.Perm(len(rs.VideoArr))
cool.ViewMore = rs.ViewMore
cool.Share = rs.Share
cool.VideoInfo = rs.VideoArr[idx[0]]
cool.Rank = &model.CoolRank{
ID: int(his.RankID),
Name: r,
Img: "https://i0.hdslb.com" + _pendantIDImgMap[his.RankID],
}
}
us, err := s.accInfo(c, his.Mid)
if err != nil || us == nil {
log.Error("CheckQueCaptcha accInfo(%d) info is null error(%v)", mid, err)
return
}
cool.Name = us.Name
cool.Face = us.Face
if err = json.Unmarshal([]byte(his.CompleteResult), &completeResult); err != nil {
log.Error("json.Unmarshal(%s) error(%v)", his.CompleteResult, err)
err = nil
}
log.Info("hid(%d), completeResult: %v+", hid, completeResult)
for k := range completeResult {
for _, t := range s.questionTypeCache {
if len(t.Subs) != 0 {
for _, s := range t.Subs {
if int64(k) == s.ID {
types = append(types, &model.TypeInfo{ID: s.ID, Name: s.Name, LabelName: s.LabelName})
}
}
}
if int64(k) == t.ID {
types = append(types, t)
}
}
}
log.Info("hid(%d), cool types: %v+", hid, types)
for _, t := range types {
for _, p := range li {
if p.Name == t.LabelName {
p.Num += completeResult[int8(t.ID)]
}
}
}
log.Info("hid(%d), power types: %v+", hid, li)
cool.Powers = append(cool.Powers, li...)
if _, ok := _recTypeIDMap[int(his.RankID)]; ok {
cool.MainTids = _recTypeIDMap[int(his.RankID)]["main_tid"]
cool.SubTids = _recTypeIDMap[int(his.RankID)]["sub_tid"]
}
return
}
// ExtraScore .
func (s *Service) ExtraScore(c context.Context, mid int64) (res *model.ExtraScoreReply, err error) {
res = &model.ExtraScoreReply{}
h, err := s.history(c, mid)
if err != nil {
return
}
res.Score = h.StepExtraScore + int64(s.c.Answer.BaseNum)
return
}
func (s *Service) history(c context.Context, mid int64) (ah *model.AnswerHistory, err error) {
cok := true
if ah, err = s.answerDao.HistoryCache(c, mid); err != nil {
cok = false
return
}
if ah != nil {
return
}
ah, err = s.answerDao.History(c, mid)
if err != nil {
return
}
if ah != nil && cok {
s.answerDao.SetHistoryCache(c, mid, ah)
}
return
}
func (s *Service) answerDuration() (d time.Duration) {
return time.Duration(s.c.Answer.Duration) * time.Minute
}
func sliceAtoi(sa []string) ([]int, error) {
si := make([]int, 0, len(sa))
for _, a := range sa {
i, err := strconv.Atoi(a)
if err != nil {
return si, err
}
si = append(si, i)
}
return si, nil
}
func (s *Service) extraQueByBigData(c context.Context, mid int64, ip string) (ok bool, passids []int64, npassids []int64) {
passids, npassids, err := s.accountDao.ExtraIds(c, mid, ip)
if err != nil || len(passids) != s.c.Answer.BaseExtraPassNum || len(npassids) != s.c.Answer.BaseExtraNoPassNum {
return
}
ids := append(passids, npassids...)
if qm, err := s.answerDao.ExtraByIds(c, ids); err != nil || len(qm) != (s.c.Answer.BaseExtraPassNum+s.c.Answer.BaseExtraNoPassNum) {
log.Error("s.answerDao.ExtraByIds(%v) error(%v)", ids, err)
return
}
ok = true
return
}
func (s *Service) loadtypes() (t map[int64]*model.TypeInfo) {
tys, err := s.answerDao.Types(context.Background())
if err != nil {
log.Error("s.questionDao.Types error(%v)", err)
return
}
tmp := map[int64]*model.TypeInfo{}
for _, v := range tys {
if v.Parentid == 0 && tmp[v.ID] == nil {
tmp[v.ID] = &model.TypeInfo{ID: v.ID, Name: v.Name, Subs: []*model.SubType{}}
} else if tmp[v.Parentid] != nil {
tmp[v.Parentid].Subs = append(tmp[v.Parentid].Subs, &model.SubType{ID: v.ID, Name: v.Name, LabelName: v.LabelName})
}
}
s.questionTypeCache = tmp
t = tmp
log.Info("load question type cacheproc success,%v", t)
return
}

View File

@@ -0,0 +1,202 @@
package service
import (
"context"
"fmt"
"testing"
"time"
"github.com/smartystreets/goconvey/convey"
)
func TestServiceBaseQ(t *testing.T) {
convey.Convey("BaseQ", t, func() {
res, err := s.BaseQ(context.Background(), 14771787, "", false)
convey.So(err, convey.ShouldBeNil)
convey.So(res, convey.ShouldNotBeNil)
})
}
func TestServiceBaseQs(t *testing.T) {
convey.Convey("BaseQs", t, func() {
rqs, err := s.BaseQs(context.Background(), 14771787, "", false)
convey.So(err, convey.ShouldBeNil)
convey.So(rqs, convey.ShouldNotBeNil)
})
}
func TestServiceConvertExtraQs(t *testing.T) {
convey.Convey("ConvertExtraQs", t, func() {
res, err := s.ConvertExtraQs(context.Background(), 14771787, "", false)
convey.So(err, convey.ShouldBeNil)
convey.So(res, convey.ShouldNotBeNil)
})
}
func TestServiceExtraQs(t *testing.T) {
convey.Convey("ExtraQs", t, func() {
rqs, err := s.ExtraQs(context.Background(), 14771787, "", false)
convey.So(err, convey.ShouldBeNil)
convey.So(rqs, convey.ShouldNotBeNil)
})
}
func TestServicecheckExtraState(t *testing.T) {
convey.Convey("checkExtraState", t, func() {
h, err := s.checkExtraState(context.Background(), 14771787, time.Now())
convey.So(err, convey.ShouldBeNil)
convey.So(h, convey.ShouldNotBeNil)
})
}
func TestServiceProTypes(t *testing.T) {
convey.Convey("ProTypes", t, func() {
res, err := s.proTypes(context.Background(), 14771787)
convey.So(err, convey.ShouldBeNil)
convey.So(res, convey.ShouldNotBeNil)
})
}
func TestServiceProType(t *testing.T) {
convey.Convey("ProType", t, func() {
res, err := s.ProType(context.Background(), 14771787, "")
convey.So(err, convey.ShouldBeNil)
convey.So(res, convey.ShouldNotBeNil)
})
}
func TestServiceConvertProQues(t *testing.T) {
convey.Convey("ConvertProQues", t, func() {
res, err := s.ConvertProQues(context.Background(), 14771787, "", "", false)
convey.So(err, convey.ShouldBeNil)
convey.So(res, convey.ShouldNotBeNil)
})
}
func TestServiceProQues(t *testing.T) {
convey.Convey("ProQues", t, func() {
rqs, err := s.ProQues(context.Background(), 14771787, "", "", false)
convey.So(err, convey.ShouldBeNil)
convey.So(rqs, convey.ShouldNotBeNil)
})
}
func TestServicecheckBase(t *testing.T) {
convey.Convey("checkBase", t, func() {
ah, err := s.checkBase(context.Background(), 0, time.Now())
convey.So(err, convey.ShouldBeNil)
convey.So(ah, convey.ShouldNotBeNil)
})
}
func TestServicecheckTime(t *testing.T) {
convey.Convey("checkTime", t, func() {
at, rs := s.checkTime(context.Background(), 0, time.Now())
convey.So(rs, convey.ShouldNotBeNil)
convey.So(at, convey.ShouldNotBeNil)
})
}
func TestServiceconcatData(t *testing.T) {
convey.Convey("concatData", t, func() {
rqs, err := s.concatData(context.Background(), 14771787, []int64{}, "", false, 0)
convey.So(err, convey.ShouldBeNil)
convey.So(rqs, convey.ShouldNotBeNil)
})
}
func TestServiceconcatExtraData(t *testing.T) {
convey.Convey("concatExtraData", t, func() {
rqs, err := s.concatExtraData(context.Background(), 14771787, []int64{}, []int64{}, []int64{}, "", false, 0)
convey.So(err, convey.ShouldBeNil)
convey.So(rqs, convey.ShouldNotBeNil)
})
}
func TestServiceansHash(t *testing.T) {
convey.Convey("ansHash", t, func() {
ansHash := s.ansHash(0, "")
convey.So(ansHash, convey.ShouldNotBeNil)
})
}
func TestServiceimgPosition(t *testing.T) {
convey.Convey("imgPosition", t, func() {
rq := s.imgPosition(context.Background(), nil, 14771787, "", false)
convey.So(rq, convey.ShouldNotBeNil)
})
}
func TestServiceimgExtraPosition(t *testing.T) {
convey.Convey("imgExtraPosition", t, func() {
rq := s.imgExtraPosition(context.Background(), nil, 14771787, "", false)
convey.So(rq, convey.ShouldNotBeNil)
})
}
func TestServiceloadQidsCache(t *testing.T) {
convey.Convey("loadQidsCache", t, func() {
s.loadQidsCache()
})
}
func TestServiceloadExtraQidsCache(t *testing.T) {
convey.Convey("loadExtraQidsCache", t, func() {
s.loadExtraQidsCache()
})
}
func TestServiceCool(t *testing.T) {
convey.Convey("Cool", t, func() {
cool, err := s.Cool(context.Background(), 0, 0)
convey.So(err, convey.ShouldBeNil)
convey.So(cool, convey.ShouldNotBeNil)
})
}
func TestServiceExtraScore(t *testing.T) {
convey.Convey("ExtraScore", t, func() {
score, err := s.ExtraScore(context.Background(), 6383240)
fmt.Println(score)
convey.So(err, convey.ShouldBeNil)
convey.So(score, convey.ShouldBeGreaterThanOrEqualTo, 0)
})
}
func TestServicehistory(t *testing.T) {
convey.Convey("history", t, func() {
ah, err := s.history(context.Background(), 0)
convey.So(err, convey.ShouldBeNil)
convey.So(ah, convey.ShouldNotBeNil)
})
}
func TestServiceanswerDuration(t *testing.T) {
convey.Convey("answerDuration", t, func() {
d := s.answerDuration()
convey.So(d, convey.ShouldNotBeNil)
})
}
func TestSliceAtoi(t *testing.T) {
convey.Convey("sliceAtoi", t, func() {
p1, p2 := sliceAtoi([]string{})
convey.So(p2, convey.ShouldBeNil)
convey.So(p1, convey.ShouldNotBeNil)
})
}
func TestServiceextraQueByBigData(t *testing.T) {
convey.Convey("extraQueByBigData", t, func() {
ok, passids, npassids := s.extraQueByBigData(context.Background(), 0, "")
convey.So(npassids, convey.ShouldNotBeNil)
convey.So(passids, convey.ShouldNotBeNil)
convey.So(ok, convey.ShouldNotBeNil)
})
}
func TestServiceloadtypes(t *testing.T) {
convey.Convey("loadtypes", t, func() {
t := s.loadtypes()
convey.So(t, convey.ShouldNotBeNil)
})
}

View File

@@ -0,0 +1,746 @@
package service
import (
"context"
"encoding/json"
"fmt"
"strconv"
"time"
"go-common/app/interface/main/answer/conf"
"go-common/app/interface/main/answer/model"
accoutCli "go-common/app/service/main/account/api"
"go-common/library/ecode"
"go-common/library/log"
"go-common/library/log/infoc"
"go-common/library/net/metadata"
"go-common/library/text/translate/chinese"
)
var (
// concat type type_id : _pendantIDNameMap id
_rankIDPendantMap = map[int]int{
// 11: 6,
// 12: 7,
// 14: 8,
// 15: 9,
// 17: 11,
// 27: 10,
// 28: 12,
// 41: 13,
// 9: 14,
27: 124,
28: 127,
31: 126,
29: 123,
18: 121,
8: 125,
19: 129,
15: 130,
7: 128,
}
// concat pendant
_pendantIDNameMap = map[int]string{
5: "哔哩王",
6: "声控",
7: "追番党",
8: "调教师",
9: "动感DJ",
10: "局座",
11: "攻略组",
12: "学霸",
13: "迷影者",
14: "全明星",
122: "哔哩王",
124: "声控",
127: "追番党",
126: "调教师",
123: "动感DJ",
121: "局座",
125: "攻略组",
129: "学霸",
130: "迷影者",
128: "全明星",
}
// 老挂件id对应新挂件id
_oldPIDToNewMap = map[int]int{
5: 122,
6: 124,
7: 127,
8: 126,
9: 123,
10: 121,
11: 125,
12: 129,
13: 130,
14: 128,
122: 122,
124: 124,
127: 127,
126: 126,
123: 123,
121: 121,
125: 125,
129: 129,
130: 130,
128: 128,
}
_pendantIDImgMap = map[int]string{
122: "/bfs/face/67ed957ae789852bcc59b1c1e3097ea23179f793.png",
124: "/bfs/face/ff61b405cdcf8f7860c67293218340aeaed6e233.png",
127: "/bfs/face/369098093a07af821b767eac44b51f97ee8501c5.png",
126: "/bfs/face/9e775c3ebe224a774d4b2f99fd5be342eb6f51ec.png",
123: "/bfs/face/939fa982d8b1c1fd653de5c7890db03d62e87226.png",
121: "/bfs/face/7f6b5cb11ea7abd2e05b04f65f190dfb10456554.png",
125: "/bfs/face/90cc47168e40326dc934fad7b9abb82aa748d6ac.png",
129: "/bfs/face/42869dad53926c75e3010150c15b16a8925fb268.png",
130: "/bfs/face/3d5ee491c125bf452b2dbec082dbb8209b645316.png",
128: "/bfs/face/b53937110e8009a720e2426ea69c449483718b3c.png",
}
// 125: "攻略组",--> 题库(8,9,12,13,14)
// 130: "迷影者",--> 题库(15,16,17)
// 121: "局座",--> 题库(18)
// 129: "学霸",--> 题库(19,20,21,22,23,24,25,26)
// 124: "声控",--> 题库(27)
// 127: "追番党",--> 题库(28)
// 126: "调教师",--> 题库(31)
// 123: "动感DJ",--> 题库(30,29)
// 128: "全明星",--> 题库(35,34,33,32)
// 122: "哔哩王",
// 分区合并归类
_typeIDMap = map[int][]int{
// 11: {12, 13}, // 动漫作品+动漫内容
// 15: {15, 16}, // ACG+三次元音乐
// 17: {17, 18, 19, 20, 21, 22, 23, 24, 25}, // 各类游戏
// 28: {28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 7, 8}, // 科学技术+音频+视频技术
// 41: {41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52}, // 各类影视剧
8: {8, 9, 12, 13, 14}, // 游戏
19: {19, 20, 21, 22, 23, 24, 25, 26}, // 科技
15: {15, 16, 17}, // 影视
29: {29, 30}, // 音乐
7: {7, 35, 34, 33, 32}, // 鬼畜+流行前线
}
// 兼容账号rank错误
_rank0 = int32(0)
)
const (
_unBindTel = 0
)
// ProCheck check second step questions
func (s *Service) ProCheck(c context.Context, mid int64, ids []int64, ansHash map[int64]string, lang string) (hid int64, err error) {
var now = time.Now()
if len(ids) != s.c.Answer.ProNum {
err = ecode.AnswerQsNumErr
return
}
if s.checkAnswerBlock(c, mid) {
err = ecode.AnswerBlock
return
}
ah, err := s.history(c, mid)
if err != nil || ah == nil || ah.StartTime.Add(s.answerDuration()).Before(now) || ah.Score != 0 {
err = ecode.AnswerBaseNotPassed
return
}
if (now.Unix() - ah.StepTwoStartTime.Unix()) < s.c.Answer.BlockedTimestamp {
s.answerDao.SetBlockCache(c, mid)
log.Error("member user answer block, time space(%v)", now.Unix()-ah.StepTwoStartTime.Unix())
err = ecode.AnswerBlock
return
}
qsidsMc, err := s.answerDao.IdsCache(c, mid, model.Q)
if err != nil {
err = ecode.AnswerMidCacheQidsErr
log.Error("s.answerDao.IdsCache(%d) err(%v) ", mid, err)
return
}
ok, err := s.checkQsIDs(c, ids, mid, qsidsMc, s.c.Answer.ProNum)
if !ok {
return
}
errIds, rc, err := s.checkAns(c, mid, ids, ansHash, lang, s.c.Answer.ProNum)
if err != nil {
return
}
rcJSON, err := json.Marshal(rc)
if err != nil {
log.Error("json.Marshal(%v) error(%v)", rc, err)
return
}
total := s.c.Answer.BaseNum + s.c.Answer.ProNum + int(ah.StepExtraScore)
score := total - len(errIds)
ahDB := &model.AnswerHistory{
ID: ah.ID,
Hid: ah.Hid,
CompleteResult: string(rcJSON),
CompleteTime: now,
Score: int8(score),
IsFirstPass: 0,
}
log.Info("user: %d, score:%d, his: %v", mid, score, ahDB)
member, err := s.accInfo(c, mid)
if err == nil && member != nil && score >= model.Score60 && member.Rank == model.UserInfoRank {
ahDB.IsFirstPass = 1
}
ahDB.RankID = s.pendant(c, ahDB, mid, metadata.String(c, metadata.RemoteIP), rc)
r, err := s.answerDao.SetHistory(c, mid, ahDB)
if err != nil || r != 1 {
return
}
ah.CompleteResult = ahDB.CompleteResult
ah.CompleteTime = ahDB.CompleteTime
ah.Score = ahDB.Score
ah.IsFirstPass = ahDB.IsFirstPass
ah.RankID = ahDB.RankID
ah.Mtime = now
s.userActionLog(mid, model.ProCheck, ah)
if ahDB.Score >= model.Score60 && ahDB.RankID > 0 {
if hid, _, err = s.answerDao.PendantHistory(c, mid); err != nil {
return
}
if hid <= 0 {
s.answerDao.AddPendantHistory(c, mid, ah.Hid)
}
}
hid = ah.Hid
s.missch.Do(c, func(ctx context.Context) {
s.answerDao.DelHistoryCache(ctx, mid)
s.answerDao.DelIdsCache(ctx, mid, model.Q)
})
return
}
// CheckBase check base question all
func (s *Service) CheckBase(c context.Context, mid int64, ids []int64, ansHas map[int64]string, lang string) (res *model.AnsCheck, err error) {
var (
now = time.Now()
errIds []int64
profileReply *accoutCli.ProfileReply
)
// 检查手机绑定
if profileReply, err = s.accountSvc.Profile3(c, &accoutCli.MidReq{Mid: mid}); err != nil || profileReply == nil || profileReply.Profile == nil {
log.Error("s.accRPC.Profile3(%d) err(%+v)", mid, err)
err = ecode.AnswerAccCallErr
return
}
if profileReply.Profile.TelStatus == _unBindTel {
err = ecode.AnswerNeedBindTel
return
}
if len(ids) < s.c.Answer.BaseNum {
err = ecode.RequestErr
return
}
if s.checkAnswerBlock(c, mid) {
err = ecode.AnswerBlock
return
}
res = &model.AnsCheck{}
at, ok := s.checkTime(c, mid, now)
if !ok {
err = ecode.AnswerTimeExpire
return
}
if len(ids) != s.c.Answer.BaseNum {
err = ecode.AnswerQsNumErr
return
}
qsIdsMc, err := s.answerDao.IdsCache(c, mid, model.Q)
if err != nil {
log.Error("s.answerDao.IdsCache(%d) err(%v) ", mid, err)
err = ecode.AnswerMidCacheQidsErr
return
}
ok, err = s.checkQsIDs(c, ids, mid, qsIdsMc, s.c.Answer.BaseNum)
if err != nil || !ok {
return
}
errIds, _, err = s.checkAns(c, mid, ids, ansHas, lang, s.c.Answer.BaseNum)
res.QidList = errIds
if err != nil {
return
}
if len(errIds) > 0 {
return
}
s.basePass(c, mid, at, now)
res.Pass = true
return
}
// Captcha get question captcha
func (s *Service) Captcha(c context.Context, mid int64, clientType string, newCaptcha int) (res *model.ProcessRes, err error) {
if s.checkAnswerBlock(c, mid) {
err = ecode.AnswerBlock
return
}
ah, err := s.history(c, mid)
if err != nil || ah == nil || ah.Score == model.Score0 {
log.Info("answer Captcha(%d) answer history is null or score is zero err(%v) ", mid, err)
if ah != nil {
if ah.StepOneCompleteTime == 0 {
err = ecode.AnswerBaseNotPassed
return
}
if ah.StepExtraCompleteTime == 0 {
err = ecode.AnswerExtraNoPass
return
}
}
err = ecode.AnswerProNoPass
return
}
if ah.IsPassCaptcha == model.CaptchaPass {
err = ecode.AnswerCaptchaPassed
return
}
if !conf.Conf.Answer.Captcha {
if res, err = s.preProcess(c, mid, metadata.String(c, metadata.RemoteIP), clientType, newCaptcha); err == nil {
return
}
log.Error("s.preProcess(%d,%s,%d) err:%+v", mid, clientType, newCaptcha, err)
}
var token, url string
if token, url, err = s.answerDao.Captcha(c); err != nil {
return
}
res = &model.ProcessRes{
Token: token,
URL: url,
CaptchaType: model.BiliCaptcha,
}
return
}
// Validate check question captcha
func (s *Service) Validate(c context.Context, challenge, validate, seccode, clientType string, success int, mid int64,
cookie, captchaType string, comargs map[string]string) (res *model.AnsCheck, err error) {
var now = time.Now()
if s.checkAnswerBlock(c, mid) {
err = ecode.AnswerBlock
return
}
res = &model.AnsCheck{}
ah, err := s.history(c, mid)
log.Info(" Validate ah (%d) res(%v) ", mid, ah)
if err != nil || ah == nil || ah.Score == model.Score0 {
log.Info("answer Validate(%d) answer history is null or score is zero err(%v) ", mid, err)
if ah != nil {
if ah.StepOneCompleteTime == 0 {
err = ecode.AnswerBaseNotPassed
return
}
if ah.StepExtraCompleteTime == 0 {
err = ecode.AnswerExtraNoPass
return
}
}
err = ecode.AnswerProNoPass
return
}
// passed go to next page
if ah.IsPassCaptcha == model.CaptchaPass {
res.Pass = true
res.HistoryID = ah.Hid
return
}
ip := metadata.String(c, metadata.RemoteIP)
switch captchaType {
case model.BiliCaptcha:
if err = s.answerDao.Verify(c, validate, seccode, ip); err != nil {
log.Error("answerDao.Verify(%s,%s,%s) error:%+v", validate, seccode, ip, err)
return
}
res.Pass = true
default:
if ok := s.validate(c, challenge, validate, seccode, clientType, ip, success, mid); !ok {
log.Error("Validate validate(%v,%v,%v,%v,%v,%d) error(%v)", challenge, validate, seccode, clientType, success, mid, err)
err = ecode.AnswerGeetestVaErr
return
}
res.Pass = true
}
member, err := s.accInfo(c, mid)
if err != nil || member == nil {
log.Error("Validate accInfo(%d) info is null error(%v)", mid, err)
return
}
if _, err = s.answerDao.UpdateCaptcha(c, ah.ID, ah.Mid, model.CaptchaPass); err != nil {
log.Error("s.answerDao.UpdateCaptcha error (%v) ", err)
err = ecode.ServerErr
return
}
ah.IsPassCaptcha = model.CaptchaPass
ah.Mtime = now
s.userActionLog(mid, model.Captcha, ah)
s.answerDao.DelHistoryCache(c, mid)
log.Info(" Validate member (%v) rank(%d) ", ah, member.Rank)
if ah.Score >= model.Score60 && (member.Rank == model.UserInfoRank || member.Rank == _rank0) {
log.Info(" beFormal in (%d) ", mid)
s.sendData(c, comargs, ah, ip)
if err = s.accountDao.BeFormal(c, mid, ip); err != nil {
log.Error(" beFormal fail(%d) err(%v)", mid, err)
s.addRetryBeFormal(&model.Formal{Mid: mid, IP: ip})
err = ecode.AnswerFormalFailed
return
}
s.answerDao.UpdateLevel(c, ah.ID, ah.Mid, 1, 1)
ah.IsFirstPass = 1
ah.PassedLevel = 1
ah.Mtime = now
s.userActionLog(mid, model.Level, ah)
s.answerDao.DelHistoryCache(c, mid)
s.PendantRec(c, &model.ReqPendant{HID: ah.Hid, MID: mid})
}
res.HistoryID = ah.Hid
return
}
// checkQsIDs check question id param.
func (s *Service) checkQsIDs(c context.Context, ids []int64, mid int64, qsIdsMc []int64, qs int) (ok bool, err error) {
if qsIdsMc == nil {
log.Error("CheckBase.qsIdsMc is nil (%d,%v) )", mid, qsIdsMc)
err = ecode.AnswerMidCacheQidsErr
return
}
if len(ids) != qs {
err = ecode.AnswerQsNumErr
return
}
qidMap := map[int64]bool{}
for _, v := range qsIdsMc {
qidMap[v] = true
}
i := 0
for _, v := range ids {
if qidMap[v] {
i++
}
}
if i == qs {
ok = true
} else {
err = ecode.AnswerQidDiffRequestErr
}
return
}
// checkAns check question ans.
func (s *Service) checkAns(c context.Context, mid int64, ids []int64, ansHash map[int64]string, lang string, count int) (errIds []int64, rc map[int8]int, err error) {
qs, err := s.answerDao.ByIds(c, ids)
if err != nil || qs == nil || len(qs) != count {
log.Error("checkAns.qs is nil (%v,%v) error(%v)", ids, qs, err)
err = ecode.AnswerMidDBQueErr
return
}
errIds = []int64{}
rc = make(map[int8]int)
for _, q := range qs {
if lang == model.LangZhTW {
q.Ans[0] = chinese.Convert(c, q.Ans[0])
}
if h := s.ansHash(mid, q.Ans[0]); h != ansHash[q.ID] {
errIds = append(errIds, q.ID)
} else {
rc[q.TypeID]++
}
}
return
}
// basePass base question pass.
func (s *Service) basePass(c context.Context, mid int64, at *model.AnswerTime, now time.Time) {
h := &model.AnswerHistory{
Mid: mid,
StartTime: at.Stime,
StepOneErrTimes: at.Etimes,
StepOneCompleteTime: now.Unix() - at.Stime.Unix(),
Ctime: now,
Mtime: now,
}
r, hid, err := s.answerDao.AddHistory(c, mid, h)
if err != nil || r != 1 {
log.Error("answerDao.AddHistory r !=1 (%d,%v) error(%v)", mid, h, err)
return
}
h.Hid, _ = strconv.ParseInt(hid, 10, 64)
s.userActionLog(mid, model.BasePass, h)
s.answerDao.DelHistoryCache(c, mid)
s.answerDao.DelExpireCache(c, mid)
s.answerDao.DelIdsCache(c, mid, model.Q)
}
// setPendant set pendant.
func (s *Service) pendant(c context.Context, ah *model.AnswerHistory, mid int64, ip string, rc map[int8]int) (rankID int) {
var (
ok bool
ht int
typeIDScore = map[int8]int{ // key:_typeIDMap`key,value:Score
8: 0,
19: 0,
15: 0,
29: 0,
7: 0,
}
)
if ah.Score == model.FullScore {
return model.RankTop // 122: "哔哩王",
}
for k, v := range rc {
switch k {
case 8, 9, 12, 13, 14: // 游戏 125: "攻略组",--> 题库(8,9,12,13,14)
typeIDScore[8] += v
case 15, 16, 17: // 影视 130: "迷影者",--> 题库(15,16,17)
typeIDScore[15] += v
case 19, 20, 21, 22, 23, 24, 25, 26: // 科技 129: "学霸",--> 题库(19,20,21,22,23,24,25,26)
typeIDScore[19] += v
case 29, 30: // 音乐 123: "动感DJ",--> 题库(30,29)
typeIDScore[29] += v
case 7, 35, 34, 33, 32: // 鬼畜+流行前线 128: "全明星",--> 题库(35,34,33,32)
typeIDScore[7] += v
default:
// 121: "局座",--> 题库(18)
// 124: "声控",--> 题库(27)
// 127: "追番党",--> 题库(28)
// 126: "调教师",--> 题库(31)
typeIDScore[k] += v
}
}
score := 0
for k, v := range typeIDScore {
if score < v {
score = v
ht = int(k)
}
}
rankID, ok = _rankIDPendantMap[ht]
if !ok {
log.Warn("user(%d),pendant() rankId(%d) result:%+v ", mid, _rankIDPendantMap[ht], rc)
}
return
}
func (s *Service) checkAnswerBlock(c context.Context, mid int64) (block bool) {
block, _ = s.answerDao.CheckBlockCache(c, mid)
return
}
func (s *Service) sendData(c context.Context, comargs map[string]string, ah *model.AnswerHistory, ip string) {
s.promBeFormal.Incr("count")
// add report bigdata log
ans := []interface{}{
strconv.FormatInt(ah.StepOneCompleteTime, 10),
ah.CompleteResult,
strconv.FormatInt(ah.CompleteTime.Unix()-ah.StepTwoStartTime.Unix(), 10),
fmt.Sprintf("%d", ah.Score),
strconv.FormatInt(time.Now().Unix(), 10),
}
s.missch.Do(c, func(ctx context.Context) {
ac := map[string]string{
"itemType": infoc.ItemTypeLV,
"action": infoc.ActionAnswer,
"ip": ip,
"mid": strconv.FormatInt(ah.Mid, 10),
"sid": comargs["sid"],
"ua": comargs["ua"],
"buvid": comargs["buvid"],
"refer": comargs["refer"],
"url": comargs["url"],
}
log.Info("s.infoc2.ServiceAntiCheatBus(%v,%v)", ac, ans)
s.infoc2.ServiceAntiCheatBus(ac, ans)
})
}
// ExtraCheck extra check.
func (s *Service) ExtraCheck(c context.Context, mid int64, ids []int64, ansHash map[int64]string, ua string, lang string, refer string, buvid string) (err error) {
var now = time.Now()
if len(ids) < s.c.Answer.ExtraNum {
err = ecode.RequestErr
return
}
if s.checkAnswerBlock(c, mid) {
err = ecode.AnswerBlock
return
}
ah, err := s.history(c, mid)
if err != nil || ah == nil || ah.StartTime.Add(s.answerDuration()).Before(now) || ah.Score != 0 {
err = ecode.AnswerBaseNotPassed
return
}
if len(ids) != (s.c.Answer.BaseExtraPassNum + s.c.Answer.BaseExtraNoPassNum) {
return ecode.AnswerQsNumErr
}
passids, err := s.answerDao.IdsCache(c, mid, model.BaseExtraPassQ)
if err != nil {
log.Error("s.answerDao.IdsCache(%d) extra pass err(%v) ", mid, err)
return ecode.AnswerMidCacheQidsErr
}
nopassids, err := s.answerDao.IdsCache(c, mid, model.BaseExtraNoPassQ)
if err != nil {
log.Error("s.answerDao.IdsCache(%d) extra nopass err(%v) ", mid, err)
return ecode.AnswerMidCacheQidsErr
}
idsmc := append(passids, nopassids...)
ok, err := s.checkQsIDs(c, ids, mid, idsmc, s.c.Answer.BaseExtraPassNum+s.c.Answer.BaseExtraNoPassNum)
if err != nil || !ok {
return
}
ret, qs, _ := s.checkExtraPassAns(c, mid, passids, ansHash, lang, s.c.Answer.BaseExtraPassNum)
ah.StepExtraScore = int64(ret * s.c.Answer.BaseExtraScore)
ah.StepExtraCompleteTime = now.Unix() - ah.StartTime.Unix()
if _, err = s.answerDao.UpdateExtraRet(c, ah.ID, mid, ah.StepExtraCompleteTime, ah.StepExtraScore); err != nil {
log.Error("s.answerDao.UpdateExtraRet(%d) err(%v) ", mid, err)
return
}
ah.Mtime = now
s.userActionLog(mid, model.ExtraCheck, ah)
s.answerDao.DelHistoryCache(c, mid)
s.answerDao.DelIdsCache(c, mid, model.BaseExtraPassQ)
s.answerDao.DelIdsCache(c, mid, model.BaseExtraNoPassQ)
// send answer ret to bigdata
rs, err := s.sendExtraRetMsg(c, mid, qs, nopassids, ansHash, s.c.Answer.BaseExtraNoPassNum)
if err != nil {
log.Error("s.sendExtraRetMsg(%d,%v,%v,%v) err(%v) ", mid, qs, nopassids, ansHash, err)
return
}
s.answerDao.PubExtraRet(c, mid, &model.DataBusResult{
Mid: mid,
Buvid: buvid,
IP: metadata.String(c, metadata.RemoteIP),
Ua: ua,
Refer: refer,
Score: int8(ah.StepExtraScore),
Rs: rs,
Hid: ah.Hid,
})
return
}
// checkExtraPassAns check extra question ans.
func (s *Service) checkExtraPassAns(c context.Context, mid int64, ids []int64, ansHash map[int64]string, lang string, count int) (ret int, qs map[int64]*model.ExtraQst, err error) {
qs, err = s.answerDao.ExtraByIds(c, ids)
if err != nil || qs == nil || len(qs) != count {
log.Error("checkAns extra qs is nil (%v,%v) error(%v)", ids, qs, err)
err = ecode.AnswerMidDBQueErr
return
}
for _, q := range qs {
var ans string
switch q.Ans {
case model.NormalQ:
if lang == model.LangZhTW {
ans = s.ansHash(mid, chinese.Convert(c, model.ExtraAnsA))
} else {
ans = s.ansHash(mid, model.ExtraAnsA)
}
case model.ViolationQ:
if lang == model.LangZhTW {
ans = s.ansHash(mid, chinese.Convert(c, model.ExtraAnsB))
} else {
ans = s.ansHash(mid, model.ExtraAnsB)
}
}
if ansHash[q.ID] == ans {
ret++
}
}
return
}
func (s *Service) sendExtraRetMsg(c context.Context, mid int64, passqs map[int64]*model.ExtraQst, nopassids []int64,
ansHash map[int64]string, count int) (rs []*model.Rs, err error) {
var (
qs map[int64]*model.ExtraQst
)
qs, err = s.answerDao.ExtraByIds(c, nopassids)
if err != nil || qs == nil || len(qs) != count {
log.Error("checkAns extra nopassqs is nil (%v) error(%v)", qs, err)
err = ecode.AnswerMidDBQueErr
return
}
for k, v := range passqs {
qs[k] = v
}
for _, q := range qs {
var (
userAns int8
)
ansA := s.ansHash(mid, model.ExtraAnsA)
ansB := s.ansHash(mid, model.ExtraAnsB)
switch ansHash[q.ID] {
case ansA:
userAns = model.NormalQ
case ansB:
userAns = model.ViolationQ
default:
userAns = model.UnKownQ
}
rs = append(rs, &model.Rs{
ID: q.OriginID,
Question: q.Question,
Ans: userAns,
TrueAns: q.Ans,
AvID: q.AvID,
Status: q.Status,
Source: q.Source,
Ctime: q.Ctime,
Mtime: q.Mtime,
})
}
return
}
// PendantRec .
func (s *Service) PendantRec(c context.Context, arg *model.ReqPendant) (err error) {
var (
ok bool
status int8
hid, ret int64
his *model.AnswerHistory
)
if hid, status, err = s.answerDao.PendantHistory(c, arg.MID); err != nil {
return
}
if hid != arg.HID {
log.Warn("mid(%d) arg.hid(%d) db.hid(%d) is invald!", arg.MID, arg.HID, hid)
return
}
if status != model.PendantNotGet {
log.Warn("mid(%d) hid(%d) not first get!", arg.MID, arg.HID)
return
}
his, err = s.historyByHid(c, arg.HID)
if err != nil {
return
}
if his.Score < model.Score60 || his.IsFirstPass != 1 {
log.Warn("mid(%d) hid(%d) score(%d) isFirstPass(%d) not pass or first answer !", arg.MID, arg.HID, his.Score, his.IsFirstPass)
return
}
if _, ok = _pendantIDNameMap[int(his.RankID)]; !ok {
log.Warn("mid(%d) get illegal pid(%d) by answer first!", arg.MID, int(his.RankID))
return
}
if ret, err = s.answerDao.UpPendantHistory(c, arg.MID, arg.HID); err != nil {
return
}
if ret <= 0 {
log.Warn("mid(%d) hid(%d) pid(%d) history answer not get!", arg.MID, arg.HID, int(his.RankID))
return
}
s.missch.Do(c, func(ctx context.Context) {
if pendantErr := s.accountDao.GivePendant(ctx, arg.MID, int64(his.RankID), model.PenDantDays, metadata.String(c, metadata.RemoteIP)); pendantErr != nil {
log.Error("s.accountDao.GivePendant(%d,%d) error(%+v)", arg.MID, int64(his.RankID), pendantErr)
}
})
return
}

View File

@@ -0,0 +1,115 @@
package service
import (
"context"
"testing"
"time"
"github.com/smartystreets/goconvey/convey"
)
func TestServiceProCheck(t *testing.T) {
convey.Convey("ProCheck", t, func() {
hid, err := s.ProCheck(context.Background(), 14771787, []int64{}, nil, "")
convey.So(err, convey.ShouldBeNil)
convey.So(hid, convey.ShouldNotBeNil)
})
}
func TestServiceCheckBase(t *testing.T) {
convey.Convey("CheckBase", t, func() {
res, err := s.CheckBase(context.Background(), 14771787, []int64{}, nil, "")
convey.So(err, convey.ShouldBeNil)
convey.So(res, convey.ShouldNotBeNil)
})
}
func TestServiceCaptcha(t *testing.T) {
convey.Convey("Captcha", t, func() {
res, err := s.Captcha(context.Background(), 14771787, "", 0)
convey.So(err, convey.ShouldBeNil)
convey.So(res, convey.ShouldNotBeNil)
})
}
func TestServiceValidate(t *testing.T) {
convey.Convey("Validate", t, func() {
res, err := s.Validate(context.Background(), "", "", "", "", 0, 0, "", "", nil)
convey.So(err, convey.ShouldBeNil)
convey.So(res, convey.ShouldNotBeNil)
})
}
func TestServicecheckQsIDs(t *testing.T) {
convey.Convey("checkQsIDs", t, func() {
ok, err := s.checkQsIDs(context.Background(), []int64{}, 0, []int64{}, 0)
convey.So(err, convey.ShouldBeNil)
convey.So(ok, convey.ShouldNotBeNil)
})
}
func TestServicecheckAns(t *testing.T) {
convey.Convey("checkAns", t, func() {
errIds, rc, err := s.checkAns(context.Background(), 14771787, []int64{}, nil, "", 0)
convey.So(err, convey.ShouldBeNil)
convey.So(rc, convey.ShouldNotBeNil)
convey.So(errIds, convey.ShouldNotBeNil)
})
}
func TestServicebasePass(t *testing.T) {
convey.Convey("basePass", t, func() {
s.basePass(context.Background(), 0, nil, time.Now())
})
}
func TestServicependant(t *testing.T) {
convey.Convey("pendant", t, func() {
err := s.pendant(context.Background(), nil, 0, "", nil)
convey.So(err, convey.ShouldBeNil)
})
}
func TestServicecheckAnswerBlock(t *testing.T) {
convey.Convey("checkAnswerBlock", t, func() {
block := s.checkAnswerBlock(context.Background(), 0)
convey.So(block, convey.ShouldNotBeNil)
})
}
func TestServicesendData(t *testing.T) {
convey.Convey("sendData", t, func() {
s.sendData(context.Background(), nil, nil, "")
})
}
func TestServiceExtraCheck(t *testing.T) {
convey.Convey("ExtraCheck", t, func() {
err := s.ExtraCheck(context.Background(), 14771787, []int64{}, nil, "", "", "", "")
convey.So(err, convey.ShouldBeNil)
})
}
func TestServicecheckExtraPassAns(t *testing.T) {
convey.Convey("checkExtraPassAns", t, func() {
ret, qs, err := s.checkExtraPassAns(context.Background(), 14771787, []int64{}, nil, "", 0)
convey.So(err, convey.ShouldBeNil)
convey.So(qs, convey.ShouldNotBeNil)
convey.So(ret, convey.ShouldNotBeNil)
})
}
func TestServicesendExtraRetMsg(t *testing.T) {
convey.Convey("sendExtraRetMsg", t, func() {
rs, err := s.sendExtraRetMsg(context.Background(), 0, nil, []int64{}, nil, 0)
convey.So(err, convey.ShouldBeNil)
convey.So(rs, convey.ShouldNotBeNil)
})
}
func TestServicePendantRec(t *testing.T) {
convey.Convey("PendantRec", t, func() {
err := s.PendantRec(context.Background(), nil)
convey.So(err, convey.ShouldBeNil)
})
}

View File

@@ -0,0 +1,59 @@
package service
import (
"context"
"fmt"
"go-common/app/interface/main/answer/model"
"go-common/library/ecode"
)
func (s *Service) convertModel(rpcRes *model.AnsQuesList) (res *model.AnsQueDetailList) {
res = &model.AnsQueDetailList{CurrentTime: rpcRes.CurrentTime.Unix(), EndTime: rpcRes.EndTime.Unix()}
for _, q := range rpcRes.QuesList {
que := &model.AnsQueDetail{
ID: q.ID, AnsImg: q.Img, QsHeight: q.Height, QsPositionY: q.PositionY,
Ans1Hash: q.Ans[0].AnsHash, Ans0Height: q.Ans[0].Height, Ans0PositionY: q.Ans[0].PositionY,
Ans2Hash: q.Ans[1].AnsHash, Ans1Height: q.Ans[1].Height, Ans1PositionY: q.Ans[1].PositionY,
Ans3Hash: q.Ans[2].AnsHash, Ans2Height: q.Ans[2].Height, Ans2PositionY: q.Ans[2].PositionY,
Ans4Hash: q.Ans[3].AnsHash, Ans3Height: q.Ans[3].Height, Ans3PositionY: q.Ans[3].PositionY,
}
res.QuesList = append(res.QuesList, que)
}
return
}
func (s *Service) convertExtraModel(rpcRes *model.AnsQuesList) (res *model.AnsQueDetailList) {
res = &model.AnsQueDetailList{CurrentTime: rpcRes.CurrentTime.Unix(), EndTime: rpcRes.EndTime.Unix()}
for _, q := range rpcRes.QuesList {
que := &model.AnsQueDetail{
ID: q.ID, AnsImg: q.Img, QsHeight: q.Height, QsPositionY: q.PositionY,
Ans1Hash: q.Ans[0].AnsHash, Ans0Height: q.Ans[0].Height, Ans0PositionY: q.Ans[0].PositionY,
Ans2Hash: q.Ans[1].AnsHash, Ans1Height: q.Ans[1].Height, Ans1PositionY: q.Ans[1].PositionY,
}
res.QuesList = append(res.QuesList, que)
}
return
}
func (s *Service) historyByHid(ctx context.Context, hid int64) (his *model.AnswerHistory, err error) {
his, err = s.answerDao.HidCache(ctx, hid)
if err != nil {
return
}
if his != nil {
return
}
if len(fmt.Sprintf("%d", hid)) < 10 {
i, _ := s.answerDao.SharingIndexByHid(ctx, hid)
his, err = s.answerDao.OldHistory(ctx, hid, i)
} else {
his, err = s.answerDao.HistoryByHid(ctx, hid)
}
if err != nil || his == nil {
err = ecode.NothingFound
return
}
s.answerDao.SetHidCache(ctx, his)
return
}

View File

@@ -0,0 +1,21 @@
package service
import (
"testing"
"github.com/smartystreets/goconvey/convey"
)
func TestServiceconvertModel(t *testing.T) {
convey.Convey("convertModel", t, func() {
res := s.convertModel(nil)
convey.So(res, convey.ShouldNotBeNil)
})
}
func TestServiceconvertExtraModel(t *testing.T) {
convey.Convey("convertExtraModel", t, func() {
res := s.convertExtraModel(nil)
convey.So(res, convey.ShouldNotBeNil)
})
}

View File

@@ -0,0 +1,150 @@
package service
import (
"context"
"crypto/md5"
"encoding/hex"
"fmt"
"math"
"math/rand"
"strconv"
"strings"
"go-common/app/interface/main/answer/model"
"go-common/library/log"
"github.com/pkg/errors"
)
// preProcess getGeetestChal
func (s *Service) preProcess(c context.Context, mid int64, ip, clientType string, newCaptcha int) (res *model.ProcessRes, err error) {
var pre string
res = &model.ProcessRes{}
gc, geeType := s.geetestDao.GeeConfig(clientType, s.c.Geetest)
res.CaptchaID = gc.CaptchaID
res.NewCaptcha = newCaptcha
if pre, err = s.geetestDao.PreProcess(c, mid, ip, geeType, gc, newCaptcha); err != nil || pre == "" {
log.Error("s.geetestDao.PreProcess(%d) err(%v)", mid, err)
randOne := md5.Sum([]byte(strconv.Itoa(rand.Intn(100))))
randTwo := md5.Sum([]byte(strconv.Itoa(rand.Intn(100))))
challenge := hex.EncodeToString(randOne[:]) + hex.EncodeToString(randTwo[:])[0:2]
res.Challenge = challenge
err = nil
return
}
res.Success = 1
slice := md5.Sum([]byte(pre + gc.PrivateKEY))
res.Challenge = hex.EncodeToString(slice[:])
return
}
// validate recheck the seccode
func (s *Service) validate(c context.Context, challenge, validate, seccode, clientType, ip string, success int, mid int64) (stat bool) {
if len(validate) != 32 {
log.Error("s.Validate(%s,%s,%s,%d) err(validate not eq 32byte)", challenge, validate, seccode, mid)
stat = s.failbackValidate(c, challenge, validate, seccode)
log.Info("s.failbackValidate(%s,%s,%s,%d), stat(%t)", challenge, validate, seccode, mid, stat)
return
}
if success != 1 {
slice := md5.Sum([]byte(challenge))
stat = hex.EncodeToString(slice[:]) == validate
return
}
gc, geeType := s.geetestDao.GeeConfig(clientType, s.c.Geetest)
slice := md5.Sum([]byte(gc.PrivateKEY + "geetest" + challenge))
if hex.EncodeToString(slice[:]) != validate {
log.Error("s.Validate(%s,%s,%s,%d) err(challenge not found)", challenge, validate, seccode, mid)
return
}
res, err := s.geetestDao.Validate(c, challenge, seccode, geeType, ip, gc.CaptchaID, mid)
if err != nil {
if errors.Cause(err) == context.DeadlineExceeded { // for geetest timeout
stat = true
return
}
log.Error("s.geetestDao.Validate(%d) err(%v)", mid, err)
return
}
slice = md5.Sum([]byte(seccode))
stat = hex.EncodeToString(slice[:]) == res.Seccode
return
}
//failbackValidate geetest failback model.
func (s *Service) failbackValidate(c context.Context, challenge, validate, seccode string) bool {
varr := strings.Split(validate, "_")
if len(varr) < 3 {
return false
}
encodeAns := varr[0]
encodeFbii := varr[1]
encodeIgi := varr[2]
decodeAns := s.decodeResponse(challenge, encodeAns)
decodeFbii := s.decodeResponse(challenge, encodeFbii)
decodeIgi := s.decodeResponse(challenge, encodeIgi)
return s.validateFailImage(decodeAns, decodeFbii, decodeIgi)
}
func (s *Service) decodeResponse(challenge, userresponse string) (res int) {
if len(userresponse) > 100 {
return
}
digits := []int{1, 2, 5, 10, 50}
key := make(map[rune]int)
for _, i := range challenge {
if _, exist := key[i]; exist {
continue
}
value := digits[len(key)%5]
key[i] = value
}
for _, i := range userresponse {
res += key[i]
}
res -= s.decodeRandBase(challenge)
return
}
func (s *Service) decodeRandBase(challenge string) int {
baseStr := challenge[32:]
var tempList []int
for _, char := range baseStr {
tempChar := int(char)
result := tempChar - 48
if tempChar > 57 {
result = tempChar - 87
}
tempList = append(tempList, result)
}
return tempList[0]*36 + tempList[1]
}
func (s *Service) md5Encode(values []byte) string {
return fmt.Sprintf("%x", md5.Sum(values))
}
func (s *Service) validateFailImage(ans, fullBgIndex, imgGrpIndex int) bool {
var thread float64 = 3
fullBg := s.md5Encode([]byte(strconv.Itoa(fullBgIndex)))[0:10]
imgGrp := s.md5Encode([]byte(strconv.Itoa(imgGrpIndex)))[10:20]
var answerDecode []byte
for i := 0; i < 9; i++ {
if i%2 == 0 {
answerDecode = append(answerDecode, fullBg[i])
} else if i%2 == 1 {
answerDecode = append(answerDecode, imgGrp[i])
}
}
xDecode := answerDecode[4:]
xInt64, err := strconv.ParseInt(string(xDecode), 16, 32)
if err != nil {
log.Error("%+v", err)
}
xInt := int(xInt64)
result := xInt % 200
if result < 40 {
result = 40
}
return math.Abs(float64(ans-result)) < thread
}

View File

@@ -0,0 +1,58 @@
package service
import (
"context"
"testing"
"github.com/smartystreets/goconvey/convey"
)
func TestServicePreProcess(t *testing.T) {
convey.Convey("PreProcess", t, func() {
res, err := s.preProcess(context.Background(), 0, "", "", 0)
convey.So(err, convey.ShouldBeNil)
convey.So(res, convey.ShouldNotBeNil)
})
}
func TestServicevalidate(t *testing.T) {
convey.Convey("validate", t, func() {
stat := s.validate(context.Background(), "", "", "", "", "", 0, 0)
convey.So(stat, convey.ShouldNotBeNil)
})
}
func TestServicefailbackValidate(t *testing.T) {
convey.Convey("failbackValidate", t, func() {
p1 := s.failbackValidate(context.Background(), "", "", "")
convey.So(p1, convey.ShouldNotBeNil)
})
}
func TestServicedecodeResponse(t *testing.T) {
convey.Convey("decodeResponse", t, func() {
res := s.decodeResponse("", "")
convey.So(res, convey.ShouldNotBeNil)
})
}
func TestServicedecodeRandBase(t *testing.T) {
convey.Convey("decodeRandBase", t, func() {
p1 := s.decodeRandBase("")
convey.So(p1, convey.ShouldNotBeNil)
})
}
func TestServicemd5Encode(t *testing.T) {
convey.Convey("md5Encode", t, func() {
p1 := s.md5Encode([]byte{})
convey.So(p1, convey.ShouldNotBeNil)
})
}
func TestServicevalidateFailImage(t *testing.T) {
convey.Convey("validateFailImage", t, func() {
p1 := s.validateFailImage(0, 0, 0)
convey.So(p1, convey.ShouldNotBeNil)
})
}

View File

@@ -0,0 +1,381 @@
package service
import "go-common/app/interface/main/answer/model"
// RankShire struct .
type RankShire struct {
Share *model.CoolShare
ViewMore string
VideoArr []*model.CoolVideo
}
var (
_rankShire = map[int]*RankShire{
122: {
Share: &model.CoolShare{Content: "老司机中的老司机,哲学王中的哲学王。",
ShortContent: "老司机中的老司机,哲学王中的哲学王。"},
ViewMore: "http://www.bilibili.com/ranking",
VideoArr: []*model.CoolVideo{
{
URL: "http://www.bilibili.com/video/av221107/",
Name: "乾杯 - ( ゜- ゜)つロ",
Img: "https://static-s.bilibili.com/account/img/answer/221107.jpg",
WatchNum: "85.8万",
UpNum: "87515",
}, {
URL: "http://www.bilibili.com/video/av296938/",
Name: "我们的BILIBILI",
Img: "https://static-s.bilibili.com/account/img/answer/296938.jpg",
WatchNum: "83.4万",
UpNum: "94643",
},
},
},
124: {
Share: &model.CoolShare{Content: "在无限大的梦想后面,<br/>无论喜欢动画的你去哪,<br/>那只幻化的蝴蝶,永远会陪伴在你身边。",
ShortContent: "无限大的梦想后面,那只蝴蝶会永远陪伴着你。"},
ViewMore: "http://www.bilibili.com/video/bangumi.html",
VideoArr: []*model.CoolVideo{
{
URL: "http://www.bilibili.com/video/av19781/",
Name: "【催泪向MAD】butter-fly (主MIX) 【数码宝贝】",
Img: "https://static-s.bilibili.com/account/img/answer/19781.jpg",
WatchNum: "104.1万",
UpNum: "173642",
}, {
URL: "http://www.bilibili.com/video/av2216404/",
Name: "海尔兄弟【bilibili正版】",
Img: "https://static-s.bilibili.com/account/img/answer/2216404.jpg",
WatchNum: "40.8万",
UpNum: "39693",
}, {
URL: "http://www.bilibili.com/video/av491271/",
Name: "【综漫MAD】致一直陪伴着我的二次元——谢谢",
Img: "https://static-s.bilibili.com/account/img/answer/491271.jpg",
WatchNum: "29.8万",
UpNum: "19541",
}, {
URL: "http://www.bilibili.com/video/av101457/",
Name: "【MAD】因为我是活在二次元的女孩",
Img: "https://static-s.bilibili.com/account/img/answer/101457.jpg",
WatchNum: "53.7万",
UpNum: "19034",
}, {
URL: "http://www.bilibili.com/video/av853594/",
Name: "致二次元,谢谢你给了我整个世界",
Img: "https://static-s.bilibili.com/account/img/answer/853594.png",
WatchNum: "17.0万",
UpNum: "16830",
},
},
},
127: {
Share: &model.CoolShare{Content: "在无限大的梦想后面,<br/>无论喜欢动画的你去哪,<br/>那只幻化的蝴蝶,永远会陪伴在你身边。",
ShortContent: "无限大的梦想后面,那只蝴蝶会永远陪伴着你。"},
ViewMore: "http://www.bilibili.com/video/bangumi.html",
VideoArr: []*model.CoolVideo{
{
URL: "http://www.bilibili.com/video/av19781/",
Name: "【催泪向MAD】butter-fly (主MIX) 【数码宝贝】",
Img: "https://static-s.bilibili.com/account/img/answer/19781.jpg",
WatchNum: "104.1万",
UpNum: "173642",
},
{
URL: "http://www.bilibili.com/video/av2216404/",
Name: "海尔兄弟【bilibili正版】",
Img: "https://static-s.bilibili.com/account/img/answer/2216404.jpg",
WatchNum: "40.8万",
UpNum: "39693",
},
{
URL: "http://www.bilibili.com/video/av491271/",
Name: "【综漫MAD】致一直陪伴着我的二次元——谢谢",
Img: "https://static-s.bilibili.com/account/img/answer/491271.jpg",
WatchNum: "29.8万",
UpNum: "19541",
},
{
URL: "http://www.bilibili.com/video/av101457/",
Name: "【MAD】因为我是活在二次元的女孩",
Img: "https://static-s.bilibili.com/account/img/answer/101457.jpg",
WatchNum: "53.7万",
UpNum: "19034",
},
{
URL: "http://www.bilibili.com/video/av853594/",
Name: "致二次元,谢谢你给了我整个世界",
Img: "https://static-s.bilibili.com/account/img/answer/853594.png",
WatchNum: "17.0万",
UpNum: "16830",
},
},
},
126: {
Share: &model.CoolShare{Content: "给你最想听的音乐,让耳朵来一场旅行,我是旋律的向导。",
ShortContent: "给你最想听的音乐,让耳朵来一场旅行,我是旋律的向导。"},
ViewMore: "http://www.bilibili.com/video/music-vocaloid-1.html",
VideoArr: []*model.CoolVideo{
{
URL: "http://www.bilibili.com/video/av2129461/",
Name: "洛天依言和原创《普通DISCO》",
Img: "https://static-s.bilibili.com/account/img/answer/2129461.jpg",
WatchNum: "173.7万",
UpNum: "53752",
},
{
URL: "http://www.bilibili.com/video/av644136/",
Name: "【洛天依翻唱】跳蛋的性福理论【手书PV】【JumPingEgG】",
Img: "https://static-s.bilibili.com/account/img/answer/644136.jpg",
WatchNum: "64.6万",
UpNum: "25293",
},
{
URL: "http://www.bilibili.com/video/av2075941/",
Name: "【洛天依古风原创曲】权御天下【原创PV付】",
Img: "https://static-s.bilibili.com/account/img/answer/2075941.jpg",
WatchNum: "128.7万",
UpNum: "27589",
},
{
URL: "http://www.bilibili.com/video/av482844/",
Name: "【洛天依原创】一半一半",
Img: "https://static-s.bilibili.com/account/img/answer/482844.jpg",
WatchNum: "50.5万",
UpNum: "22569",
},
{
URL: "http://www.bilibili.com/video/av556019/",
Name: "【niconico超会议2現場版】威风堂々【Vmoe字幕组】",
Img: "https://static-s.bilibili.com/account/img/answer/556019.jpg",
WatchNum: "67.4万",
UpNum: "19833",
},
},
},
123: {
Share: &model.CoolShare{Content: "给你最想听的音乐,让耳朵来一场旅行,我是旋律的向导。",
ShortContent: "给你最想听的音乐,让耳朵来一场旅行,我是旋律的向导。"},
ViewMore: "http://www.bilibili.com/video/music.html",
VideoArr: []*model.CoolVideo{
{
URL: "http://www.bilibili.com/video/av643809/",
Name: "茶太--团子大家族 现场版",
Img: "https://static-s.bilibili.com/account/img/answer/643809.jpg",
WatchNum: "14.8万",
UpNum: "7947",
},
{
URL: "http://www.bilibili.com/video/av345249/",
Name: "【AMV、時代的眼泪、泪腺崩坏、燃烧殆尽】最后的Butterfly重制版",
Img: "https://static-s.bilibili.com/account/img/answer/345249.png",
WatchNum: "31.7万",
UpNum: "39937",
},
{
URL: "http://www.bilibili.com/video/av736852/",
Name: "看到鼓手时我跪下尿了一地!",
Img: "https://static-s.bilibili.com/account/img/answer/736852.jpg",
WatchNum: "91.2万",
UpNum: "11302",
},
{
URL: "http://www.bilibili.com/video/av1393947/",
Name: "电二胡的咆哮 【致童年—数码宝贝】butterfly",
Img: "https://static-s.bilibili.com/account/img/answer/1393947.jpg",
WatchNum: "35.6万",
UpNum: "14199",
},
{
URL: "http://www.bilibili.com/video/av1507163/",
Name: "你们要的小苹果交响版",
Img: "https://static-s.bilibili.com/account/img/answer/1507163.jpg",
WatchNum: "30.9万",
UpNum: "7092",
},
},
},
121: {
Share: &model.CoolShare{Content: "常存好奇之心,<br/>新技能 get 是他们的座右铭。",
ShortContent: "常存好奇之心,新技能 get 是他们的座右铭。"},
ViewMore: "http://www.bilibili.com/video/tech-future-military-1.html",
VideoArr: []*model.CoolVideo{
{
URL: "http://www.bilibili.com/video/av2075767/",
Name: "巴雷特狙击枪后座力有多强?",
Img: "https://static-s.bilibili.com/account/img/answer/2075767.jpg",
WatchNum: "33.7万",
UpNum: "2002",
},
{
URL: "http://www.bilibili.com/video/av1952604/",
Name: "【张召忠】 印度史上最搞笑的大阅兵 铁血军情20150201",
Img: "https://static-s.bilibili.com/account/img/answer/1952604.jpg",
WatchNum: "28.3万",
UpNum: "12463",
},
{
URL: "http://www.bilibili.com/video/av927165/",
Name: "【军武次位面】第九期:十大战列舰",
Img: "https://static-s.bilibili.com/account/img/answer/927165.png",
WatchNum: "32.6万",
UpNum: "10880",
},
},
},
125: {
Share: &model.CoolShare{Content: "你就是来自那个世界的勇士吗?<br/>果然你在这个虚拟世界有着强大的力量啊!<br/>准备好攻略这场战斗了么那么…Link Start!",
ShortContent: "准备好攻略这场战斗了么那么…Link Start!"},
ViewMore: "http://www.bilibili.com/video/game.html",
VideoArr: []*model.CoolVideo{
{
URL: "http://www.bilibili.com/video/av1293479/",
Name: "【若风噩梦人机】瞬间爆炸的恐怖电脑们!",
Img: "https://static-s.bilibili.com/account/img/answer/1293479.jpg",
WatchNum: "73.1万",
UpNum: "25452",
},
{
URL: "http://www.bilibili.com/video/av1561567/",
Name: "【散人】大型励志剧 娱乐圈小助理养成计划更新至P20 遇龙2杀青",
Img: "https://static-s.bilibili.com/account/img/answer/1561567.jpg",
WatchNum: "306.5万",
UpNum: "581921",
},
{
URL: "http://www.bilibili.com/video/av862182/",
Name: "【文明5】美丽新世界神级实况7P完结",
Img: "https://static-s.bilibili.com/account/img/answer/862182.jpg",
WatchNum: "14.4万",
UpNum: "9325",
},
{
URL: "http://www.bilibili.com/video/av885977/",
Name: "<Mugen>狂下左右节操全无大会最终章-燃烧热情吧!向着梦想的彼方!",
Img: "https://static-s.bilibili.com/account/img/answer/885977.jpg",
WatchNum: "14.2万",
UpNum: "11825",
},
{
URL: "http://www.bilibili.com/video/av2269587/",
Name: "LOL这也能翻盘史上最奇葩的翻盘这竟然是钻石排位",
Img: "https://static-s.bilibili.com/account/img/answer/2269587.jpg",
WatchNum: "99.3万",
UpNum: "37457",
},
},
},
129: {
Share: &model.CoolShare{Content: "常存好奇之心,<br/>新技能 get 是他们的座右铭。",
ShortContent: "常存好奇之心,新技能 get 是他们的座右铭。"},
ViewMore: "http://www.bilibili.com/video/technology.html",
VideoArr: []*model.CoolVideo{
{
URL: "http://www.bilibili.com/video/av1787919/",
Name: "史上最无节操的手机架子鼓教程 ,结尾开大招。",
Img: "https://static-s.bilibili.com/account/img/answer/dcc6580b4dcc7e339545018cc312bf76.jpg",
WatchNum: "29.2万",
UpNum: "4744",
},
{
URL: "http://www.bilibili.com/video/av679319/",
Name: "论爱情动作片和真实生活啪啪啪的区别",
Img: "https://static-s.bilibili.com/account/img/answer/13752405809fed020ca2372901.jpg",
WatchNum: "43.3万",
UpNum: "3028",
},
{
URL: "http://www.bilibili.com/video/av2275735/",
Name: "Besiege贴吧4月作品精选",
Img: "https://static-s.bilibili.com/account/img/answer/d382baaee8f144b924975b05cc592c60.jpg",
WatchNum: "10.5万",
UpNum: "6506",
},
{
URL: "http://www.bilibili.com/video/av2278660/",
Name: "英梨梨&诗羽de绘成方法 五一特别篇【别人君】",
Img: "https://static-s.bilibili.com/account/img/answer/fd41bb17a5f512c37f01570ddd994b49.jpg",
WatchNum: "4.7万",
UpNum: "2837",
},
{
URL: "http://www.bilibili.com/video/av2278660/",
Name: "这大概是最好的日语入门教学了吧----五十音学习",
Img: "https://static-s.bilibili.com/account/img/answer/8c7436785697cc5c450b33eb93c2353f.jpg",
WatchNum: "63.6万",
UpNum: "104048",
},
},
},
130: {
Share: &model.CoolShare{Content: "一天不看片会死星人就是你吗?<br/>大开脑洞YY主人公也大丈夫~<br/>在异次元的空间里跟我们一起做梦吧。",
ShortContent: "一天不看片会死星人就是你吗?在异次元的空间里跟我们一起做梦吧。"},
ViewMore: "http://www.bilibili.com/video/tv-presentation-1.html",
VideoArr: []*model.CoolVideo{
{
URL: "http://www.bilibili.com/video/av2019928/",
Name: "泰剧《不一样的美男》中字第一集@天府泰剧",
Img: "https://static-s.bilibili.com/account/img/answer/daab100da59667394e70822e3e63d254.jpg",
WatchNum: "203.5万",
UpNum: "219407",
},
{
URL: "http://www.bilibili.com/video/av639407/",
Name: "【熟肉】半泽直树 01【人人字幕】",
Img: "https://static-s.bilibili.com/account/img/answer/076d81509a1c93a4569e942b281f46c1.jpg",
WatchNum: "141.8万",
UpNum: "61416",
},
{
URL: "http://www.bilibili.com/video/av1999475/",
Name: "【国产】少年包青天 第一部 2000 40集全集",
Img: "https://static-s.bilibili.com/account/img/answer/33a6c9cf7b38bb08c4da7ec5ac3b12f4.jpg",
WatchNum: "45.8万",
UpNum: "167676",
},
},
},
128: {
Share: &model.CoolShare{Content: "从未见过如此才华横溢之人,<br/>他们的技术、创意和努力,<br/>使「鬼畜」成为一种艺术。",
ShortContent: "他们的技术、创意和努力,使「鬼畜」成为一种艺术。"},
ViewMore: "http://www.bilibili.com/video/kichiku.html",
VideoArr: []*model.CoolVideo{
{
URL: "http://www.bilibili.com/video/av75179/",
Name: "【葛平金曲】循环(完整版)",
Img: "https://static-s.bilibili.com/account/img/answer/1301416217-61b.jpg",
WatchNum: "45.1万",
UpNum: "21196",
},
{
URL: "http://www.bilibili.com/video/av1858893/",
Name: "全是猴【白金王司猴】",
Img: "https://static-s.bilibili.com/account/img/answer/f1d17e3ce7f9e71e1508ced43d6a8656.jpg",
WatchNum: "111.4万",
UpNum: "14230",
},
{
URL: "http://www.bilibili.com/video/av1076105/",
Name: "妮可 妮可 妮可",
Img: "https://static-s.bilibili.com/account/img/answer/ee087327bf0a239ad114633e2806fa79.jpg",
WatchNum: "94.6万",
UpNum: "25738",
},
{
URL: "http://www.bilibili.com/video/av2271112/",
Name: "【循环向】跟着雷总摇起来Are you OK",
Img: "https://static-s.bilibili.com/account/img/answer/2fc528fee5d0cbfb98b266bb7ec3a1ad.jpg",
WatchNum: "118.1万",
UpNum: "18960",
},
{
URL: "http://www.bilibili.com/video/av794506/",
Name: "【元首葛炮】要金坷垃",
Img: "https://static-s.bilibili.com/account/img/answer/1d34e9f58def856acf501289d2cacc56.jpg",
WatchNum: "64.3万",
UpNum: "13795",
},
}},
}
)

View File

@@ -0,0 +1,126 @@
package service
import (
"context"
"time"
"go-common/app/interface/main/answer/conf"
"go-common/app/interface/main/answer/dao"
accountDao "go-common/app/interface/main/answer/dao/account"
geetestDao "go-common/app/interface/main/answer/dao/geetest"
"go-common/app/interface/main/answer/model"
accoutCli "go-common/app/service/main/account/api"
memrpc "go-common/app/service/main/member/api/gorpc"
"go-common/library/log"
"go-common/library/log/anticheat"
"go-common/library/queue/databus/report"
"go-common/library/stat/prom"
"go-common/library/sync/pipeline/fanout"
)
// Service struct of service.
type Service struct {
c *conf.Config
answerDao *dao.Dao
geetestDao *geetestDao.Dao
accountDao *accountDao.Dao
accountSvc accoutCli.AccountClient
memRPC *memrpc.Service
missch *fanout.Fanout
beformalch chan *model.Formal
questionTypeCache map[int64]*model.TypeInfo
rankCache []*model.RankInfo
mRankCache []*model.RankInfo
tcQestTick time.Duration
rankQuestTick time.Duration
promBeFormal *prom.Prom
infoc2 *anticheat.AntiCheat
}
// New create service instance and return.
func New(c *conf.Config) (s *Service) {
s = &Service{
c: c,
answerDao: dao.New(c),
geetestDao: geetestDao.New(c),
accountDao: accountDao.New(c),
memRPC: memrpc.New(c.RPCClient.Member),
missch: fanout.New("cache", fanout.Worker(1), fanout.Buffer(1024)),
beformalch: make(chan *model.Formal, 1024),
questionTypeCache: map[int64]*model.TypeInfo{},
rankCache: []*model.RankInfo{},
mRankCache: []*model.RankInfo{},
tcQestTick: time.Duration(c.Question.TcQestTick),
rankQuestTick: time.Duration(c.Question.RankQestTick),
promBeFormal: prom.New().WithCounter("answer_beformal_count", []string{"name"}),
}
accountSvc, err := accoutCli.NewClient(c.AccountRPC)
if err != nil {
panic(err)
}
s.accountSvc = accountSvc
s.loadQidsCache()
s.loadExtraQidsCache()
s.loadtypes()
go s.rankcacheproc()
go s.beformalproc()
if c.Infoc2 != nil {
s.infoc2 = anticheat.New(c.Infoc2)
}
return
}
func (s *Service) addRetryBeFormal(msg *model.Formal) {
select {
case s.beformalch <- msg:
default:
log.Warn("beformalch chan full")
}
}
func (s *Service) beformalproc() {
var (
err error
c = context.Background()
msg *model.Formal
)
for {
msg = <-s.beformalch
for retries := 0; retries < s.c.Answer.MaxRetries; retries++ {
if err = s.accountDao.BeFormal(c, msg.Mid, msg.IP); err != nil {
sleep := s.c.Backoff.Backoff(retries)
log.Error("beFormal fail(%d) sleep(%d) err(%+v)", msg.Mid, sleep, err)
time.Sleep(sleep * time.Second)
continue
}
break
}
}
}
// Close dao.
func (s *Service) Close() {
s.answerDao.Close()
}
func (s *Service) rankcacheproc() {
for {
time.Sleep(s.tcQestTick)
s.loadtypes()
s.loadQidsCache()
s.loadExtraQidsCache()
}
}
func (s *Service) userActionLog(mid int64, typ string, ah *model.AnswerHistory) {
report.User(&report.UserInfo{
Mid: mid,
Business: model.AnswerLogID,
Action: model.AnswerUpdate,
Ctime: time.Now(),
Content: map[string]interface{}{
typ: ah,
},
})
}

View File

@@ -0,0 +1,64 @@
package service
import (
"flag"
"os"
"testing"
"go-common/app/interface/main/answer/conf"
"github.com/smartystreets/goconvey/convey"
)
var (
s *Service
)
func TestMain(m *testing.M) {
if os.Getenv("DEPLOY_ENV") != "" {
flag.Set("app_id", "main.account-law.answer")
flag.Set("conf_appid", "main.account-law.answer")
flag.Set("conf_token", "ba3ee255695e8d7b46782268ddc9c8a3")
flag.Set("tree_id", "25260")
flag.Set("conf_version", "docker-1")
flag.Set("deploy_env", "uat")
flag.Set("conf_env", "10")
flag.Set("conf_host", "config.bilibili.co")
flag.Set("conf_path", "/tmp")
flag.Set("region", "sh")
flag.Set("zone", "sh001")
} else {
flag.Set("conf", "../cmd/answer-test.toml")
}
flag.Parse()
if err := conf.Init(); err != nil {
panic(err)
}
s = New(conf.Conf)
os.Exit(m.Run())
}
func TestServiceaddRetryBeFormal(t *testing.T) {
convey.Convey("addRetryBeFormal", t, func() {
s.addRetryBeFormal(nil)
})
}
func TestServicebeformalproc(t *testing.T) {
convey.Convey("beformalproc", t, func() {
s.beformalproc()
})
}
func TestServiceClose(t *testing.T) {
convey.Convey("Close", t, func() {
s.Close()
})
}
func TestServicerankcacheproc(t *testing.T) {
convey.Convey("rankcacheproc", t, func() {
s.rankcacheproc()
})
}

View File

@@ -0,0 +1,41 @@
package service
import (
"context"
"go-common/app/interface/main/answer/model"
accoutCli "go-common/app/service/main/account/api"
memModel "go-common/app/service/main/member/model"
"go-common/library/ecode"
"go-common/library/log"
"go-common/library/net/metadata"
)
// CheckBirthday check user had birthday info
func (s *Service) CheckBirthday(c context.Context, mid int64) (ok bool) {
var (
err error
res *memModel.BaseInfo
arg = &memModel.ArgMemberMid{Mid: mid, RemoteIP: metadata.String(c, metadata.RemoteIP)}
)
if res, err = s.memRPC.Base(c, arg); err != nil {
log.Error("s.accRPC.Detail(mid:%d) error (%v)", mid, err)
return
}
if res != nil && res.Birthday != 0 {
birthday := res.Birthday.Time().Format("2006-01-02")
if birthday != model.DefBirthday1 && birthday != model.DefBirthday2 {
ok = true
}
}
return
}
func (s *Service) accInfo(c context.Context, mid int64) (*accoutCli.Info, error) {
accInfo, err := s.accountSvc.Info3(c, &accoutCli.MidReq{Mid: mid})
if err != nil || accInfo == nil || accInfo.Info == nil {
log.Error("s.accRPC.Info(%d) error(%v)", mid, err)
return nil, ecode.AnswerAccCallErr
}
return accInfo.Info, nil
}

View File

@@ -0,0 +1,22 @@
package service
import (
"context"
"testing"
"github.com/smartystreets/goconvey/convey"
)
func TestServiceCheckBirthday(t *testing.T) {
convey.Convey("CheckBirthday", t, func() {
ok := s.CheckBirthday(context.Background(), 0)
convey.So(ok, convey.ShouldNotBeNil)
})
}
func TestServiceaccInfo(t *testing.T) {
convey.Convey("accInfo", t, func() {
ai, err := s.accInfo(context.Background(), 0)
convey.So(err, convey.ShouldBeNil)
convey.So(ai, convey.ShouldNotBeNil)
})
}