redis.go 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647
  1. package dao
  2. import (
  3. "context"
  4. "fmt"
  5. "strconv"
  6. artmdl "go-common/app/interface/openplatform/article/model"
  7. "go-common/app/service/main/archive/api"
  8. "go-common/app/service/main/archive/model/archive"
  9. "go-common/app/service/main/feed/model"
  10. feedmdl "go-common/app/service/main/feed/model"
  11. "go-common/library/cache/redis"
  12. "go-common/library/log"
  13. "go-common/library/time"
  14. "go-common/library/xstr"
  15. )
  16. const (
  17. _prefixUpper = "ua_" // upper's archive list
  18. _prefixAppFeed = "af_" // user's app feed list
  19. _prefixWebFeed = "wf_" // user's web feed list
  20. _prefixAppLast = "al_" // user's last access
  21. _prefixWebLast = "wl_" // user's last access
  22. _prefixArtLast = "tl_" // user's last access of article
  23. _prefixBangumiFeed = "banf_" // user's bangumi feed list
  24. _prefixArchiveFeed = "arcf_" // user's archive feed list
  25. _prefixArticleFeed = "artf_" // user's article feed list
  26. _prefixAppUnreadCount = "ac_" // user's app unread count
  27. _prefixWebUnreadCount = "wc_" // user's web unread count
  28. _prefixArtUnreadCount = "tc_" // user's article unread count
  29. )
  30. func upperKey(mid int64) string {
  31. return _prefixUpper + strconv.FormatInt(mid, 10)
  32. }
  33. func bangumiFeedKey(mid int64) string {
  34. return _prefixBangumiFeed + strconv.FormatInt(mid, 10)
  35. }
  36. func archiveFeedKey(mid int64) string {
  37. return _prefixArchiveFeed + strconv.FormatInt(mid, 10)
  38. }
  39. func from(i int64) (time.Time, int8) {
  40. return time.Time((i >> 8)), int8(int64(i) & 0xff)
  41. }
  42. func combine(t time.Time, copyright int8) int64 {
  43. return int64(t)<<8 | int64(copyright)
  44. }
  45. func feedKey(ft int, mid int64) string {
  46. midStr := strconv.FormatInt(mid, 10)
  47. if ft == model.TypeApp {
  48. return _prefixAppFeed + midStr
  49. } else if ft == model.TypeWeb {
  50. return _prefixWebFeed + midStr
  51. } else {
  52. return _prefixArticleFeed + midStr
  53. }
  54. }
  55. func unreadCountKey(ft int, mid int64) string {
  56. midStr := strconv.FormatInt(mid%100000, 10)
  57. if ft == model.TypeApp {
  58. return _prefixAppUnreadCount + midStr
  59. } else if ft == model.TypeWeb {
  60. return _prefixWebUnreadCount + midStr
  61. } else {
  62. return _prefixArtUnreadCount + midStr
  63. }
  64. }
  65. func lastKey(ft int, mid int64) string {
  66. midStr := strconv.FormatInt(mid%100000, 10)
  67. if ft == model.TypeApp {
  68. return _prefixAppLast + midStr
  69. } else if ft == model.TypeWeb {
  70. return _prefixWebLast + midStr
  71. } else {
  72. return _prefixArtLast + midStr
  73. }
  74. }
  75. // appFeedValue convert Feed to string, format: "type,id,fold,fold,fold..."
  76. func appFeedValue(f *feedmdl.Feed) string {
  77. ids := []int64{f.Type, f.ID}
  78. for _, arc := range f.Fold {
  79. ids = append(ids, arc.Aid)
  80. }
  81. return xstr.JoinInts(ids)
  82. }
  83. func recoverFeed(idsStr string) (fe *feedmdl.Feed, err error) {
  84. var (
  85. aid int64
  86. ids []int64
  87. )
  88. if ids, err = xstr.SplitInts(idsStr); err != nil {
  89. return
  90. }
  91. if len(ids) < 2 {
  92. err = fmt.Errorf("recoverFeed failed idsStr(%v)", idsStr)
  93. return
  94. }
  95. fe = &feedmdl.Feed{Type: ids[0], ID: ids[1]}
  96. for _, aid = range ids[2:] {
  97. fe.Fold = append(fe.Fold, &api.Arc{Aid: aid})
  98. }
  99. return
  100. }
  101. // pingRedis ping redis.
  102. func (d *Dao) pingRedis(c context.Context) (err error) {
  103. conn := d.redis.Get(c)
  104. if _, err = conn.Do("SET", "PING", "PONG"); err != nil {
  105. PromError("redis: ping remote", "remote redis: conn.Do(SET,PING,PONG) error(%v)", err)
  106. }
  107. conn.Close()
  108. return
  109. }
  110. // LastAccessCache get last access time of user.
  111. func (d *Dao) LastAccessCache(c context.Context, ft int, mid int64) (t int64, err error) {
  112. conn := d.redis.Get(c)
  113. defer conn.Close()
  114. key := lastKey(ft, mid)
  115. if t, err = redis.Int64(conn.Do("HGET", key, mid)); err != nil {
  116. if err == redis.ErrNil {
  117. err = nil
  118. } else {
  119. PromError("redis:获取上次访问时间", "conn.Do(HGET, %s, %s) error(%v)", key, mid, err)
  120. }
  121. }
  122. return
  123. }
  124. // AddLastAccessCache add user's last access time.
  125. func (d *Dao) AddLastAccessCache(c context.Context, ft int, mid int64, t int64) (err error) {
  126. conn := d.redis.Get(c)
  127. defer conn.Close()
  128. key := lastKey(ft, mid)
  129. if _, err = conn.Do("HSET", key, mid, t); err != nil {
  130. PromError("redis:增加上次访问时间", "conn.DO(HSET, %s, %d, %d) error(%v)", key, mid, t, err)
  131. }
  132. return
  133. }
  134. // ExpireFeedCache expire the user feed key.
  135. func (d *Dao) ExpireFeedCache(c context.Context, ft int, mid int64) (ok bool, err error) {
  136. conn := d.redis.Get(c)
  137. defer conn.Close()
  138. key := feedKey(ft, mid)
  139. if ok, err = redis.Bool(conn.Do("EXPIRE", key, d.redisExpireFeed)); err != nil {
  140. PromError("redis:Feed缓存设定过期", "conn.Do(EXPIRE, %s, %d) error(%v)", key, d.redisExpireFeed, err)
  141. }
  142. return
  143. }
  144. // PurgeFeedCache purge the user feed key.
  145. func (d *Dao) PurgeFeedCache(c context.Context, ft int, mid int64) (err error) {
  146. conn := d.redis.Get(c)
  147. defer conn.Close()
  148. key := feedKey(ft, mid)
  149. if _, err = redis.Bool(conn.Do("DEL", key)); err != nil {
  150. PromError("redis:删除feed", "conn.Do(DEL, %s, %d) error(%v)", key, err)
  151. }
  152. return
  153. }
  154. // FeedCache get upper feed by cache.
  155. func (d *Dao) FeedCache(c context.Context, ft int, mid int64, start, end int) (as []*feedmdl.Feed, bids []int64, err error) {
  156. conn := d.redis.Get(c)
  157. defer conn.Close()
  158. key := feedKey(ft, mid)
  159. vs, err := redis.Values(conn.Do("ZREVRANGE", key, start, end, "WITHSCORES"))
  160. if err != nil {
  161. PromError("redis:获取feed", "conn.Do(ZREVRANGE,%s,%d,%d) error(%v)", key, start, end, err)
  162. return
  163. }
  164. as = make([]*feedmdl.Feed, 0, len(vs))
  165. for len(vs) > 0 {
  166. var (
  167. ts int64
  168. idsStr string
  169. fe *feedmdl.Feed
  170. )
  171. if vs, err = redis.Scan(vs, &idsStr, &ts); err != nil {
  172. PromError("redis:获取feed", "redis.Scan(%v) error(%v)", vs, err)
  173. return
  174. }
  175. if idsStr != "" {
  176. fe, err = recoverFeed(idsStr)
  177. fe.PubDate = time.Time(ts)
  178. if err != nil {
  179. PromError("恢复feed", "redis.recoverFeed(%v) error(%v)", idsStr, err)
  180. err = nil
  181. continue
  182. }
  183. as = append(as, fe)
  184. switch fe.Type {
  185. case feedmdl.BangumiType:
  186. bids = append(bids, fe.ID)
  187. }
  188. }
  189. }
  190. return
  191. }
  192. // AddFeedCache add upper feed cache.
  193. func (d *Dao) AddFeedCache(c context.Context, ft int, mid int64, as []*feedmdl.Feed) (err error) {
  194. conn := d.redis.Get(c)
  195. defer conn.Close()
  196. key := feedKey(ft, mid)
  197. if err = conn.Send("DEL", key); err != nil {
  198. PromError("redis:删除feed缓存", "conn.Send(DEL, %s) error(%v)", key, err)
  199. return
  200. }
  201. commondLen := 1
  202. if len(as) > 0 {
  203. var feedLen int
  204. if ft == model.TypeApp {
  205. feedLen = d.appFeedLength
  206. } else {
  207. feedLen = d.webFeedLength
  208. }
  209. if len(as) > feedLen {
  210. as = as[:feedLen]
  211. }
  212. commonds := []interface{}{key}
  213. for _, appFeed := range as {
  214. ts := appFeed.PubDate.Time().Unix()
  215. feedValue := appFeedValue(appFeed)
  216. commonds = append(commonds, ts, feedValue)
  217. }
  218. if err = conn.Send("ZADD", commonds...); err != nil {
  219. PromError("redis:增加feed缓存", "conn.Send(ZADD, %v, %v) error(%v)", key, commonds, err)
  220. return
  221. }
  222. commondLen++
  223. if err = conn.Send("EXPIRE", key, d.redisExpireFeed); err != nil {
  224. PromError("redis:expire-feed缓存", "conn.Send(expire, %s, %v) error(%v)", key, d.redisExpireFeed, err)
  225. return
  226. }
  227. commondLen++
  228. }
  229. if err = conn.Flush(); err != nil {
  230. PromError("redis:feed缓存flush", "conn.Flush error(%v)", err)
  231. return
  232. }
  233. for i := 0; i < commondLen; i++ {
  234. if _, err = conn.Receive(); err != nil {
  235. PromError("redis:feed缓存receive", "conn.Receive error(%v)", err)
  236. return
  237. }
  238. }
  239. return
  240. }
  241. // ExpireUppersCache expire the upper key.
  242. func (d *Dao) ExpireUppersCache(c context.Context, mids []int64) (res map[int64]bool, err error) {
  243. conn := d.redis.Get(c)
  244. defer conn.Close()
  245. res = make(map[int64]bool, len(mids))
  246. for _, mid := range mids {
  247. if err = conn.Send("TTL", upperKey(mid)); err != nil {
  248. PromError("redis:up主ttl", "conn.Send(TTL, %s) error(%v)", upperKey(mid), err)
  249. return
  250. }
  251. }
  252. if err = conn.Flush(); err != nil {
  253. PromError("redis:up主flush", "conn.Flush error(%v)", err)
  254. return
  255. }
  256. var state int64
  257. for _, mid := range mids {
  258. if state, err = redis.Int64(conn.Receive()); err != nil {
  259. PromError("redis:up主receive", "conn.Receive() error(%v)", err)
  260. return
  261. }
  262. if int32(state) > (d.redisTTLUpper - d.redisExpireUpper) {
  263. res[mid] = true
  264. } else {
  265. res[mid] = false
  266. }
  267. }
  268. return
  269. }
  270. // UppersCaches batch get new archives of uppers by cache.
  271. func (d *Dao) UppersCaches(c context.Context, mids []int64, start, end int) (res map[int64][]*archive.AidPubTime, err error) {
  272. conn := d.redis.Get(c)
  273. defer conn.Close()
  274. res = make(map[int64][]*archive.AidPubTime, len(mids))
  275. for _, mid := range mids {
  276. if err = conn.Send("ZREVRANGE", upperKey(mid), start, end, "withscores"); err != nil {
  277. PromError("redis:获取up主", "conn.Send(%s) error(%v)", upperKey(mid), err)
  278. return
  279. }
  280. }
  281. if err = conn.Flush(); err != nil {
  282. PromError("redis:获取up主flush", "conn.Flush error(%v)", err)
  283. return
  284. }
  285. for _, mid := range mids {
  286. values, err := redis.Values(conn.Receive())
  287. if err != nil {
  288. PromError("redis:获取up主receive", "conn.Send(ZREVRANGE, %d) error(%v)", mid, err)
  289. err = nil
  290. continue
  291. }
  292. for len(values) > 0 {
  293. arc := archive.AidPubTime{}
  294. var score int64
  295. if values, err = redis.Scan(values, &arc.Aid, &score); err != nil {
  296. PromError("redis:scan UP主", "redis.Scan() error(%v)", err)
  297. err = nil
  298. continue
  299. }
  300. arc.PubDate, arc.Copyright = from(score)
  301. res[mid] = append(res[mid], &arc)
  302. }
  303. }
  304. CachedCount.Add("up", int64(len(res)))
  305. return
  306. }
  307. // AddUpperCaches batch add passed archive of upper.
  308. // set max num of upper's passed list.
  309. func (d *Dao) AddUpperCaches(c context.Context, mArcs map[int64][]*archive.AidPubTime) (err error) {
  310. var (
  311. mid int64
  312. arcs []*archive.AidPubTime
  313. conn = d.redis.Get(c)
  314. count int
  315. )
  316. defer conn.Close()
  317. if len(mArcs) == 0 {
  318. return
  319. }
  320. for mid, arcs = range mArcs {
  321. if len(arcs) == 0 {
  322. continue
  323. }
  324. key := upperKey(mid)
  325. if err = conn.Send("DEL", key); err != nil {
  326. PromError("redis:删除up主缓存", "conn.Send(DEL, %s) error(%v)", key, err)
  327. return
  328. }
  329. count++
  330. for _, arc := range arcs {
  331. score := combine(arc.PubDate, arc.Copyright)
  332. if err = conn.Send("ZADD", key, "CH", score, arc.Aid); err != nil {
  333. PromError("redis:增加up主缓存", "conn.Send(ZADD, %s, %d, %d) error(%v)", key, arc.Aid, err)
  334. return
  335. }
  336. count++
  337. }
  338. if err = conn.Send("ZREMRANGEBYRANK", key, 0, -(d.c.MultiRedis.MaxArcsNum + 1)); err != nil {
  339. PromError("redis:清理up主缓存", "conn.Send(ZREMRANGEBYRANK, %s) error(%v)", key, err)
  340. return
  341. }
  342. count++
  343. if err = conn.Send("EXPIRE", key, d.redisTTLUpper); err != nil {
  344. PromError("redis:expireup主缓存", "conn.Send(EXPIRE, %s, %v) error(%v)", key, d.redisTTLUpper, err)
  345. return
  346. }
  347. count++
  348. }
  349. if err = conn.Flush(); err != nil {
  350. PromError("redis:增加up主flush", "conn.Flush error(%v)", err)
  351. return
  352. }
  353. for i := 0; i < count; i++ {
  354. if _, err = conn.Receive(); err != nil {
  355. PromError("redis:增加up主receive", "conn.Receive error(%v)", err)
  356. return
  357. }
  358. }
  359. return
  360. }
  361. // AddUpperCache .
  362. func (d *Dao) AddUpperCache(c context.Context, mid int64, arc *archive.AidPubTime) (err error) {
  363. var conn = d.redis.Get(c)
  364. defer conn.Close()
  365. key := upperKey(mid)
  366. score := combine(arc.PubDate, arc.Copyright)
  367. if err = conn.Send("ZADD", key, "CH", score, arc.Aid); err != nil {
  368. PromError("redis:增加up主缓存", "conn.Send(ZADD, %s, %d, %d) error(%v)", key, arc.Aid, err)
  369. return
  370. }
  371. if err = conn.Send("ZREMRANGEBYRANK", key, 0, -(d.c.MultiRedis.MaxArcsNum + 1)); err != nil {
  372. PromError("redis:清理up主缓存", "conn.Send(ZREMRANGEBYRANK, %s) error(%v)", key, err)
  373. return
  374. }
  375. if err = conn.Flush(); err != nil {
  376. PromError("redis:增加up主flush", "conn.Flush error(%v)", err)
  377. return
  378. }
  379. for i := 0; i < 2; i++ {
  380. if _, err = conn.Receive(); err != nil {
  381. PromError("redis:增加up主receive", "conn.Receive error(%v)", err)
  382. return
  383. }
  384. }
  385. return
  386. }
  387. // DelUpperCache delete archive of upper cache.
  388. func (d *Dao) DelUpperCache(c context.Context, mid int64, aid int64) (err error) {
  389. conn := d.redis.Get(c)
  390. defer conn.Close()
  391. if _, err = conn.Do("ZREM", upperKey(mid), aid); err != nil {
  392. PromError("redis:删除up主", "conn.Do(ZERM, %s, %d) error(%v)", upperKey(mid), aid, err)
  393. }
  394. return
  395. }
  396. // AddArchiveFeedCache add archive feed cache.
  397. func (d *Dao) AddArchiveFeedCache(c context.Context, mid int64, as []*feedmdl.Feed) (err error) {
  398. conn := d.redis.Get(c)
  399. defer conn.Close()
  400. if len(as) == 0 {
  401. return
  402. }
  403. if len(as) > d.c.Feed.ArchiveFeedLength {
  404. as = as[:d.c.Feed.ArchiveFeedLength]
  405. }
  406. key := archiveFeedKey(mid)
  407. commonds := []interface{}{key}
  408. for _, f := range as {
  409. ts := f.PubDate.Time().Unix()
  410. value := appFeedValue(f)
  411. commonds = append(commonds, ts, value)
  412. }
  413. if err = conn.Send("ZADD", commonds...); err != nil {
  414. PromError("redis:增加archive-feed缓存", "conn.Send(ZADD, %v, %v) error(%v)", key, commonds, err)
  415. return
  416. }
  417. if err = conn.Send("EXPIRE", key, d.redisExpireArchiveFeed); err != nil {
  418. PromError("redis:expire-archive-feed缓存", "conn.Send(expire, %s, %v) error(%v)", key, d.redisExpireArchiveFeed, err)
  419. return
  420. }
  421. if err = conn.Flush(); err != nil {
  422. PromError("redis:archive-feed-flush", "conn.Flush error(%v)", err)
  423. return
  424. }
  425. for i := 0; i < 2; i++ {
  426. if _, err = conn.Receive(); err != nil {
  427. PromError("redis:archive-feed-receive", "conn.Receive error(%v)", err)
  428. return
  429. }
  430. }
  431. return
  432. }
  433. // AddBangumiFeedCache add bangumi feed cache.
  434. func (d *Dao) AddBangumiFeedCache(c context.Context, mid int64, as []*feedmdl.Feed) (err error) {
  435. if len(as) == 0 {
  436. return
  437. }
  438. conn := d.redis.Get(c)
  439. defer conn.Close()
  440. key := bangumiFeedKey(mid)
  441. commonds := []interface{}{key}
  442. for _, f := range as {
  443. ts := f.PubDate.Time().Unix()
  444. value := appFeedValue(f)
  445. commonds = append(commonds, ts, value)
  446. }
  447. if err = conn.Send("ZADD", commonds...); err != nil {
  448. PromError("redis:增加bangumi-feed缓存", "conn.Send(ZADD, %v, %v) error(%v)", key, commonds, err)
  449. return
  450. }
  451. if err = conn.Send("EXPIRE", key, d.redisExpireBangumiFeed); err != nil {
  452. PromError("redis:expire-bangumi-feed", "conn.Send(expire, %s, %v) error(%v)", key, d.redisExpireBangumiFeed, err)
  453. return
  454. }
  455. if err = conn.Flush(); err != nil {
  456. PromError("redis:bangumi-feed-flush", "conn.Flush error(%v)", err)
  457. return
  458. }
  459. for i := 0; i < 2; i++ {
  460. if _, err = conn.Receive(); err != nil {
  461. PromError("redis:bangumi-feed-receive", "conn.Receive error(%v)", err)
  462. return
  463. }
  464. }
  465. return
  466. }
  467. // ArchiveFeedCache get archive feed by cache.
  468. func (d *Dao) ArchiveFeedCache(c context.Context, mid int64, start, end int) (as []*feedmdl.Feed, err error) {
  469. conn := d.redis.Get(c)
  470. defer conn.Close()
  471. key := archiveFeedKey(mid)
  472. vs, err := redis.Values(conn.Do("ZREVRANGE", key, start, end, "WITHSCORES"))
  473. if err != nil {
  474. PromError("redis:获取archive-feed", "conn.Do(ZREVRANGE,%s,%d,%d) error(%v)", key, start, end, err)
  475. return
  476. }
  477. for len(vs) > 0 {
  478. var (
  479. ts int64
  480. idsStr string
  481. fe *feedmdl.Feed
  482. )
  483. if vs, err = redis.Scan(vs, &idsStr, &ts); err != nil {
  484. PromError("redis:获取archive-feed", "redis.Scan(%v) error(%v)", vs, err)
  485. return
  486. }
  487. if idsStr != "" {
  488. fe, err = recoverFeed(idsStr)
  489. fe.PubDate = time.Time(ts)
  490. if err != nil {
  491. PromError("恢复archive-feed", "redis.recoverFeed(%v) error(%v)", idsStr, err)
  492. err = nil
  493. continue
  494. }
  495. as = append(as, fe)
  496. }
  497. }
  498. return
  499. }
  500. // BangumiFeedCache get bangumi feed by cache.
  501. func (d *Dao) BangumiFeedCache(c context.Context, mid int64, start, end int) (bids []int64, err error) {
  502. conn := d.redis.Get(c)
  503. defer conn.Close()
  504. key := bangumiFeedKey(mid)
  505. vs, err := redis.Values(conn.Do("ZREVRANGE", key, start, end, "WITHSCORES"))
  506. if err != nil {
  507. PromError("redis:获取feed", "conn.Do(ZREVRANGE,%s,%d,%d) error(%v)", key, start, end, err)
  508. return
  509. }
  510. bids = make([]int64, 0, len(vs))
  511. for len(vs) > 0 {
  512. var (
  513. ts int64
  514. idsStr string
  515. fe *feedmdl.Feed
  516. )
  517. if vs, err = redis.Scan(vs, &idsStr, &ts); err != nil {
  518. PromError("redis:获取bangumi-feed", "redis.Scan(%v) error(%v)", vs, err)
  519. return
  520. }
  521. if idsStr != "" {
  522. fe, err = recoverFeed(idsStr)
  523. if err != nil {
  524. PromError("恢复bangumi-feed", "redis.recoverFeed(%v) error(%v)", idsStr, err)
  525. err = nil
  526. continue
  527. }
  528. fe.PubDate = time.Time(ts)
  529. bids = append(bids, fe.ID)
  530. }
  531. }
  532. return
  533. }
  534. // ArticleFeedCache get article feed by cache.
  535. func (d *Dao) ArticleFeedCache(c context.Context, mid int64, start, end int) (aids []int64, err error) {
  536. conn := d.redis.Get(c)
  537. defer conn.Close()
  538. key := feedKey(model.TypeArt, mid)
  539. vs, err := redis.Values(conn.Do("ZREVRANGE", key, start, end))
  540. if err != nil {
  541. log.Error("ArticleFeedCache conn.Do(ZREVRANGE,%s,%d,%d) error(%v)", key, start, end, err)
  542. return
  543. }
  544. for len(vs) > 0 {
  545. var aid int64
  546. if vs, err = redis.Scan(vs, &aid); err != nil {
  547. log.Error("ArticleFeedCache redis.Scan(%v) error(%v)", vs, err)
  548. return
  549. }
  550. aids = append(aids, aid)
  551. }
  552. return
  553. }
  554. // AddArticleFeedCache add article feed cache.
  555. func (d *Dao) AddArticleFeedCache(c context.Context, mid int64, as []*artmdl.Meta) (err error) {
  556. conn := d.redis.Get(c)
  557. defer conn.Close()
  558. if len(as) == 0 {
  559. return
  560. }
  561. if len(as) > d.c.Feed.ArticleFeedLength {
  562. as = as[:d.c.Feed.ArticleFeedLength]
  563. }
  564. key := feedKey(model.TypeArt, mid)
  565. commonds := []interface{}{key}
  566. for _, a := range as {
  567. ts := a.PublishTime.Time().Unix()
  568. commonds = append(commonds, ts, a.ID)
  569. }
  570. if err = conn.Send("ZADD", commonds...); err != nil {
  571. log.Error("AddArticleFeedCache conn.Send(ZADD, %v, %v) error(%v)", key, commonds, err)
  572. return
  573. }
  574. if err = conn.Send("EXPIRE", key, d.redisExpireArchiveFeed); err != nil {
  575. log.Error("AddArticleFeedCache conn.Send(expire, %s, %v) error(%v)", key, d.redisExpireArchiveFeed, err)
  576. return
  577. }
  578. if err = conn.Flush(); err != nil {
  579. log.Error("AddArticleFeedCache conn.Flush error(%v)", err)
  580. return
  581. }
  582. for i := 0; i < 2; i++ {
  583. if _, err = conn.Receive(); err != nil {
  584. log.Error("AddArticleFeedCache conn.Receive error(%v)", err)
  585. return
  586. }
  587. }
  588. return
  589. }
  590. // UnreadCountCache get unread count cache of user.
  591. func (d *Dao) UnreadCountCache(c context.Context, ft int, mid int64) (count int, err error) {
  592. conn := d.redis.Get(c)
  593. defer conn.Close()
  594. key := unreadCountKey(ft, mid)
  595. if count, err = redis.Int(conn.Do("HGET", key, mid)); err != nil {
  596. if err == redis.ErrNil {
  597. err = nil
  598. } else {
  599. PromError("redis:获取未读数", "conn.Do(HGET, %s, %v) error(%v)", key, mid, err)
  600. }
  601. }
  602. return
  603. }
  604. // AddUnreadCountCache add user's unread count cache.
  605. func (d *Dao) AddUnreadCountCache(c context.Context, ft int, mid int64, count int) (err error) {
  606. conn := d.redis.Get(c)
  607. defer conn.Close()
  608. key := unreadCountKey(ft, mid)
  609. if _, err = conn.Do("HSET", key, mid, count); err != nil {
  610. PromError("redis:增加未读数", "conn.DO(HSET, %s, %d, %d) error(%v)", key, mid, count, err)
  611. }
  612. return
  613. }