cache.go 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600
  1. package dao
  2. import (
  3. "context"
  4. "encoding/json"
  5. "fmt"
  6. "time"
  7. "go-common/app/service/openplatform/anti-fraud/model"
  8. "go-common/library/cache/redis"
  9. "go-common/library/ecode"
  10. "go-common/library/log"
  11. "go-common/library/xstr"
  12. )
  13. const (
  14. _keyBankQuestions = "AntiFraud:BANK_%d:QUESTIONS"
  15. _keyQuestionFetchTime = "AntiFraud:USER_%s:ITEM_%d_%d_%s_PLATFORM:%d_Date"
  16. // 上次调起组件的id
  17. _keyComponentID = "AntiFraud:USER_%s:ITEM_%d_%d_%s_PLATFORM:%d_COMID"
  18. // 回答过的问题 id
  19. _keyAnsweredIds = "AntiFraud:USER_%s:ITEM_%d_%d_%s_PLATFORM:%d_ANSWER_IDS"
  20. // 组件内获取题目次数
  21. _keyBindBank = "AntiFraud:BIND_BANK_ITEM_%s"
  22. _keyComponentTimes = "AntiFraud:USER_%s:ITEM_%d_%d_%s_PLATFORM:%d_ANSWERTIMES"
  23. _keyBankID = "AntiFraud:BANKID_%d"
  24. //答案ids缓存
  25. _keyAnswerIds = "AntiFraud:AnswerIds_%d"
  26. // 图片id
  27. _keyPicID = "AntiFraud:AnswerAllPic"
  28. // 缓存所有图片id
  29. _keyPicIds = "AntiFraud:AnswerPic_%d"
  30. //默认100条数据
  31. _limit = 100
  32. // 缓存问题
  33. _keyQusInfo = "AntiFraud:QusInfo_%d"
  34. // 缓存图片
  35. _keyAnswerPicID = "AntiFraud:USER_%s:ITEM_%d_%d_%s_AnswerPic:%d_COMID"
  36. //答题日志list
  37. _keyAddLog = "AntiFraud:AddLog"
  38. //答案缓存
  39. _keyAnswer = "AntiFraud:Answer_%d"
  40. // 5分钟缓存
  41. _fiveMinuts = 5 * time.Minute
  42. )
  43. // PingRedis check redis connection
  44. func (d *Dao) PingRedis(c context.Context) (err error) {
  45. conn := d.redis.Get(c)
  46. defer conn.Close()
  47. _, err = conn.Do("PING")
  48. return
  49. }
  50. // GetUserQuestionCache get
  51. func (d *Dao) GetUserQuestionCache(c context.Context, args *model.ArgGetQuestion, bankID int64) {
  52. key := fmt.Sprintf("USER_%s_ITEM_%d_%d_%s_PLATFORM_%d_BANK_%d", args.UID, args.Source, args.TargetItemType, args.TargetItem, args.Platform, bankID)
  53. d.RedisDo(c, "GET", key)
  54. }
  55. // RedisDo redis cmd
  56. func (d *Dao) RedisDo(c context.Context, cmd string, args ...interface{}) (reply interface{}, err error) {
  57. conn := d.redis.Get(c)
  58. defer conn.Close()
  59. return conn.Do(cmd, args...)
  60. }
  61. // SetBankQuestionsCache 将题库下的全部问题存入缓存
  62. func (d *Dao) SetBankQuestionsCache(c context.Context, bankID int64, ids []int64) (err error) {
  63. key := fmt.Sprintf(_keyBankQuestions, bankID)
  64. err = d.SetObj(c, key, ids, time.Hour)
  65. if err != nil {
  66. log.Error("d.SetBankQuestionsCache() error(%v)", err)
  67. return
  68. }
  69. return
  70. }
  71. // GetBankQuestionsCache 从缓存获取题库下的全部问题
  72. func (d *Dao) GetBankQuestionsCache(c context.Context, bankID int64) (ids []int64) {
  73. key := fmt.Sprintf(_keyBankQuestions, bankID)
  74. reply, err := redis.Bytes(d.RedisDo(c, "GET", key))
  75. if err == redis.ErrNil {
  76. return
  77. }
  78. if err != nil {
  79. log.Error("查询 redis 出错 d.GetBankQuestionsCache(%d) error(%v)", bankID, err)
  80. return
  81. }
  82. err = json.Unmarshal(reply, &ids)
  83. if err != nil {
  84. return
  85. }
  86. return
  87. }
  88. // QusFetchTime 上次题目拉取时间
  89. func (d *Dao) QusFetchTime(c context.Context, args *model.ArgGetQuestion) (ts int64) {
  90. ts, _ = redis.Int64(d.RedisDo(c, "GET", d.GetQusKey(_keyQuestionFetchTime, args)))
  91. return
  92. }
  93. // SetQusFetchTime 设置上次题目拉取时间
  94. func (d *Dao) SetQusFetchTime(c context.Context, args *model.ArgGetQuestion, ts int64) (err error) {
  95. err = d.Setex(c, d.GetQusKey(_keyQuestionFetchTime, args), ts, _fiveMinuts)
  96. if err != nil {
  97. log.Error("d.SetQusFetchTime(%v, %d) error(%v)", args, ts, err)
  98. }
  99. return
  100. }
  101. // GetQusKey getkey
  102. func (d *Dao) GetQusKey(format string, args *model.ArgGetQuestion) (s string) {
  103. s = fmt.Sprintf(format, args.UID, args.Source, args.TargetItemType, args.TargetItem, args.Platform)
  104. return
  105. }
  106. // Setex set
  107. func (d *Dao) Setex(c context.Context, key string, data interface{}, exp time.Duration) error {
  108. log.Info(" d.setex(%s, %v, %d)", key, data, exp)
  109. _, err := d.RedisDo(c, "SETEX", key, int(exp/1e9), data)
  110. if err != nil {
  111. log.Error("d.Setex(%s, %v) error(%v)", key, data, err)
  112. }
  113. return err
  114. }
  115. // SetObj set
  116. func (d *Dao) SetObj(c context.Context, key string, obj interface{}, exp time.Duration) error {
  117. log.Info(" d.setex(%s, %v, %d)", key, obj, exp)
  118. data, err := json.Marshal(obj)
  119. if err != nil {
  120. return err
  121. }
  122. _, err = d.RedisDo(c, "SETEX", key, int(exp/1e9), data)
  123. if err != nil {
  124. log.Error("d.Setex(%s, %v) error(%v)", key, string(data), err)
  125. }
  126. return err
  127. }
  128. //GetObj get
  129. func (d *Dao) GetObj(c context.Context, key string, obj interface{}) (err error) {
  130. reply, err := redis.Bytes(d.RedisDo(c, "GET", key))
  131. if err != nil {
  132. return
  133. }
  134. err = json.Unmarshal(reply, obj)
  135. if err != nil {
  136. return
  137. }
  138. return
  139. }
  140. // GetAnsweredID 获取已回答问题
  141. func (d *Dao) GetAnsweredID(c context.Context, args *model.ArgGetQuestion) (ids []int64) {
  142. key := d.GetQusKey(_keyAnsweredIds, args)
  143. ids, err := redis.Int64s(d.RedisDo(c, "SMEMBERS", key))
  144. if err != nil {
  145. if err == redis.ErrNil {
  146. err = nil
  147. } else {
  148. log.Error("d.GetAnsweredID(%v) error(%v)", args, err)
  149. }
  150. }
  151. return
  152. }
  153. // SetAnsweredID 设置已回答问题
  154. func (d *Dao) SetAnsweredID(c context.Context, args *model.ArgGetQuestion, questionID int64) (err error) {
  155. key := d.GetQusKey(_keyAnsweredIds, args)
  156. _, err = d.RedisDo(c, "SADD", key, questionID)
  157. if err != nil {
  158. log.Error("d.SetAnsweredID(%v, %d) error(%v)", args, questionID, err)
  159. }
  160. _, err = d.RedisDo(c, "EXPIRE", key, int(_fiveMinuts/1e9))
  161. if err != nil {
  162. log.Error("d.SetAnsweredID expire (%v, %d) error(%v)", args, questionID, err)
  163. }
  164. return
  165. }
  166. // RmAnsweredID 删除已回答问题
  167. func (d *Dao) RmAnsweredID(c context.Context, args *model.ArgGetQuestion) (err error) {
  168. key := d.GetQusKey(_keyAnsweredIds, args)
  169. _, err = d.RedisDo(c, "DEL", key)
  170. if err != nil {
  171. log.Error("d.SetAnsweredID(%v, %d) error(%v)", args, err)
  172. }
  173. return
  174. }
  175. // GetComponentID 获取组件id
  176. func (d *Dao) GetComponentID(c context.Context, args *model.ArgGetQuestion) (cID int, err error) {
  177. key := d.GetQusKey(_keyComponentID, args)
  178. if cID, err = redis.Int(d.RedisDo(c, "GET", key)); err != nil {
  179. if err == redis.ErrNil {
  180. err = nil
  181. } else {
  182. log.Error("d.GetComponentID(%v) error(%v)", args, err)
  183. }
  184. }
  185. return
  186. }
  187. // SetComponentID 设置组件id
  188. func (d *Dao) SetComponentID(c context.Context, args *model.ArgGetQuestion) (err error) {
  189. key := d.GetQusKey(_keyComponentID, args)
  190. err = d.Setex(c, key, args.ComponentID, _fiveMinuts)
  191. if err != nil {
  192. log.Error("d.SetComponentID(%v) error(%v)", args, err)
  193. }
  194. return
  195. }
  196. // GetComponentTimes 获取组件答题次数
  197. func (d *Dao) GetComponentTimes(c context.Context, args *model.ArgGetQuestion) (cID int64, err error) {
  198. key := d.GetQusKey(_keyComponentTimes, args)
  199. if cID, err = redis.Int64(d.RedisDo(c, "GET", key)); err != nil {
  200. if err == redis.ErrNil {
  201. err = nil
  202. } else {
  203. log.Error("d.GetComponentTimes(%v) error(%v)", args, err)
  204. }
  205. }
  206. return
  207. }
  208. // SetComponentTimes 设置组件答题次数
  209. func (d *Dao) SetComponentTimes(c context.Context, args *model.ArgGetQuestion) (err error) {
  210. key := d.GetQusKey(_keyComponentTimes, args)
  211. err = d.Setex(c, key, 0, _fiveMinuts)
  212. if err != nil {
  213. log.Error("d.SetComponentID(%v) error(%v)", args, err)
  214. }
  215. return
  216. }
  217. // IncrComponentTimes 组件计数
  218. func (d *Dao) IncrComponentTimes(c context.Context, args *model.ArgGetQuestion) (err error) {
  219. key := d.GetQusKey(_keyComponentTimes, args)
  220. _, err = d.RedisDo(c, "INCR", key)
  221. if err != nil {
  222. log.Error("d.GetComponentTimes(%v) error(%v)", args, err)
  223. }
  224. return
  225. }
  226. // GetQusBankInfoCache get
  227. func (d *Dao) GetQusBankInfoCache(c context.Context, qbid int64) (oi *model.QuestionBank, err error) {
  228. oi = &model.QuestionBank{}
  229. key := fmt.Sprintf(_keyBankID, qbid)
  230. err = d.GetObj(c, key, oi)
  231. if err == redis.ErrNil {
  232. err = nil
  233. oi, err = d.GetQusBankInfo(c, qbid)
  234. if err != nil {
  235. log.Error("d.GetQusBankInfoCache error(%v)", err)
  236. return
  237. }
  238. err = d.SetObj(c, key, oi, _fiveMinuts)
  239. }
  240. return
  241. }
  242. // GetBindBankInfo get
  243. func (d *Dao) GetBindBankInfo(c context.Context, source, targetItemType int8, targetItem string) (bind *model.QuestionBankBind, err error) {
  244. bind = &model.QuestionBankBind{}
  245. key := fmt.Sprintf(_keyBindBank, targetItem)
  246. err = d.GetObj(c, key, bind)
  247. if err == redis.ErrNil {
  248. //err = nil
  249. binds, err1 := d.GetBindBank(c, source, targetItemType, []string{targetItem})
  250. if err1 != nil {
  251. log.Error("s.GetQuestion(%v) error(%v)", targetItem, err)
  252. err = err1
  253. return
  254. }
  255. if len(binds) < 1 {
  256. log.Warn("s.GetQuestion(%v) 未找到题库绑定关系", targetItem)
  257. err = ecode.BindBankNotFound
  258. return
  259. }
  260. bind = binds[0]
  261. if bind.QuestionBank == nil {
  262. log.Error("s.GetQuestion(%v) 未找到已绑定的题库", targetItem)
  263. err = ecode.QusbNotFound
  264. return
  265. }
  266. err = d.SetObj(c, key, bind, time.Hour)
  267. //return
  268. }
  269. return
  270. }
  271. // CorrectAnswerIds id
  272. func (d *Dao) CorrectAnswerIds(c context.Context, qid int64) (ids []int64, err error) {
  273. key := fmt.Sprintf(_keyAnswerIds, qid)
  274. err = d.GetObj(c, key, &ids)
  275. if err == redis.ErrNil {
  276. err = nil
  277. answers, err1 := d.GetAnswerList(c, qid)
  278. for _, answer := range answers {
  279. if answer.IsCorrect == 1 {
  280. ids = append(ids, answer.AnswerID)
  281. }
  282. }
  283. if err1 != nil {
  284. err = err1
  285. log.Error("d.GetQusBankInfoCache error(%v)", err)
  286. return
  287. }
  288. err1 = d.SetObj(c, key, ids, time.Hour)
  289. err = err1
  290. return
  291. }
  292. return
  293. }
  294. // GetRandPic 背景图
  295. func (d *Dao) GetRandPic(c context.Context, args *model.ArgGetQuestion) (oi *model.QuestBkPic, err error) {
  296. key := _keyPicID
  297. cnt, err := redis.Int(d.RedisDo(c, "LLen", key))
  298. if err != nil {
  299. if err == redis.ErrNil {
  300. err = nil
  301. } else {
  302. log.Error("GetStatisticsCache do(RPOP, %s) error(%v)", key, err)
  303. }
  304. return
  305. }
  306. if cnt == 0 {
  307. err1 := d.PushAllPic(c)
  308. if err1 != nil {
  309. if err1 == redis.ErrNil {
  310. err = nil
  311. } else {
  312. log.Error("PushAllPic do(RPOP, %s) error(%v)", key, err)
  313. }
  314. return
  315. }
  316. }
  317. id, err := redis.Int(d.RedisDo(c, "RPOP", key))
  318. if err != nil {
  319. if err == redis.ErrNil {
  320. err = nil
  321. } else {
  322. log.Error("GetStatisticsCache do(RPOP, %s) error(%v)", key, err)
  323. }
  324. return
  325. }
  326. oi, err = d.GetPic(c, id)
  327. if err != nil {
  328. return
  329. }
  330. //缓存坐标
  331. picKey := d.GetQusKey(_keyAnswerPicID, args)
  332. err = d.SetObj(c, picKey, oi, time.Hour)
  333. if err != nil {
  334. return
  335. }
  336. return
  337. }
  338. // PushAllPic 事先放入redis list中
  339. func (d *Dao) PushAllPic(c context.Context) (err error) {
  340. cnt, err := d.GetPicCount(c)
  341. limit := _limit
  342. page := (int)(cnt / limit)
  343. for i := 0; i <= page; i++ {
  344. key := _keyPicID
  345. ids, _ := d.GetAllPicIds(c, i*limit, limit)
  346. a := make([]interface{}, 0)
  347. a = append(a, key)
  348. for _, id := range ids {
  349. a = append(a, id)
  350. }
  351. if len(a) > 1 {
  352. _, err := d.RedisDo(c, "LPUSH", a...)
  353. if err != nil {
  354. log.Error("[PushAllPic]conn.Do(lpush, %s) error(%v)", key, err)
  355. }
  356. }
  357. }
  358. return
  359. }
  360. // GetPic 获取背景图
  361. func (d *Dao) GetPic(c context.Context, id int) (oi *model.QuestBkPic, err error) {
  362. key := fmt.Sprintf(_keyPicIds, id)
  363. oi = &model.QuestBkPic{}
  364. err = d.GetObj(c, key, oi)
  365. if err == redis.ErrNil {
  366. err = nil
  367. picInfo, err1 := d.GetRandomPic(c, id)
  368. if err1 != nil {
  369. err = err1
  370. log.Error("d.GetPic error(%v)", err1)
  371. return
  372. }
  373. err1 = d.SetObj(c, key, picInfo, time.Hour)
  374. oi = picInfo
  375. err = err1
  376. return
  377. }
  378. return
  379. }
  380. // PushAnswer push
  381. func (d *Dao) PushAnswer(c context.Context, answer *model.ArgCheckAnswer, isCorrect int8) (affect int64, err error) {
  382. ids := xstr.JoinInts(answer.Answers)
  383. obj := model.AddLog{
  384. UID: answer.UID,
  385. QsID: answer.QsID,
  386. Platform: answer.Platform,
  387. Source: answer.Source,
  388. Ids: ids,
  389. IsCorrect: isCorrect,
  390. }
  391. data, err := json.Marshal(obj)
  392. if err != nil {
  393. return
  394. }
  395. _, err = d.RedisDo(c, "LPUSH", _keyAddLog, data)
  396. if err != nil {
  397. log.Error("[PushAllPic]conn.Do(RPOP,%s) error(%v)", "_keyAddLog", err)
  398. }
  399. affect = obj.QsID
  400. return
  401. }
  402. // PopAnswer pop
  403. func (d *Dao) PopAnswer(c context.Context) {
  404. for {
  405. reply, err := redis.Bytes(d.RedisDo(c, "RPOP", _keyAddLog))
  406. if len(reply) > 0 && err == nil {
  407. answer := &model.AddLog{}
  408. err = json.Unmarshal(reply, answer)
  409. if err == nil {
  410. _, err = d.db.Exec(c, _addUserAnswerSQL, answer.UID, answer.QsID, answer.Platform, answer.Source, answer.Ids, answer.IsCorrect)
  411. if err != nil {
  412. log.Error("d.PopAnswer error(%v)", err)
  413. time.Sleep(time.Second * 1)
  414. continue
  415. }
  416. }
  417. }
  418. time.Sleep(time.Second * 5)
  419. }
  420. }
  421. // GetCacheQus get
  422. func (d *Dao) GetCacheQus(c context.Context, id int64) (oi *model.Question, err error) {
  423. key := fmt.Sprintf(_keyQusInfo, id)
  424. oi = &model.Question{}
  425. err = d.GetObj(c, key, oi)
  426. if err == redis.ErrNil {
  427. err = nil
  428. info, err1 := d.GetQusInfo(c, id)
  429. if err1 != nil {
  430. err = err1
  431. log.Error("d.GetPic error(%v)", err1)
  432. return
  433. }
  434. err1 = d.SetObj(c, key, info, time.Hour)
  435. oi = info
  436. err = err1
  437. return
  438. }
  439. return
  440. }
  441. // GetCacheAnswerPic get
  442. func (d *Dao) GetCacheAnswerPic(c context.Context, args *model.ArgGetQuestion) (oi *model.QuestBkPic, err error) {
  443. picKey := d.GetQusKey(_keyAnswerPicID, args)
  444. oi = &model.QuestBkPic{}
  445. err = d.GetObj(c, picKey, oi)
  446. if err != nil {
  447. return
  448. }
  449. return
  450. }
  451. // DelTargetItemBindCache del cache
  452. func (d *Dao) DelTargetItemBindCache(c context.Context, skuID string) (err error) {
  453. key := fmt.Sprintf(_keyBindBank, skuID)
  454. _, err = d.RedisDo(c, "DEL", key)
  455. if err != nil {
  456. if err != redis.ErrNil {
  457. log.Error("d.DelTargetItemBind(%v, %d) error(%v)", skuID, err)
  458. return
  459. }
  460. err = nil
  461. }
  462. return
  463. }
  464. // DelQusCache del cache
  465. func (d *Dao) DelQusCache(c context.Context, id int64) (err error) {
  466. key := fmt.Sprintf(_keyQusInfo, id)
  467. _, err = d.RedisDo(c, "DEL", key)
  468. if err != nil {
  469. if err != redis.ErrNil {
  470. log.Error("d.DelQusCache(%v, %d) error(%v)", id, err)
  471. return
  472. }
  473. err = nil
  474. }
  475. return
  476. }
  477. // GetAnswersByCache cache
  478. func (d *Dao) GetAnswersByCache(c context.Context, id int64) (oi []*model.Answer, err error) {
  479. key := fmt.Sprintf(_keyAnswer, id)
  480. err = d.GetObj(c, key, &oi)
  481. if err == redis.ErrNil {
  482. err = nil
  483. info, err1 := d.GetAnswerList(c, id)
  484. if err1 != nil {
  485. err = err1
  486. log.Error("d.GetPic error(%v)", err1)
  487. return
  488. }
  489. err1 = d.SetObj(c, key, info, time.Hour)
  490. oi = info
  491. err = err1
  492. return
  493. }
  494. return
  495. }
  496. // DelAnswerCache del cache
  497. func (d *Dao) DelAnswerCache(c context.Context, id int64) (err error) {
  498. key := fmt.Sprintf(_keyAnswer, id)
  499. _, err = d.RedisDo(c, "DEL", key)
  500. if err != nil {
  501. if err != redis.ErrNil {
  502. log.Error("d.DelAnswerCache(%v, %d) error(%v)", id, err)
  503. return
  504. }
  505. err = nil
  506. }
  507. return
  508. }
  509. // DelQusBankCache del cache
  510. func (d *Dao) DelQusBankCache(c context.Context, id int64) (err error) {
  511. key := fmt.Sprintf(_keyBankID, id)
  512. _, err = d.RedisDo(c, "DEL", key)
  513. if err != nil {
  514. if err != redis.ErrNil {
  515. log.Error("d.DelQusBankCache(%v, %d) error(%v)", id, err)
  516. return
  517. }
  518. err = nil
  519. }
  520. return
  521. }