answer.go 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819
  1. package service
  2. import (
  3. "crypto/md5"
  4. "encoding/hex"
  5. "encoding/json"
  6. "fmt"
  7. "math"
  8. "math/rand"
  9. "strconv"
  10. "strings"
  11. "time"
  12. "unicode/utf8"
  13. "go-common/app/interface/main/answer/model"
  14. "go-common/library/ecode"
  15. "go-common/library/log"
  16. "go-common/library/text/translate/chinese"
  17. "github.com/pkg/errors"
  18. "golang.org/x/net/context"
  19. )
  20. const (
  21. _hashSalt = "bilirqeust"
  22. // _ansURI = "/answer/img?qs_id=%v&ans1_hash=%s&ans2_hash=%s&ans3_hash=%s&ans4_hash=%s"
  23. _baseTypeID = 36 // 官方基础题库
  24. _rankBtn = 7 * 24 * time.Hour
  25. _minType = 3
  26. _maxType = 10
  27. )
  28. var (
  29. _typeIdsMapping = map[int][]int{
  30. 100001: {15, 16, 17},
  31. 100002: {29, 30},
  32. 100003: {12, 13},
  33. // 22: 21,
  34. // 24: 23,
  35. // 35: 31, 36: 31,
  36. // 32: 30, 33: 30, 34: 30,
  37. // 29: 28, 37: 28, 7: 28, 8: 28,
  38. // 41: 5, 42: 5, 43: 5, 44: 5, 45: 5, 46: 5, 47: 5, 48: 5, 49: 5, 50: 5, 51: 5, 52: 5,
  39. }
  40. // 对外展示方式1
  41. _typeMap1 = []*model.TypeInfo{
  42. {Name: "游戏", Subs: []*model.SubType{
  43. {ID: 8, Name: "动作射击"},
  44. {ID: 9, Name: "冒险格斗"},
  45. {ID: 100003, Name: "策略模拟"},
  46. // {ID: 13, Name: "策略模拟"},
  47. {ID: 14, Name: "音乐体育"},
  48. }},
  49. {Name: "影视", Subs: []*model.SubType{
  50. {ID: 15, Name: "纪录片"},
  51. {ID: 16, Name: "电影"},
  52. {ID: 17, Name: "电视剧"},
  53. }},
  54. {Name: "科技", Subs: []*model.SubType{
  55. {ID: 18, Name: "军事"},
  56. {ID: 19, Name: "地理"},
  57. {ID: 20, Name: "历史"},
  58. {ID: 21, Name: "文学"},
  59. {ID: 22, Name: "数学"},
  60. {ID: 23, Name: "物理"},
  61. {ID: 24, Name: "化学"},
  62. {ID: 25, Name: "生物"},
  63. {ID: 26, Name: "数码科技"},
  64. }},
  65. {Name: "动画", Subs: []*model.SubType{
  66. {ID: 27, Name: "国创"},
  67. {ID: 28, Name: "番剧"},
  68. }},
  69. {Name: "艺术", Subs: []*model.SubType{
  70. {ID: 100002, Name: "音乐"},
  71. // {ID: 30, Name: "音乐"},
  72. {ID: 31, Name: "绘画"},
  73. }},
  74. {Name: "流行前线", Subs: []*model.SubType{
  75. {ID: 32, Name: "娱乐"},
  76. {ID: 33, Name: "时尚"},
  77. {ID: 34, Name: "运动"},
  78. }},
  79. {Name: "鬼畜", Subs: []*model.SubType{
  80. {ID: 35, Name: "鬼畜"},
  81. }},
  82. }
  83. // 推荐分区映射
  84. _recTypeIDMap = map[int]map[string][]int{
  85. 124: {"main_tid": []int{1, 167}, "sub_tid": []int{3, 129}},
  86. 127: {"main_tid": []int{1, 167}, "sub_tid": []int{3, 4}},
  87. 126: {"main_tid": []int{3, 119}, "sub_tid": []int{1}},
  88. 123: {"main_tid": []int{3}, "sub_tid": []int{129, 119}},
  89. 121: {"main_tid": []int{36, 177}, "sub_tid": []int{160}},
  90. 125: {"main_tid": []int{4}, "sub_tid": []int{129}},
  91. 129: {"main_tid": []int{36, 177}, "sub_tid": []int{160}},
  92. 130: {"main_tid": []int{23, 11}, "sub_tid": []int{160}},
  93. 128: {"main_tid": []int{119}, "sub_tid": []int{160}},
  94. }
  95. )
  96. // BaseQ base question.
  97. func (s *Service) BaseQ(c context.Context, mid int64, lang string, mobile bool) (res *model.AnsQueDetailList, err error) {
  98. var aqs *model.AnsQuesList
  99. if aqs, err = s.BaseQs(c, mid, lang, mobile); err != nil {
  100. err = errors.Wrapf(err, "s.ansRPC.BaseQs(%d,%t)", mid, mobile)
  101. return
  102. }
  103. res = s.convertModel(aqs)
  104. return
  105. }
  106. // BaseQs get base question
  107. func (s *Service) BaseQs(c context.Context, mid int64, lang string, mobile bool) (rqs *model.AnsQuesList, err error) {
  108. var (
  109. ids []int64
  110. now = time.Now()
  111. )
  112. if s.checkAnswerBlock(c, mid) {
  113. err = ecode.AnswerBlock
  114. return
  115. }
  116. h, err := s.history(c, mid)
  117. if err == nil && h != nil {
  118. if h.StartTime.Add(s.answerDuration()).After(now) && h.Score == 0 {
  119. if h.StepExtraCompleteTime != 0 {
  120. err = ecode.AnswerProNoPass // extra question pass
  121. return
  122. }
  123. err = ecode.AnswerExtraNoPass
  124. return
  125. }
  126. if h.Score > 0 && h.IsPassCaptcha == 0 {
  127. err = ecode.AnswerCaptchaNoPassed
  128. return
  129. }
  130. }
  131. ids, err = s.answerDao.IdsCache(c, mid, model.Q)
  132. if err != nil || len(ids) != s.c.Answer.BaseNum {
  133. ids, err = s.answerDao.QidByType(c, _baseTypeID, uint8(s.c.Answer.BaseNum))
  134. if err != nil {
  135. log.Error("s.answerDao.QidByType(%d,%d) error(%v)", _baseTypeID, s.c.Answer.BaseNum, err)
  136. return
  137. }
  138. if len(ids) == 0 {
  139. err = ecode.AnswerQsNumErr
  140. log.Error("qidByType ids len(%d) is 0", len(ids))
  141. return
  142. }
  143. }
  144. rqs, err = s.concatData(c, mid, ids, lang, mobile, s.c.Answer.BaseNum)
  145. if err != nil {
  146. log.Error("BaseQs s.concatData(%d, %d, %d) error(%v)", c, mid, ids, err)
  147. return
  148. }
  149. at := &model.AnswerTime{
  150. Stime: now,
  151. Etimes: 0,
  152. }
  153. err = s.answerDao.SetExpireCache(c, mid, at)
  154. rqs.CurrentTime = at.Stime
  155. rqs.EndTime = at.Stime.Add(s.answerDuration())
  156. s.answerDao.DelIdsCache(c, mid, model.BaseExtraPassQ)
  157. s.answerDao.DelIdsCache(c, mid, model.BaseExtraNoPassQ)
  158. return
  159. }
  160. // ConvertExtraQs extra question.
  161. func (s *Service) ConvertExtraQs(c context.Context, mid int64, lang string, mobile bool) (res *model.AnsQueDetailList, err error) {
  162. var ans *model.AnsQuesList
  163. if ans, err = s.ExtraQs(c, mid, lang, mobile); err != nil {
  164. err = errors.Wrapf(err, "s.ansRPC.ExtraQues(%d,%t)", mid, mobile)
  165. return
  166. }
  167. res = s.convertExtraModel(ans)
  168. return
  169. }
  170. // ExtraQs extra question.
  171. func (s *Service) ExtraQs(c context.Context, mid int64, lang string, mobile bool) (rqs *model.AnsQuesList, err error) {
  172. var (
  173. ids []int64
  174. passids []int64
  175. npassids []int64
  176. now = time.Now()
  177. )
  178. if s.checkAnswerBlock(c, mid) {
  179. err = ecode.AnswerBlock
  180. return
  181. }
  182. h, err := s.checkExtraState(c, mid, now)
  183. if err != nil {
  184. return
  185. }
  186. // keep on answer
  187. passids, _ = s.answerDao.IdsCache(c, mid, model.BaseExtraPassQ)
  188. npassids, err = s.answerDao.IdsCache(c, mid, model.BaseExtraNoPassQ)
  189. if err != nil || len(passids) != s.c.Answer.BaseExtraPassNum || len(npassids) != s.c.Answer.BaseExtraNoPassNum {
  190. var (
  191. ok bool
  192. )
  193. ok, passids, npassids = s.extraQueByBigData(c, mid, "")
  194. if !ok {
  195. // if bigdata get extra mid faild
  196. passids, err = s.answerDao.ExtraQidByType(c, model.BaseExtraPassQ, uint8(s.c.Answer.BaseExtraPassNum))
  197. if err != nil {
  198. log.Error("s.answerDao.ExtraQidByType(%d, %d, %d) error(%v)", model.BaseExtraPassQ, s.c.Answer.BaseExtraPassNum, len(passids), err)
  199. return
  200. }
  201. if len(passids) != s.c.Answer.BaseExtraPassNum {
  202. err = ecode.AnswerQsNumErr
  203. log.Warn("passids lenth(%d) neq BaseExtraPassNum(%d)", len(passids), s.c.Answer.BaseExtraPassNum)
  204. return
  205. }
  206. npassids, err = s.answerDao.ExtraQidByType(c, model.BaseExtraNoPassQ, uint8(s.c.Answer.BaseExtraNoPassNum))
  207. if err != nil {
  208. log.Error("s.answerDao.ExtraQidByType(%d, %d, %d) error(%v)", model.BaseExtraNoPassQ, s.c.Answer.BaseExtraNoPassNum, len(npassids), err)
  209. return
  210. }
  211. if len(npassids) != s.c.Answer.BaseExtraNoPassNum {
  212. err = ecode.AnswerQsNumErr
  213. log.Warn("npassids lenth(%d) neq BaseExtraNoPassNum(%d)", len(npassids), s.c.Answer.BaseExtraNoPassNum)
  214. return
  215. }
  216. }
  217. }
  218. ids = append(passids, npassids...)
  219. rqs, err = s.concatExtraData(c, mid, ids, passids, npassids, lang, mobile, s.c.Answer.BaseExtraPassNum+s.c.Answer.BaseExtraNoPassNum)
  220. if err != nil {
  221. log.Error("BaseExtraQs s.concatExtraData(%d, %d, %d) error(%v)", c, mid, ids, err)
  222. return
  223. }
  224. rqs.CurrentTime = now
  225. rqs.EndTime = h.StartTime.Add(s.answerDuration())
  226. if _, err = s.answerDao.UpdateExtraStartTime(c, h.ID, mid, now); err != nil {
  227. log.Error("s.answerDao.UpdateExtraStartTime( %d, %d) error(%v)", h.ID, mid, err)
  228. return
  229. }
  230. h.StepExtraStartTime = now
  231. h.Mtime = now
  232. s.userActionLog(mid, model.ExtraStartTime, h)
  233. s.answerDao.DelHistoryCache(c, mid)
  234. return
  235. }
  236. func (s *Service) checkExtraState(c context.Context, mid int64, now time.Time) (h *model.AnswerHistory, err error) {
  237. h, err = s.history(c, mid)
  238. if err != nil {
  239. log.Error("s.history(%v) is nil error(%v)", h, err)
  240. err = ecode.AnswerBaseNotPassed
  241. return
  242. }
  243. if h != nil {
  244. if h.Score > 0 && h.IsPassCaptcha == 0 {
  245. err = ecode.AnswerCaptchaNoPassed
  246. return
  247. }
  248. // if base pass
  249. if h.StartTime.Add(s.answerDuration()).After(now) && h.Score == 0 {
  250. if h.StepExtraCompleteTime != 0 {
  251. err = ecode.AnswerProNoPass
  252. }
  253. return
  254. }
  255. err = ecode.AnswerBaseNotPassed
  256. return
  257. }
  258. err = ecode.AnswerBaseNotPassed
  259. return
  260. }
  261. // ProTypes get promotion types.
  262. func (s *Service) proTypes(c context.Context, mid int64) (res *model.ProTypes, err error) {
  263. var (
  264. repro bool
  265. ah *model.AnswerHistory
  266. now = time.Now()
  267. )
  268. if s.checkAnswerBlock(c, mid) {
  269. err = ecode.AnswerBlock
  270. return
  271. }
  272. if ah, err = s.checkBase(c, mid, now); err != nil {
  273. return
  274. }
  275. qsidsMc, err := s.answerDao.IdsCache(c, mid, model.Q)
  276. if err == nil && len(qsidsMc) == s.c.Answer.ProNum {
  277. repro = true
  278. }
  279. res = &model.ProTypes{List: _typeMap1, EndTime: ah.StartTime.Add(s.answerDuration()), CurrentTime: now, Repro: repro}
  280. return
  281. }
  282. // ProType type.
  283. func (s *Service) ProType(c context.Context, mid int64, lang string) (res *model.AnsProType, err error) {
  284. var (
  285. repro string
  286. list = []*model.AnsTypeList{}
  287. )
  288. rpcRes, err := s.proTypes(c, mid)
  289. if err != nil {
  290. log.Error("s.proTypes(%+d) error (%v)", mid, err)
  291. return
  292. }
  293. for _, vt := range rpcRes.List {
  294. var sub = []*model.AnsType{}
  295. for _, vst := range vt.Subs {
  296. ansType := &model.AnsType{ID: vst.ID, Name: vst.Name}
  297. if lang == model.LangZhTW {
  298. ansType.Name = chinese.Convert(c, ansType.Name)
  299. }
  300. sub = append(sub, ansType)
  301. }
  302. ansTypeList := &model.AnsTypeList{Name: vt.Name, Fields: sub}
  303. if lang == model.LangZhTW {
  304. ansTypeList.Name = chinese.Convert(c, ansTypeList.Name)
  305. }
  306. list = append(list, ansTypeList)
  307. }
  308. if rpcRes.Repro {
  309. repro = "yes"
  310. } else {
  311. repro = "no"
  312. }
  313. res = &model.AnsProType{List: list, CurrentTime: rpcRes.CurrentTime.Unix(), EndTime: rpcRes.EndTime.Unix(), Repro: repro}
  314. return
  315. }
  316. // ConvertProQues pro question.
  317. func (s *Service) ConvertProQues(c context.Context, mid int64, tIds string, lang string, mobile bool) (res []*model.AnsQueDetail, err error) {
  318. var (
  319. ans *model.AnsQuesList
  320. ansdl *model.AnsQueDetailList
  321. )
  322. if ans, err = s.ProQues(c, mid, tIds, lang, mobile); err != nil {
  323. err = errors.Wrapf(err, "s.ProQues(%d,%v,%t)", mid, tIds, mobile)
  324. return
  325. }
  326. ansdl = s.convertModel(ans)
  327. res = ansdl.QuesList
  328. return
  329. }
  330. // ProQues question info.
  331. func (s *Service) ProQues(c context.Context, mid int64, qtsStr string, lang string, mobile bool) (rqs *model.AnsQuesList, err error) {
  332. var (
  333. ah *model.AnswerHistory
  334. now = time.Now()
  335. allQids []int64
  336. tIds, realTIDs []int
  337. )
  338. if s.checkAnswerBlock(c, mid) {
  339. err = ecode.AnswerBlock
  340. return
  341. }
  342. if ah, err = s.checkBase(c, mid, now); err != nil {
  343. return
  344. }
  345. allQids, err = s.answerDao.IdsCache(c, mid, model.Q)
  346. if err != nil || len(allQids) != s.c.Answer.ProNum {
  347. tIDStrArr := strings.Split(qtsStr, ",")
  348. if len(tIDStrArr) < _minType || len(tIDStrArr) > _maxType {
  349. err = ecode.AnswerTypeIDsErr
  350. return
  351. }
  352. if tIds, err = sliceAtoi(tIDStrArr); err != nil {
  353. err = ecode.AnswerTypeIDsErr
  354. return
  355. }
  356. for _, qt := range tIds {
  357. if qt <= 0 {
  358. err = ecode.RequestErr
  359. return
  360. }
  361. if mapIDS, ok := _typeIdsMapping[qt]; ok {
  362. realTIDs = append(realTIDs, mapIDS...)
  363. continue
  364. }
  365. realTIDs = append(realTIDs, qt)
  366. }
  367. num := math.Ceil(float64(s.c.Answer.ProNum) / float64(len(realTIDs)))
  368. log.Warn("realTIDs:%v", realTIDs)
  369. for _, qt := range realTIDs {
  370. var t []int64
  371. t, err = s.answerDao.QidByType(c, qt, uint8(num))
  372. if err != nil {
  373. log.Error("s.answerDao.QidByType(%d, %f, %d) error(%+v)", qt, num, len(t), err)
  374. return
  375. }
  376. if len(t) == 0 {
  377. log.Error("mid:%d the QidByType(%d, %f, %d) of len is 0", mid, qt, num, len(t))
  378. err = ecode.AnswerMidDBQueErr
  379. return
  380. }
  381. allQids = append(allQids, t...)
  382. }
  383. if len(allQids) == 0 || len(allQids) < s.c.Answer.ProNum {
  384. 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)
  385. err = ecode.NothingFound
  386. return
  387. }
  388. }
  389. if rqs, err = s.concatData(c, mid, allQids, lang, mobile, s.c.Answer.ProNum); err != nil {
  390. log.Error("ProQues s.concatData(%d, %d, %d) error(%v)", c, mid, allQids, err)
  391. return
  392. }
  393. if _, err = s.answerDao.UpdateStepTwoTime(c, ah.ID, mid, now); err != nil {
  394. return
  395. }
  396. ah.StepTwoStartTime = now
  397. ah.Mtime = now
  398. s.userActionLog(mid, model.ProQues, ah)
  399. s.answerDao.DelHistoryCache(c, mid)
  400. return
  401. }
  402. func (s *Service) checkBase(c context.Context, mid int64, now time.Time) (ah *model.AnswerHistory, err error) {
  403. ah, err = s.history(c, mid)
  404. if err != nil || ah == nil || ah.StartTime.Add(s.answerDuration()).Before(now) || ah.Score != 0 || ah.StepOneCompleteTime == 0 {
  405. err = ecode.AnswerBaseNotPassed
  406. log.Error("checkBase(%d, %v) AnswerExpire error(%v)", mid, now, err)
  407. return
  408. }
  409. if ah.StepExtraCompleteTime == 0 {
  410. err = ecode.AnswerExtraNoPass
  411. return
  412. }
  413. if ah.Score > 0 && ah.IsPassCaptcha == 0 {
  414. err = ecode.AnswerCaptchaNoPassed
  415. }
  416. return
  417. }
  418. func (s *Service) checkTime(c context.Context, mid int64, now time.Time) (at *model.AnswerTime, rs bool) {
  419. var err error
  420. if at, err = s.answerDao.ExpireCache(c, mid); err != nil {
  421. return
  422. }
  423. if at == nil || at.Stime.Add(s.answerDuration()).Before(now) {
  424. return
  425. }
  426. rs = true
  427. return
  428. }
  429. func (s *Service) concatData(c context.Context, mid int64, ids []int64, lang string, mobile bool, qs int) (rqs *model.AnsQuesList, err error) {
  430. var (
  431. list []*model.AnsQue
  432. qm map[int64]*model.Question
  433. )
  434. if qm, err = s.answerDao.ByIds(c, ids); err != nil {
  435. log.Error("s.answerDao.ByIds(%v) error(%v)", ids, err)
  436. err = ecode.NothingFound
  437. return
  438. }
  439. for _, d := range ids {
  440. i := qm[d]
  441. rq := s.imgPosition(c, i, mid, lang, mobile)
  442. list = append(list, rq)
  443. }
  444. if len(list) > qs {
  445. list = list[:qs]
  446. }
  447. rqs = &model.AnsQuesList{QuesList: list}
  448. if err := s.answerDao.SetIdsCache(c, mid, ids, model.Q); err != nil {
  449. log.Error("s.answerDao.SetIdsCache(%d, %d) error(%v)", mid, ids, err)
  450. }
  451. log.Info("s.concatData load que success(%d, %v, %v, %d)", mid, ids, mobile, qs)
  452. return
  453. }
  454. 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) {
  455. var (
  456. list []*model.AnsQue
  457. qm map[int64]*model.ExtraQst
  458. )
  459. if qm, err = s.answerDao.ExtraByIds(c, ids); err != nil || len(qm) < qs {
  460. log.Error("s.answerDao.ExtraByIds(%v) error(%+v)", ids, err)
  461. return
  462. }
  463. for _, d := range ids {
  464. i := qm[d]
  465. rq := s.imgExtraPosition(c, i, mid, lang, mobile)
  466. list = append(list, rq)
  467. }
  468. if len(list) > qs {
  469. list = list[:qs]
  470. }
  471. rqs = &model.AnsQuesList{QuesList: list}
  472. if err = s.answerDao.SetIdsCache(c, mid, passids, model.BaseExtraPassQ); err != nil {
  473. log.Error("s.answerDao.SetIdsCache(%d, %d) error(%v)", mid, passids, err)
  474. return
  475. }
  476. if err = s.answerDao.SetIdsCache(c, mid, nopassids, model.BaseExtraNoPassQ); err != nil {
  477. log.Error("s.answerDao.SetIdsCache(%d, %d) error(%v)", mid, nopassids, err)
  478. return
  479. }
  480. log.Info("s.concatData extra load que success(%d, %v, %v, %d)", mid, ids, mobile, qs)
  481. return
  482. }
  483. // ansHash get answer hash.
  484. func (s *Service) ansHash(mid int64, ans string) (ansHash string) {
  485. h := md5.New()
  486. h.Write([]byte(fmt.Sprintf("%s%d%s", ans, mid, _hashSalt)))
  487. return hex.EncodeToString(h.Sum(nil))
  488. }
  489. func (s *Service) imgPosition(c context.Context, qs *model.Question, mid int64, lang string, mobile bool) (rq *model.AnsQue) {
  490. var (
  491. y float64
  492. qsLineLength float64 = 36
  493. questionFontSize float64 = 10
  494. questionTitleSize float64 = 12
  495. ans = make([]*model.AnsPosition, 4)
  496. imgStr = "v3_%s_A-%s_B-%s_C-%s_D-%s_%s"
  497. p = map[bool]string{true: "H5", false: "PC"}
  498. bfsHost = "https://i0.hdslb.com/bfs/member/"
  499. as [4]string
  500. )
  501. rq = &model.AnsQue{ID: qs.ID}
  502. if mobile {
  503. qsLineLength = 11
  504. questionFontSize = 12
  505. questionTitleSize = 16
  506. }
  507. qsLength := utf8.RuneCountInString(qs.Question)
  508. if float64(qsLength) > qsLineLength {
  509. line := math.Ceil(float64(qsLength) / qsLineLength)
  510. rq.Height = 2 * line * questionTitleSize
  511. rq.PositionY = y
  512. } else {
  513. rq.Height = 2 * questionTitleSize
  514. }
  515. y = rq.Height
  516. if lang == model.LangZhTW {
  517. qs.Question = chinese.Convert(c, qs.Question)
  518. qs.Ans[0] = chinese.Convert(c, qs.Ans[0])
  519. qs.Ans[1] = chinese.Convert(c, qs.Ans[1])
  520. qs.Ans[2] = chinese.Convert(c, qs.Ans[2])
  521. qs.Ans[3] = chinese.Convert(c, qs.Ans[3])
  522. }
  523. idx := rand.Perm(4)
  524. for i := range qs.Ans {
  525. ans[i] = &model.AnsPosition{
  526. AnsHash: s.ansHash(mid, qs.Ans[idx[i]]),
  527. Height: 2 * questionFontSize,
  528. PositionY: y,
  529. }
  530. y += 2 * questionFontSize
  531. as[i] = qs.Ans[idx[i]]
  532. }
  533. m := md5.New()
  534. m.Write([]byte(fmt.Sprintf(imgStr, strconv.FormatInt(qs.ID, 10), as[0], as[1], as[2], as[3], p[mobile])))
  535. fname := hex.EncodeToString(m.Sum(nil)) + ".jpg"
  536. if s.c.Answer.Debug {
  537. fname = fmt.Sprintf("debug_%s", fname)
  538. }
  539. rq.Img = bfsHost + fname
  540. rq.Ans = ans
  541. return
  542. }
  543. func (s *Service) imgExtraPosition(c context.Context, qs *model.ExtraQst, mid int64, lang string, mobile bool) (rq *model.AnsQue) {
  544. var (
  545. y float64
  546. qsLineLength float64 = 36
  547. questionFontSize float64 = 10
  548. questionTitleSize float64 = 12
  549. ans = make([]*model.AnsPosition, 2)
  550. imgStr = "%s_A-%s_B-%s_%s"
  551. p = map[bool]string{true: "H5", false: "PC"}
  552. bfsHost = "https://i0.hdslb.com/bfs/member/"
  553. as [2]string
  554. )
  555. rq = &model.AnsQue{ID: qs.ID}
  556. if mobile {
  557. qsLineLength = 11
  558. questionFontSize = 12
  559. questionTitleSize = 16
  560. }
  561. qsLength := utf8.RuneCountInString(qs.Question)
  562. if float64(qsLength) > qsLineLength {
  563. line := math.Ceil(float64(qsLength) / qsLineLength)
  564. rq.Height = 2 * line * questionTitleSize
  565. rq.PositionY = y
  566. } else {
  567. rq.Height = 2 * questionTitleSize
  568. }
  569. y = rq.Height
  570. if lang == model.LangZhTW {
  571. as = [2]string{chinese.Convert(c, model.ExtraAnsA), chinese.Convert(c, model.ExtraAnsB)}
  572. } else {
  573. as = [2]string{model.ExtraAnsA, model.ExtraAnsB}
  574. }
  575. for k, v := range as {
  576. ans[k] = &model.AnsPosition{
  577. AnsHash: s.ansHash(mid, v),
  578. Height: 2 * questionFontSize,
  579. PositionY: y,
  580. }
  581. y += 2 * questionFontSize
  582. }
  583. m := md5.New()
  584. m.Write([]byte(fmt.Sprintf(imgStr, strconv.FormatInt(qs.OriginID, 10), as[0], as[1], p[mobile])))
  585. fname := hex.EncodeToString(m.Sum(nil)) + ".jpg"
  586. if s.c.Answer.Debug {
  587. fname = fmt.Sprintf("debug_%s", fname)
  588. }
  589. rq.Img = bfsHost + fname
  590. rq.Ans = ans
  591. return
  592. }
  593. func (s *Service) loadQidsCache() {
  594. qs, err := s.answerDao.QidsByState(context.Background(), model.PassCheck)
  595. if len(qs) == 0 || err != nil {
  596. log.Error("s.answerDao.loadQidsCache(%d) size is zero error(%v)", model.PassCheck, err)
  597. }
  598. qmap := map[int8][]int64{}
  599. for _, q := range qs {
  600. qmap[q.TypeID] = append(qmap[q.TypeID], q.ID)
  601. }
  602. for k, v := range qmap {
  603. s.answerDao.DelQidsCache(context.Background(), int(k))
  604. s.answerDao.SetQids(context.Background(), v, int(k))
  605. }
  606. log.Info("s.answerDao.loadQidsCache suc(%v)", qmap)
  607. }
  608. func (s *Service) loadExtraQidsCache() {
  609. qs, err := s.answerDao.QidsExtraByState(context.Background(), model.MaxLoadQueSize)
  610. if len(qs) == 0 || err != nil {
  611. log.Error("s.answerDao.QidsExtraByState(%d) size is zero error(%v)", model.MaxLoadQueSize, err)
  612. return
  613. }
  614. qmap := map[int8][]int64{}
  615. for _, q := range qs {
  616. qmap[q.Ans] = append(qmap[q.Ans], q.ID)
  617. }
  618. for k, v := range qmap {
  619. s.answerDao.DelExtraQidsCache(context.Background(), k)
  620. s.answerDao.SetExtraQids(context.Background(), v, k)
  621. }
  622. log.Info("s.answerDao.loadExtraQidsCache suc(%v)", qmap)
  623. }
  624. // Cool .
  625. func (s *Service) Cool(c context.Context, hid, mid int64) (cool *model.AnsCool, err error) {
  626. var (
  627. his *model.AnswerHistory
  628. types []*model.TypeInfo
  629. li = []*model.CoolPower{
  630. {Name: "动画", Num: 0},
  631. {Name: "艺术", Num: 0},
  632. {Name: "游戏", Num: 0},
  633. {Name: "科技", Num: 0},
  634. {Name: "影视", Num: 0},
  635. {Name: "鬼畜", Num: 0},
  636. }
  637. completeResult = make(map[int8]int64)
  638. )
  639. his, err = s.historyByHid(c, hid)
  640. if err != nil {
  641. return
  642. }
  643. cool = &model.AnsCool{
  644. Score: his.Score,
  645. IsSameUser: his.Mid == mid,
  646. IsFirstPass: his.IsFirstPass,
  647. Level: his.PassedLevel,
  648. Share: &model.CoolShare{},
  649. VideoInfo: &model.CoolVideo{},
  650. Rank: &model.CoolRank{},
  651. }
  652. cool.CanShowRankBtn = his.Score >= 85 && his.Mtime.Before(time.Now().Add(_rankBtn))
  653. r := _pendantIDNameMap[int(his.RankID)]
  654. if r != "" {
  655. if rid, ok := _oldPIDToNewMap[his.RankID]; ok {
  656. his.RankID = rid
  657. }
  658. rs := _rankShire[his.RankID]
  659. idx := rand.Perm(len(rs.VideoArr))
  660. cool.ViewMore = rs.ViewMore
  661. cool.Share = rs.Share
  662. cool.VideoInfo = rs.VideoArr[idx[0]]
  663. cool.Rank = &model.CoolRank{
  664. ID: int(his.RankID),
  665. Name: r,
  666. Img: "https://i0.hdslb.com" + _pendantIDImgMap[his.RankID],
  667. }
  668. }
  669. us, err := s.accInfo(c, his.Mid)
  670. if err != nil || us == nil {
  671. log.Error("CheckQueCaptcha accInfo(%d) info is null error(%v)", mid, err)
  672. return
  673. }
  674. cool.Name = us.Name
  675. cool.Face = us.Face
  676. if err = json.Unmarshal([]byte(his.CompleteResult), &completeResult); err != nil {
  677. log.Error("json.Unmarshal(%s) error(%v)", his.CompleteResult, err)
  678. err = nil
  679. }
  680. log.Info("hid(%d), completeResult: %v+", hid, completeResult)
  681. for k := range completeResult {
  682. for _, t := range s.questionTypeCache {
  683. if len(t.Subs) != 0 {
  684. for _, s := range t.Subs {
  685. if int64(k) == s.ID {
  686. types = append(types, &model.TypeInfo{ID: s.ID, Name: s.Name, LabelName: s.LabelName})
  687. }
  688. }
  689. }
  690. if int64(k) == t.ID {
  691. types = append(types, t)
  692. }
  693. }
  694. }
  695. log.Info("hid(%d), cool types: %v+", hid, types)
  696. for _, t := range types {
  697. for _, p := range li {
  698. if p.Name == t.LabelName {
  699. p.Num += completeResult[int8(t.ID)]
  700. }
  701. }
  702. }
  703. log.Info("hid(%d), power types: %v+", hid, li)
  704. cool.Powers = append(cool.Powers, li...)
  705. if _, ok := _recTypeIDMap[int(his.RankID)]; ok {
  706. cool.MainTids = _recTypeIDMap[int(his.RankID)]["main_tid"]
  707. cool.SubTids = _recTypeIDMap[int(his.RankID)]["sub_tid"]
  708. }
  709. return
  710. }
  711. // ExtraScore .
  712. func (s *Service) ExtraScore(c context.Context, mid int64) (res *model.ExtraScoreReply, err error) {
  713. res = &model.ExtraScoreReply{}
  714. h, err := s.history(c, mid)
  715. if err != nil {
  716. return
  717. }
  718. res.Score = h.StepExtraScore + int64(s.c.Answer.BaseNum)
  719. return
  720. }
  721. func (s *Service) history(c context.Context, mid int64) (ah *model.AnswerHistory, err error) {
  722. cok := true
  723. if ah, err = s.answerDao.HistoryCache(c, mid); err != nil {
  724. cok = false
  725. return
  726. }
  727. if ah != nil {
  728. return
  729. }
  730. ah, err = s.answerDao.History(c, mid)
  731. if err != nil {
  732. return
  733. }
  734. if ah != nil && cok {
  735. s.answerDao.SetHistoryCache(c, mid, ah)
  736. }
  737. return
  738. }
  739. func (s *Service) answerDuration() (d time.Duration) {
  740. return time.Duration(s.c.Answer.Duration) * time.Minute
  741. }
  742. func sliceAtoi(sa []string) ([]int, error) {
  743. si := make([]int, 0, len(sa))
  744. for _, a := range sa {
  745. i, err := strconv.Atoi(a)
  746. if err != nil {
  747. return si, err
  748. }
  749. si = append(si, i)
  750. }
  751. return si, nil
  752. }
  753. func (s *Service) extraQueByBigData(c context.Context, mid int64, ip string) (ok bool, passids []int64, npassids []int64) {
  754. passids, npassids, err := s.accountDao.ExtraIds(c, mid, ip)
  755. if err != nil || len(passids) != s.c.Answer.BaseExtraPassNum || len(npassids) != s.c.Answer.BaseExtraNoPassNum {
  756. return
  757. }
  758. ids := append(passids, npassids...)
  759. if qm, err := s.answerDao.ExtraByIds(c, ids); err != nil || len(qm) != (s.c.Answer.BaseExtraPassNum+s.c.Answer.BaseExtraNoPassNum) {
  760. log.Error("s.answerDao.ExtraByIds(%v) error(%v)", ids, err)
  761. return
  762. }
  763. ok = true
  764. return
  765. }
  766. func (s *Service) loadtypes() (t map[int64]*model.TypeInfo) {
  767. tys, err := s.answerDao.Types(context.Background())
  768. if err != nil {
  769. log.Error("s.questionDao.Types error(%v)", err)
  770. return
  771. }
  772. tmp := map[int64]*model.TypeInfo{}
  773. for _, v := range tys {
  774. if v.Parentid == 0 && tmp[v.ID] == nil {
  775. tmp[v.ID] = &model.TypeInfo{ID: v.ID, Name: v.Name, Subs: []*model.SubType{}}
  776. } else if tmp[v.Parentid] != nil {
  777. tmp[v.Parentid].Subs = append(tmp[v.Parentid].Subs, &model.SubType{ID: v.ID, Name: v.Name, LabelName: v.LabelName})
  778. }
  779. }
  780. s.questionTypeCache = tmp
  781. t = tmp
  782. log.Info("load question type cacheproc success,%v", t)
  783. return
  784. }