answercheck.go 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746
  1. package service
  2. import (
  3. "context"
  4. "encoding/json"
  5. "fmt"
  6. "strconv"
  7. "time"
  8. "go-common/app/interface/main/answer/conf"
  9. "go-common/app/interface/main/answer/model"
  10. accoutCli "go-common/app/service/main/account/api"
  11. "go-common/library/ecode"
  12. "go-common/library/log"
  13. "go-common/library/log/infoc"
  14. "go-common/library/net/metadata"
  15. "go-common/library/text/translate/chinese"
  16. )
  17. var (
  18. // concat type type_id : _pendantIDNameMap id
  19. _rankIDPendantMap = map[int]int{
  20. // 11: 6,
  21. // 12: 7,
  22. // 14: 8,
  23. // 15: 9,
  24. // 17: 11,
  25. // 27: 10,
  26. // 28: 12,
  27. // 41: 13,
  28. // 9: 14,
  29. 27: 124,
  30. 28: 127,
  31. 31: 126,
  32. 29: 123,
  33. 18: 121,
  34. 8: 125,
  35. 19: 129,
  36. 15: 130,
  37. 7: 128,
  38. }
  39. // concat pendant
  40. _pendantIDNameMap = map[int]string{
  41. 5: "哔哩王",
  42. 6: "声控",
  43. 7: "追番党",
  44. 8: "调教师",
  45. 9: "动感DJ",
  46. 10: "局座",
  47. 11: "攻略组",
  48. 12: "学霸",
  49. 13: "迷影者",
  50. 14: "全明星",
  51. 122: "哔哩王",
  52. 124: "声控",
  53. 127: "追番党",
  54. 126: "调教师",
  55. 123: "动感DJ",
  56. 121: "局座",
  57. 125: "攻略组",
  58. 129: "学霸",
  59. 130: "迷影者",
  60. 128: "全明星",
  61. }
  62. // 老挂件id对应新挂件id
  63. _oldPIDToNewMap = map[int]int{
  64. 5: 122,
  65. 6: 124,
  66. 7: 127,
  67. 8: 126,
  68. 9: 123,
  69. 10: 121,
  70. 11: 125,
  71. 12: 129,
  72. 13: 130,
  73. 14: 128,
  74. 122: 122,
  75. 124: 124,
  76. 127: 127,
  77. 126: 126,
  78. 123: 123,
  79. 121: 121,
  80. 125: 125,
  81. 129: 129,
  82. 130: 130,
  83. 128: 128,
  84. }
  85. _pendantIDImgMap = map[int]string{
  86. 122: "/bfs/face/67ed957ae789852bcc59b1c1e3097ea23179f793.png",
  87. 124: "/bfs/face/ff61b405cdcf8f7860c67293218340aeaed6e233.png",
  88. 127: "/bfs/face/369098093a07af821b767eac44b51f97ee8501c5.png",
  89. 126: "/bfs/face/9e775c3ebe224a774d4b2f99fd5be342eb6f51ec.png",
  90. 123: "/bfs/face/939fa982d8b1c1fd653de5c7890db03d62e87226.png",
  91. 121: "/bfs/face/7f6b5cb11ea7abd2e05b04f65f190dfb10456554.png",
  92. 125: "/bfs/face/90cc47168e40326dc934fad7b9abb82aa748d6ac.png",
  93. 129: "/bfs/face/42869dad53926c75e3010150c15b16a8925fb268.png",
  94. 130: "/bfs/face/3d5ee491c125bf452b2dbec082dbb8209b645316.png",
  95. 128: "/bfs/face/b53937110e8009a720e2426ea69c449483718b3c.png",
  96. }
  97. // 125: "攻略组",--> 题库(8,9,12,13,14)
  98. // 130: "迷影者",--> 题库(15,16,17)
  99. // 121: "局座",--> 题库(18)
  100. // 129: "学霸",--> 题库(19,20,21,22,23,24,25,26)
  101. // 124: "声控",--> 题库(27)
  102. // 127: "追番党",--> 题库(28)
  103. // 126: "调教师",--> 题库(31)
  104. // 123: "动感DJ",--> 题库(30,29)
  105. // 128: "全明星",--> 题库(35,34,33,32)
  106. // 122: "哔哩王",
  107. // 分区合并归类
  108. _typeIDMap = map[int][]int{
  109. // 11: {12, 13}, // 动漫作品+动漫内容
  110. // 15: {15, 16}, // ACG+三次元音乐
  111. // 17: {17, 18, 19, 20, 21, 22, 23, 24, 25}, // 各类游戏
  112. // 28: {28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 7, 8}, // 科学技术+音频+视频技术
  113. // 41: {41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52}, // 各类影视剧
  114. 8: {8, 9, 12, 13, 14}, // 游戏
  115. 19: {19, 20, 21, 22, 23, 24, 25, 26}, // 科技
  116. 15: {15, 16, 17}, // 影视
  117. 29: {29, 30}, // 音乐
  118. 7: {7, 35, 34, 33, 32}, // 鬼畜+流行前线
  119. }
  120. // 兼容账号rank错误
  121. _rank0 = int32(0)
  122. )
  123. const (
  124. _unBindTel = 0
  125. )
  126. // ProCheck check second step questions
  127. func (s *Service) ProCheck(c context.Context, mid int64, ids []int64, ansHash map[int64]string, lang string) (hid int64, err error) {
  128. var now = time.Now()
  129. if len(ids) != s.c.Answer.ProNum {
  130. err = ecode.AnswerQsNumErr
  131. return
  132. }
  133. if s.checkAnswerBlock(c, mid) {
  134. err = ecode.AnswerBlock
  135. return
  136. }
  137. ah, err := s.history(c, mid)
  138. if err != nil || ah == nil || ah.StartTime.Add(s.answerDuration()).Before(now) || ah.Score != 0 {
  139. err = ecode.AnswerBaseNotPassed
  140. return
  141. }
  142. if (now.Unix() - ah.StepTwoStartTime.Unix()) < s.c.Answer.BlockedTimestamp {
  143. s.answerDao.SetBlockCache(c, mid)
  144. log.Error("member user answer block, time space(%v)", now.Unix()-ah.StepTwoStartTime.Unix())
  145. err = ecode.AnswerBlock
  146. return
  147. }
  148. qsidsMc, err := s.answerDao.IdsCache(c, mid, model.Q)
  149. if err != nil {
  150. err = ecode.AnswerMidCacheQidsErr
  151. log.Error("s.answerDao.IdsCache(%d) err(%v) ", mid, err)
  152. return
  153. }
  154. ok, err := s.checkQsIDs(c, ids, mid, qsidsMc, s.c.Answer.ProNum)
  155. if !ok {
  156. return
  157. }
  158. errIds, rc, err := s.checkAns(c, mid, ids, ansHash, lang, s.c.Answer.ProNum)
  159. if err != nil {
  160. return
  161. }
  162. rcJSON, err := json.Marshal(rc)
  163. if err != nil {
  164. log.Error("json.Marshal(%v) error(%v)", rc, err)
  165. return
  166. }
  167. total := s.c.Answer.BaseNum + s.c.Answer.ProNum + int(ah.StepExtraScore)
  168. score := total - len(errIds)
  169. ahDB := &model.AnswerHistory{
  170. ID: ah.ID,
  171. Hid: ah.Hid,
  172. CompleteResult: string(rcJSON),
  173. CompleteTime: now,
  174. Score: int8(score),
  175. IsFirstPass: 0,
  176. }
  177. log.Info("user: %d, score:%d, his: %v", mid, score, ahDB)
  178. member, err := s.accInfo(c, mid)
  179. if err == nil && member != nil && score >= model.Score60 && member.Rank == model.UserInfoRank {
  180. ahDB.IsFirstPass = 1
  181. }
  182. ahDB.RankID = s.pendant(c, ahDB, mid, metadata.String(c, metadata.RemoteIP), rc)
  183. r, err := s.answerDao.SetHistory(c, mid, ahDB)
  184. if err != nil || r != 1 {
  185. return
  186. }
  187. ah.CompleteResult = ahDB.CompleteResult
  188. ah.CompleteTime = ahDB.CompleteTime
  189. ah.Score = ahDB.Score
  190. ah.IsFirstPass = ahDB.IsFirstPass
  191. ah.RankID = ahDB.RankID
  192. ah.Mtime = now
  193. s.userActionLog(mid, model.ProCheck, ah)
  194. if ahDB.Score >= model.Score60 && ahDB.RankID > 0 {
  195. if hid, _, err = s.answerDao.PendantHistory(c, mid); err != nil {
  196. return
  197. }
  198. if hid <= 0 {
  199. s.answerDao.AddPendantHistory(c, mid, ah.Hid)
  200. }
  201. }
  202. hid = ah.Hid
  203. s.missch.Do(c, func(ctx context.Context) {
  204. s.answerDao.DelHistoryCache(ctx, mid)
  205. s.answerDao.DelIdsCache(ctx, mid, model.Q)
  206. })
  207. return
  208. }
  209. // CheckBase check base question all
  210. func (s *Service) CheckBase(c context.Context, mid int64, ids []int64, ansHas map[int64]string, lang string) (res *model.AnsCheck, err error) {
  211. var (
  212. now = time.Now()
  213. errIds []int64
  214. profileReply *accoutCli.ProfileReply
  215. )
  216. // 检查手机绑定
  217. if profileReply, err = s.accountSvc.Profile3(c, &accoutCli.MidReq{Mid: mid}); err != nil || profileReply == nil || profileReply.Profile == nil {
  218. log.Error("s.accRPC.Profile3(%d) err(%+v)", mid, err)
  219. err = ecode.AnswerAccCallErr
  220. return
  221. }
  222. if profileReply.Profile.TelStatus == _unBindTel {
  223. err = ecode.AnswerNeedBindTel
  224. return
  225. }
  226. if len(ids) < s.c.Answer.BaseNum {
  227. err = ecode.RequestErr
  228. return
  229. }
  230. if s.checkAnswerBlock(c, mid) {
  231. err = ecode.AnswerBlock
  232. return
  233. }
  234. res = &model.AnsCheck{}
  235. at, ok := s.checkTime(c, mid, now)
  236. if !ok {
  237. err = ecode.AnswerTimeExpire
  238. return
  239. }
  240. if len(ids) != s.c.Answer.BaseNum {
  241. err = ecode.AnswerQsNumErr
  242. return
  243. }
  244. qsIdsMc, err := s.answerDao.IdsCache(c, mid, model.Q)
  245. if err != nil {
  246. log.Error("s.answerDao.IdsCache(%d) err(%v) ", mid, err)
  247. err = ecode.AnswerMidCacheQidsErr
  248. return
  249. }
  250. ok, err = s.checkQsIDs(c, ids, mid, qsIdsMc, s.c.Answer.BaseNum)
  251. if err != nil || !ok {
  252. return
  253. }
  254. errIds, _, err = s.checkAns(c, mid, ids, ansHas, lang, s.c.Answer.BaseNum)
  255. res.QidList = errIds
  256. if err != nil {
  257. return
  258. }
  259. if len(errIds) > 0 {
  260. return
  261. }
  262. s.basePass(c, mid, at, now)
  263. res.Pass = true
  264. return
  265. }
  266. // Captcha get question captcha
  267. func (s *Service) Captcha(c context.Context, mid int64, clientType string, newCaptcha int) (res *model.ProcessRes, err error) {
  268. if s.checkAnswerBlock(c, mid) {
  269. err = ecode.AnswerBlock
  270. return
  271. }
  272. ah, err := s.history(c, mid)
  273. if err != nil || ah == nil || ah.Score == model.Score0 {
  274. log.Info("answer Captcha(%d) answer history is null or score is zero err(%v) ", mid, err)
  275. if ah != nil {
  276. if ah.StepOneCompleteTime == 0 {
  277. err = ecode.AnswerBaseNotPassed
  278. return
  279. }
  280. if ah.StepExtraCompleteTime == 0 {
  281. err = ecode.AnswerExtraNoPass
  282. return
  283. }
  284. }
  285. err = ecode.AnswerProNoPass
  286. return
  287. }
  288. if ah.IsPassCaptcha == model.CaptchaPass {
  289. err = ecode.AnswerCaptchaPassed
  290. return
  291. }
  292. if !conf.Conf.Answer.Captcha {
  293. if res, err = s.preProcess(c, mid, metadata.String(c, metadata.RemoteIP), clientType, newCaptcha); err == nil {
  294. return
  295. }
  296. log.Error("s.preProcess(%d,%s,%d) err:%+v", mid, clientType, newCaptcha, err)
  297. }
  298. var token, url string
  299. if token, url, err = s.answerDao.Captcha(c); err != nil {
  300. return
  301. }
  302. res = &model.ProcessRes{
  303. Token: token,
  304. URL: url,
  305. CaptchaType: model.BiliCaptcha,
  306. }
  307. return
  308. }
  309. // Validate check question captcha
  310. func (s *Service) Validate(c context.Context, challenge, validate, seccode, clientType string, success int, mid int64,
  311. cookie, captchaType string, comargs map[string]string) (res *model.AnsCheck, err error) {
  312. var now = time.Now()
  313. if s.checkAnswerBlock(c, mid) {
  314. err = ecode.AnswerBlock
  315. return
  316. }
  317. res = &model.AnsCheck{}
  318. ah, err := s.history(c, mid)
  319. log.Info(" Validate ah (%d) res(%v) ", mid, ah)
  320. if err != nil || ah == nil || ah.Score == model.Score0 {
  321. log.Info("answer Validate(%d) answer history is null or score is zero err(%v) ", mid, err)
  322. if ah != nil {
  323. if ah.StepOneCompleteTime == 0 {
  324. err = ecode.AnswerBaseNotPassed
  325. return
  326. }
  327. if ah.StepExtraCompleteTime == 0 {
  328. err = ecode.AnswerExtraNoPass
  329. return
  330. }
  331. }
  332. err = ecode.AnswerProNoPass
  333. return
  334. }
  335. // passed go to next page
  336. if ah.IsPassCaptcha == model.CaptchaPass {
  337. res.Pass = true
  338. res.HistoryID = ah.Hid
  339. return
  340. }
  341. ip := metadata.String(c, metadata.RemoteIP)
  342. switch captchaType {
  343. case model.BiliCaptcha:
  344. if err = s.answerDao.Verify(c, validate, seccode, ip); err != nil {
  345. log.Error("answerDao.Verify(%s,%s,%s) error:%+v", validate, seccode, ip, err)
  346. return
  347. }
  348. res.Pass = true
  349. default:
  350. if ok := s.validate(c, challenge, validate, seccode, clientType, ip, success, mid); !ok {
  351. log.Error("Validate validate(%v,%v,%v,%v,%v,%d) error(%v)", challenge, validate, seccode, clientType, success, mid, err)
  352. err = ecode.AnswerGeetestVaErr
  353. return
  354. }
  355. res.Pass = true
  356. }
  357. member, err := s.accInfo(c, mid)
  358. if err != nil || member == nil {
  359. log.Error("Validate accInfo(%d) info is null error(%v)", mid, err)
  360. return
  361. }
  362. if _, err = s.answerDao.UpdateCaptcha(c, ah.ID, ah.Mid, model.CaptchaPass); err != nil {
  363. log.Error("s.answerDao.UpdateCaptcha error (%v) ", err)
  364. err = ecode.ServerErr
  365. return
  366. }
  367. ah.IsPassCaptcha = model.CaptchaPass
  368. ah.Mtime = now
  369. s.userActionLog(mid, model.Captcha, ah)
  370. s.answerDao.DelHistoryCache(c, mid)
  371. log.Info(" Validate member (%v) rank(%d) ", ah, member.Rank)
  372. if ah.Score >= model.Score60 && (member.Rank == model.UserInfoRank || member.Rank == _rank0) {
  373. log.Info(" beFormal in (%d) ", mid)
  374. s.sendData(c, comargs, ah, ip)
  375. if err = s.accountDao.BeFormal(c, mid, ip); err != nil {
  376. log.Error(" beFormal fail(%d) err(%v)", mid, err)
  377. s.addRetryBeFormal(&model.Formal{Mid: mid, IP: ip})
  378. err = ecode.AnswerFormalFailed
  379. return
  380. }
  381. s.answerDao.UpdateLevel(c, ah.ID, ah.Mid, 1, 1)
  382. ah.IsFirstPass = 1
  383. ah.PassedLevel = 1
  384. ah.Mtime = now
  385. s.userActionLog(mid, model.Level, ah)
  386. s.answerDao.DelHistoryCache(c, mid)
  387. s.PendantRec(c, &model.ReqPendant{HID: ah.Hid, MID: mid})
  388. }
  389. res.HistoryID = ah.Hid
  390. return
  391. }
  392. // checkQsIDs check question id param.
  393. func (s *Service) checkQsIDs(c context.Context, ids []int64, mid int64, qsIdsMc []int64, qs int) (ok bool, err error) {
  394. if qsIdsMc == nil {
  395. log.Error("CheckBase.qsIdsMc is nil (%d,%v) )", mid, qsIdsMc)
  396. err = ecode.AnswerMidCacheQidsErr
  397. return
  398. }
  399. if len(ids) != qs {
  400. err = ecode.AnswerQsNumErr
  401. return
  402. }
  403. qidMap := map[int64]bool{}
  404. for _, v := range qsIdsMc {
  405. qidMap[v] = true
  406. }
  407. i := 0
  408. for _, v := range ids {
  409. if qidMap[v] {
  410. i++
  411. }
  412. }
  413. if i == qs {
  414. ok = true
  415. } else {
  416. err = ecode.AnswerQidDiffRequestErr
  417. }
  418. return
  419. }
  420. // checkAns check question ans.
  421. 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) {
  422. qs, err := s.answerDao.ByIds(c, ids)
  423. if err != nil || qs == nil || len(qs) != count {
  424. log.Error("checkAns.qs is nil (%v,%v) error(%v)", ids, qs, err)
  425. err = ecode.AnswerMidDBQueErr
  426. return
  427. }
  428. errIds = []int64{}
  429. rc = make(map[int8]int)
  430. for _, q := range qs {
  431. if lang == model.LangZhTW {
  432. q.Ans[0] = chinese.Convert(c, q.Ans[0])
  433. }
  434. if h := s.ansHash(mid, q.Ans[0]); h != ansHash[q.ID] {
  435. errIds = append(errIds, q.ID)
  436. } else {
  437. rc[q.TypeID]++
  438. }
  439. }
  440. return
  441. }
  442. // basePass base question pass.
  443. func (s *Service) basePass(c context.Context, mid int64, at *model.AnswerTime, now time.Time) {
  444. h := &model.AnswerHistory{
  445. Mid: mid,
  446. StartTime: at.Stime,
  447. StepOneErrTimes: at.Etimes,
  448. StepOneCompleteTime: now.Unix() - at.Stime.Unix(),
  449. Ctime: now,
  450. Mtime: now,
  451. }
  452. r, hid, err := s.answerDao.AddHistory(c, mid, h)
  453. if err != nil || r != 1 {
  454. log.Error("answerDao.AddHistory r !=1 (%d,%v) error(%v)", mid, h, err)
  455. return
  456. }
  457. h.Hid, _ = strconv.ParseInt(hid, 10, 64)
  458. s.userActionLog(mid, model.BasePass, h)
  459. s.answerDao.DelHistoryCache(c, mid)
  460. s.answerDao.DelExpireCache(c, mid)
  461. s.answerDao.DelIdsCache(c, mid, model.Q)
  462. }
  463. // setPendant set pendant.
  464. func (s *Service) pendant(c context.Context, ah *model.AnswerHistory, mid int64, ip string, rc map[int8]int) (rankID int) {
  465. var (
  466. ok bool
  467. ht int
  468. typeIDScore = map[int8]int{ // key:_typeIDMap`key,value:Score
  469. 8: 0,
  470. 19: 0,
  471. 15: 0,
  472. 29: 0,
  473. 7: 0,
  474. }
  475. )
  476. if ah.Score == model.FullScore {
  477. return model.RankTop // 122: "哔哩王",
  478. }
  479. for k, v := range rc {
  480. switch k {
  481. case 8, 9, 12, 13, 14: // 游戏 125: "攻略组",--> 题库(8,9,12,13,14)
  482. typeIDScore[8] += v
  483. case 15, 16, 17: // 影视 130: "迷影者",--> 题库(15,16,17)
  484. typeIDScore[15] += v
  485. case 19, 20, 21, 22, 23, 24, 25, 26: // 科技 129: "学霸",--> 题库(19,20,21,22,23,24,25,26)
  486. typeIDScore[19] += v
  487. case 29, 30: // 音乐 123: "动感DJ",--> 题库(30,29)
  488. typeIDScore[29] += v
  489. case 7, 35, 34, 33, 32: // 鬼畜+流行前线 128: "全明星",--> 题库(35,34,33,32)
  490. typeIDScore[7] += v
  491. default:
  492. // 121: "局座",--> 题库(18)
  493. // 124: "声控",--> 题库(27)
  494. // 127: "追番党",--> 题库(28)
  495. // 126: "调教师",--> 题库(31)
  496. typeIDScore[k] += v
  497. }
  498. }
  499. score := 0
  500. for k, v := range typeIDScore {
  501. if score < v {
  502. score = v
  503. ht = int(k)
  504. }
  505. }
  506. rankID, ok = _rankIDPendantMap[ht]
  507. if !ok {
  508. log.Warn("user(%d),pendant() rankId(%d) result:%+v ", mid, _rankIDPendantMap[ht], rc)
  509. }
  510. return
  511. }
  512. func (s *Service) checkAnswerBlock(c context.Context, mid int64) (block bool) {
  513. block, _ = s.answerDao.CheckBlockCache(c, mid)
  514. return
  515. }
  516. func (s *Service) sendData(c context.Context, comargs map[string]string, ah *model.AnswerHistory, ip string) {
  517. s.promBeFormal.Incr("count")
  518. // add report bigdata log
  519. ans := []interface{}{
  520. strconv.FormatInt(ah.StepOneCompleteTime, 10),
  521. ah.CompleteResult,
  522. strconv.FormatInt(ah.CompleteTime.Unix()-ah.StepTwoStartTime.Unix(), 10),
  523. fmt.Sprintf("%d", ah.Score),
  524. strconv.FormatInt(time.Now().Unix(), 10),
  525. }
  526. s.missch.Do(c, func(ctx context.Context) {
  527. ac := map[string]string{
  528. "itemType": infoc.ItemTypeLV,
  529. "action": infoc.ActionAnswer,
  530. "ip": ip,
  531. "mid": strconv.FormatInt(ah.Mid, 10),
  532. "sid": comargs["sid"],
  533. "ua": comargs["ua"],
  534. "buvid": comargs["buvid"],
  535. "refer": comargs["refer"],
  536. "url": comargs["url"],
  537. }
  538. log.Info("s.infoc2.ServiceAntiCheatBus(%v,%v)", ac, ans)
  539. s.infoc2.ServiceAntiCheatBus(ac, ans)
  540. })
  541. }
  542. // ExtraCheck extra check.
  543. 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) {
  544. var now = time.Now()
  545. if len(ids) < s.c.Answer.ExtraNum {
  546. err = ecode.RequestErr
  547. return
  548. }
  549. if s.checkAnswerBlock(c, mid) {
  550. err = ecode.AnswerBlock
  551. return
  552. }
  553. ah, err := s.history(c, mid)
  554. if err != nil || ah == nil || ah.StartTime.Add(s.answerDuration()).Before(now) || ah.Score != 0 {
  555. err = ecode.AnswerBaseNotPassed
  556. return
  557. }
  558. if len(ids) != (s.c.Answer.BaseExtraPassNum + s.c.Answer.BaseExtraNoPassNum) {
  559. return ecode.AnswerQsNumErr
  560. }
  561. passids, err := s.answerDao.IdsCache(c, mid, model.BaseExtraPassQ)
  562. if err != nil {
  563. log.Error("s.answerDao.IdsCache(%d) extra pass err(%v) ", mid, err)
  564. return ecode.AnswerMidCacheQidsErr
  565. }
  566. nopassids, err := s.answerDao.IdsCache(c, mid, model.BaseExtraNoPassQ)
  567. if err != nil {
  568. log.Error("s.answerDao.IdsCache(%d) extra nopass err(%v) ", mid, err)
  569. return ecode.AnswerMidCacheQidsErr
  570. }
  571. idsmc := append(passids, nopassids...)
  572. ok, err := s.checkQsIDs(c, ids, mid, idsmc, s.c.Answer.BaseExtraPassNum+s.c.Answer.BaseExtraNoPassNum)
  573. if err != nil || !ok {
  574. return
  575. }
  576. ret, qs, _ := s.checkExtraPassAns(c, mid, passids, ansHash, lang, s.c.Answer.BaseExtraPassNum)
  577. ah.StepExtraScore = int64(ret * s.c.Answer.BaseExtraScore)
  578. ah.StepExtraCompleteTime = now.Unix() - ah.StartTime.Unix()
  579. if _, err = s.answerDao.UpdateExtraRet(c, ah.ID, mid, ah.StepExtraCompleteTime, ah.StepExtraScore); err != nil {
  580. log.Error("s.answerDao.UpdateExtraRet(%d) err(%v) ", mid, err)
  581. return
  582. }
  583. ah.Mtime = now
  584. s.userActionLog(mid, model.ExtraCheck, ah)
  585. s.answerDao.DelHistoryCache(c, mid)
  586. s.answerDao.DelIdsCache(c, mid, model.BaseExtraPassQ)
  587. s.answerDao.DelIdsCache(c, mid, model.BaseExtraNoPassQ)
  588. // send answer ret to bigdata
  589. rs, err := s.sendExtraRetMsg(c, mid, qs, nopassids, ansHash, s.c.Answer.BaseExtraNoPassNum)
  590. if err != nil {
  591. log.Error("s.sendExtraRetMsg(%d,%v,%v,%v) err(%v) ", mid, qs, nopassids, ansHash, err)
  592. return
  593. }
  594. s.answerDao.PubExtraRet(c, mid, &model.DataBusResult{
  595. Mid: mid,
  596. Buvid: buvid,
  597. IP: metadata.String(c, metadata.RemoteIP),
  598. Ua: ua,
  599. Refer: refer,
  600. Score: int8(ah.StepExtraScore),
  601. Rs: rs,
  602. Hid: ah.Hid,
  603. })
  604. return
  605. }
  606. // checkExtraPassAns check extra question ans.
  607. 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) {
  608. qs, err = s.answerDao.ExtraByIds(c, ids)
  609. if err != nil || qs == nil || len(qs) != count {
  610. log.Error("checkAns extra qs is nil (%v,%v) error(%v)", ids, qs, err)
  611. err = ecode.AnswerMidDBQueErr
  612. return
  613. }
  614. for _, q := range qs {
  615. var ans string
  616. switch q.Ans {
  617. case model.NormalQ:
  618. if lang == model.LangZhTW {
  619. ans = s.ansHash(mid, chinese.Convert(c, model.ExtraAnsA))
  620. } else {
  621. ans = s.ansHash(mid, model.ExtraAnsA)
  622. }
  623. case model.ViolationQ:
  624. if lang == model.LangZhTW {
  625. ans = s.ansHash(mid, chinese.Convert(c, model.ExtraAnsB))
  626. } else {
  627. ans = s.ansHash(mid, model.ExtraAnsB)
  628. }
  629. }
  630. if ansHash[q.ID] == ans {
  631. ret++
  632. }
  633. }
  634. return
  635. }
  636. func (s *Service) sendExtraRetMsg(c context.Context, mid int64, passqs map[int64]*model.ExtraQst, nopassids []int64,
  637. ansHash map[int64]string, count int) (rs []*model.Rs, err error) {
  638. var (
  639. qs map[int64]*model.ExtraQst
  640. )
  641. qs, err = s.answerDao.ExtraByIds(c, nopassids)
  642. if err != nil || qs == nil || len(qs) != count {
  643. log.Error("checkAns extra nopassqs is nil (%v) error(%v)", qs, err)
  644. err = ecode.AnswerMidDBQueErr
  645. return
  646. }
  647. for k, v := range passqs {
  648. qs[k] = v
  649. }
  650. for _, q := range qs {
  651. var (
  652. userAns int8
  653. )
  654. ansA := s.ansHash(mid, model.ExtraAnsA)
  655. ansB := s.ansHash(mid, model.ExtraAnsB)
  656. switch ansHash[q.ID] {
  657. case ansA:
  658. userAns = model.NormalQ
  659. case ansB:
  660. userAns = model.ViolationQ
  661. default:
  662. userAns = model.UnKownQ
  663. }
  664. rs = append(rs, &model.Rs{
  665. ID: q.OriginID,
  666. Question: q.Question,
  667. Ans: userAns,
  668. TrueAns: q.Ans,
  669. AvID: q.AvID,
  670. Status: q.Status,
  671. Source: q.Source,
  672. Ctime: q.Ctime,
  673. Mtime: q.Mtime,
  674. })
  675. }
  676. return
  677. }
  678. // PendantRec .
  679. func (s *Service) PendantRec(c context.Context, arg *model.ReqPendant) (err error) {
  680. var (
  681. ok bool
  682. status int8
  683. hid, ret int64
  684. his *model.AnswerHistory
  685. )
  686. if hid, status, err = s.answerDao.PendantHistory(c, arg.MID); err != nil {
  687. return
  688. }
  689. if hid != arg.HID {
  690. log.Warn("mid(%d) arg.hid(%d) db.hid(%d) is invald!", arg.MID, arg.HID, hid)
  691. return
  692. }
  693. if status != model.PendantNotGet {
  694. log.Warn("mid(%d) hid(%d) not first get!", arg.MID, arg.HID)
  695. return
  696. }
  697. his, err = s.historyByHid(c, arg.HID)
  698. if err != nil {
  699. return
  700. }
  701. if his.Score < model.Score60 || his.IsFirstPass != 1 {
  702. log.Warn("mid(%d) hid(%d) score(%d) isFirstPass(%d) not pass or first answer !", arg.MID, arg.HID, his.Score, his.IsFirstPass)
  703. return
  704. }
  705. if _, ok = _pendantIDNameMap[int(his.RankID)]; !ok {
  706. log.Warn("mid(%d) get illegal pid(%d) by answer first!", arg.MID, int(his.RankID))
  707. return
  708. }
  709. if ret, err = s.answerDao.UpPendantHistory(c, arg.MID, arg.HID); err != nil {
  710. return
  711. }
  712. if ret <= 0 {
  713. log.Warn("mid(%d) hid(%d) pid(%d) history answer not get!", arg.MID, arg.HID, int(his.RankID))
  714. return
  715. }
  716. s.missch.Do(c, func(ctx context.Context) {
  717. if pendantErr := s.accountDao.GivePendant(ctx, arg.MID, int64(his.RankID), model.PenDantDays, metadata.String(c, metadata.RemoteIP)); pendantErr != nil {
  718. log.Error("s.accountDao.GivePendant(%d,%d) error(%+v)", arg.MID, int64(his.RankID), pendantErr)
  719. }
  720. })
  721. return
  722. }