453 lines
11 KiB
Go
453 lines
11 KiB
Go
package dao
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"strconv"
|
|
|
|
"go-common/app/admin/main/reply/model"
|
|
"go-common/library/cache/redis"
|
|
"go-common/library/log"
|
|
)
|
|
|
|
const (
|
|
_prefixIdx = "i_"
|
|
_prefixNewRootIdx = "ri_"
|
|
_prefixAuditIdx = "ai_%d_%d"
|
|
// 针对大忽悠时间被删除评论的人的MID
|
|
_prefixAdminDelMid = "mid_%d"
|
|
|
|
// f_{折叠类型,根评论还是评论区}_{评论区ID或者根评论ID}
|
|
_foldedReplyFmt = "f_%s_%d"
|
|
|
|
// dialog
|
|
_prefixDialogIdx = "d_%d"
|
|
|
|
_maxCount = 20000
|
|
)
|
|
|
|
func keyFolderIdx(kind string, ID int64) string {
|
|
return fmt.Sprintf(_foldedReplyFmt, kind, ID)
|
|
}
|
|
|
|
func keyDelMid(mid int64) string {
|
|
return fmt.Sprintf(_prefixAdminDelMid, mid)
|
|
}
|
|
|
|
// KeyMainIdx ...
|
|
func keyMainIdx(oid int64, tp, sort int32) string {
|
|
if oid > _oidOverflow {
|
|
return fmt.Sprintf("%s_%d_%d_%d", _prefixIdx, oid, tp, sort)
|
|
}
|
|
return _prefixIdx + strconv.FormatInt((oid<<16)|(int64(tp)<<8)|int64(sort), 10)
|
|
}
|
|
|
|
// keyDialogIdx ...
|
|
func keyDialogIdx(dialog int64) string {
|
|
return fmt.Sprintf(_prefixDialogIdx, dialog)
|
|
}
|
|
|
|
// KeyRootIdx ...
|
|
func keyRootIdx(root int64) string {
|
|
return _prefixNewRootIdx + strconv.FormatInt(root, 10)
|
|
}
|
|
|
|
func keyIdx(oid int64, tp, sort int32) string {
|
|
if oid > _oidOverflow {
|
|
return fmt.Sprintf("%s_%d_%d_%d", _prefixIdx, oid, tp, sort)
|
|
}
|
|
return _prefixIdx + strconv.FormatInt((oid<<16)|(int64(tp)<<8)|int64(sort), 10)
|
|
}
|
|
|
|
func keyNewRootIdx(rpID int64) string {
|
|
return _prefixNewRootIdx + strconv.FormatInt(rpID, 10)
|
|
}
|
|
|
|
func keyAuditIdx(oid int64, tp int32) string {
|
|
return fmt.Sprintf(_prefixAuditIdx, oid, tp)
|
|
}
|
|
|
|
func (d *Dao) ExsistsDelMid(c context.Context, mid int64) (ok bool, err error) {
|
|
conn := d.redis.Get(c)
|
|
defer conn.Close()
|
|
key := keyDelMid(mid)
|
|
if ok, err = redis.Bool(conn.Do("EXISTS", key)); err != nil {
|
|
log.Error("conn.Do(EXISTS, %s) error(%v)", key, err)
|
|
}
|
|
return
|
|
}
|
|
|
|
func (d *Dao) SetDelMid(c context.Context, mid int64) (err error) {
|
|
conn := d.redis.Get(c)
|
|
defer conn.Close()
|
|
// 15天内
|
|
if err = conn.Send("SETEX", keyDelMid(mid), 86400*15, 1); err != nil {
|
|
log.Error("redis SETEX(%s) error(%v)", keyDelMid(mid), err)
|
|
}
|
|
return
|
|
}
|
|
|
|
// ExpireIndex set expire time for index.
|
|
func (d *Dao) ExpireIndex(c context.Context, oid int64, typ, sort int32) (ok bool, err error) {
|
|
conn := d.redis.Get(c)
|
|
defer conn.Close()
|
|
if ok, err = redis.Bool(conn.Do("EXPIRE", keyIdx(oid, typ, sort), d.redisExpire)); err != nil {
|
|
log.Error("conn.Do(EXPIRE) error(%v)", err)
|
|
}
|
|
return
|
|
}
|
|
|
|
// ExpireNewChildIndex set expire time for root's index.
|
|
func (d *Dao) ExpireNewChildIndex(c context.Context, root int64) (ok bool, err error) {
|
|
conn := d.redis.Get(c)
|
|
defer conn.Close()
|
|
if ok, err = redis.Bool(conn.Do("EXPIRE", keyNewRootIdx(root), d.redisExpire)); err != nil {
|
|
log.Error("conn.Do(EXPIRE) error(%v)", err)
|
|
}
|
|
return
|
|
}
|
|
|
|
// TopChildReply ...
|
|
func (d *Dao) TopChildReply(c context.Context, root, child int64) (err error) {
|
|
conn := d.redis.Get(c)
|
|
defer conn.Close()
|
|
if _, err = conn.Do("ZADD", keyNewRootIdx(root), 0, child); err != nil {
|
|
return
|
|
}
|
|
return
|
|
}
|
|
|
|
// CountReplies get count of reply.
|
|
func (d *Dao) CountReplies(c context.Context, oid int64, tp, sort int32) (count int, err error) {
|
|
key := keyIdx(oid, tp, sort)
|
|
conn := d.redis.Get(c)
|
|
defer conn.Close()
|
|
if count, err = redis.Int(conn.Do("ZCARD", key)); err != nil {
|
|
log.Error("CountReplies error(%v)", err)
|
|
}
|
|
return
|
|
}
|
|
|
|
// MinScore get the lowest score from sorted set
|
|
func (d *Dao) MinScore(c context.Context, oid int64, tp int32, sort int32) (score int32, err error) {
|
|
key := keyIdx(oid, tp, sort)
|
|
conn := d.redis.Get(c)
|
|
defer conn.Close()
|
|
values, err := redis.Values(conn.Do("ZRANGE", key, 0, 0, "WITHSCORES"))
|
|
if err != nil {
|
|
log.Error("conn.Do(ZREVRANGE, %s) error(%v)", key, err)
|
|
return
|
|
}
|
|
if len(values) != 2 {
|
|
err = fmt.Errorf("redis zrange items(%v) length not 2", values)
|
|
return
|
|
}
|
|
var id int64
|
|
redis.Scan(values, &id, &score)
|
|
return
|
|
}
|
|
|
|
// AddFloorIndex add index by floor.
|
|
func (d *Dao) AddFloorIndex(c context.Context, rp *model.Reply) (err error) {
|
|
min, err := d.MinScore(c, rp.Oid, rp.Type, model.SortByFloor)
|
|
if err != nil {
|
|
log.Error("s.dao.Redis.AddFloorIndex failed , oid(%d) type(%d) err(%v)", rp.Oid, rp.Type, err)
|
|
} else if rp.Floor <= min {
|
|
return
|
|
}
|
|
|
|
key := keyIdx(rp.Oid, rp.Type, model.SortByFloor)
|
|
conn := d.redis.Get(c)
|
|
defer conn.Close()
|
|
if err = conn.Send("ZADD", key, rp.Floor, rp.ID); err != nil {
|
|
log.Error("conn.Send error(%v)", err)
|
|
return
|
|
}
|
|
if err = conn.Send("EXPIRE", key, d.redisExpire); err != nil {
|
|
log.Error("conn.Send error(%v)", err)
|
|
return
|
|
}
|
|
if err = conn.Flush(); err != nil {
|
|
log.Error("conn.Flush error(%v)", err)
|
|
return
|
|
}
|
|
if _, err = conn.Receive(); err != nil {
|
|
log.Error("conn.Receive error(%v)", err)
|
|
return
|
|
}
|
|
return
|
|
}
|
|
|
|
// AddCountIndex add index by count.
|
|
func (d *Dao) AddCountIndex(c context.Context, rp *model.Reply) (err error) {
|
|
if rp.IsTop() {
|
|
return
|
|
}
|
|
var count int
|
|
if count, err = d.CountReplies(c, rp.Oid, rp.Type, model.SortByCount); err != nil {
|
|
return
|
|
} else if count >= _maxCount {
|
|
var min int32
|
|
if min, err = d.MinScore(c, rp.Oid, rp.Type, model.SortByCount); err != nil {
|
|
return
|
|
}
|
|
if rp.RCount <= min {
|
|
return
|
|
}
|
|
}
|
|
|
|
key := keyIdx(rp.Oid, rp.Type, model.SortByCount)
|
|
conn := d.redis.Get(c)
|
|
defer conn.Close()
|
|
if err = conn.Send("ZADD", key, int64(rp.RCount)<<32|(int64(rp.Floor)&0xFFFFFFFF), rp.ID); err != nil {
|
|
log.Error("conn.Send error(%v)", err)
|
|
return
|
|
}
|
|
if err = conn.Send("EXPIRE", key, d.redisExpire); err != nil {
|
|
log.Error("conn.Send error(%v)", err)
|
|
return
|
|
}
|
|
if err = conn.Flush(); err != nil {
|
|
log.Error("conn.Flush error(%v)", err)
|
|
return
|
|
}
|
|
if _, err = conn.Receive(); err != nil {
|
|
log.Error("conn.Receive error(%v)", err)
|
|
return
|
|
}
|
|
return
|
|
}
|
|
|
|
// AddLikeIndex add index by like.
|
|
func (d *Dao) AddLikeIndex(c context.Context, rp *model.Reply, rpt *model.Report) (err error) {
|
|
if rp.IsTop() {
|
|
return
|
|
}
|
|
var rptCn int32
|
|
if rpt != nil {
|
|
rptCn = rpt.Count
|
|
}
|
|
score := int64((float32(rp.Like+model.WeightLike) / float32(rp.Hate+model.WeightHate+rptCn)) * 100)
|
|
score = score<<32 | (int64(rp.RCount) & 0xFFFFFFFF)
|
|
var count int
|
|
if count, err = d.CountReplies(c, rp.Oid, rp.Type, model.SortByLike); err != nil {
|
|
return
|
|
} else if count >= _maxCount {
|
|
var min int32
|
|
if min, err = d.MinScore(c, rp.Oid, rp.Type, model.SortByLike); err != nil {
|
|
return
|
|
}
|
|
if score <= int64(min) {
|
|
return
|
|
}
|
|
}
|
|
|
|
key := keyIdx(rp.Oid, rp.Type, model.SortByLike)
|
|
conn := d.redis.Get(c)
|
|
defer conn.Close()
|
|
if err = conn.Send("ZADD", key, score, rp.ID); err != nil {
|
|
log.Error("conn.Send error(%v)", err)
|
|
return
|
|
}
|
|
if err = conn.Send("EXPIRE", key, d.redisExpire); err != nil {
|
|
log.Error("conn.Send error(%v)", err)
|
|
return
|
|
}
|
|
if err = conn.Flush(); err != nil {
|
|
log.Error("conn.Flush error(%v)", err)
|
|
return
|
|
}
|
|
if _, err = conn.Receive(); err != nil {
|
|
log.Error("conn.Receive() error(%v)", err)
|
|
return
|
|
}
|
|
return
|
|
}
|
|
|
|
// DelIndexBySort delete index by sort.
|
|
func (d *Dao) DelIndexBySort(c context.Context, rp *model.Reply, sort int32) (err error) {
|
|
key := keyIdx(rp.Oid, rp.Type, sort)
|
|
conn := d.redis.Get(c)
|
|
defer conn.Close()
|
|
if _, err = conn.Do("ZREM", key, rp.ID); err != nil {
|
|
log.Error("conn.Do(ZREM) error(%v)", err)
|
|
}
|
|
return
|
|
}
|
|
|
|
// DelReplyIndex delete reply index.
|
|
func (d *Dao) DelReplyIndex(c context.Context, rp *model.Reply) (err error) {
|
|
var (
|
|
key string
|
|
n int
|
|
)
|
|
conn := d.redis.Get(c)
|
|
defer conn.Close()
|
|
if rp.Root == 0 {
|
|
key = keyIdx(rp.Oid, rp.Type, model.SortByFloor)
|
|
err = conn.Send("ZREM", key, rp.ID)
|
|
key = keyIdx(rp.Oid, rp.Type, model.SortByCount)
|
|
err = conn.Send("ZREM", key, rp.ID)
|
|
key = keyIdx(rp.Oid, rp.Type, model.SortByLike)
|
|
err = conn.Send("ZREM", key, rp.ID)
|
|
n += 3
|
|
} else {
|
|
if rp.Dialog != 0 {
|
|
key = keyDialogIdx(rp.Dialog)
|
|
err = conn.Send("ZREM", key, rp.ID)
|
|
n++
|
|
}
|
|
key = keyNewRootIdx(rp.Root)
|
|
err = conn.Send("ZREM", key, rp.ID)
|
|
n++
|
|
}
|
|
if err != nil {
|
|
log.Error("conn.Send error(%v)", err)
|
|
return
|
|
}
|
|
if err = conn.Flush(); err != nil {
|
|
log.Error("conn.Flush error(%v)", err)
|
|
return
|
|
}
|
|
for i := 0; i < n; i++ {
|
|
if _, err = conn.Receive(); err != nil {
|
|
log.Error("conn.Receive error(%v)", err)
|
|
return
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
// AddNewChildIndex add root reply index by floor.
|
|
func (d *Dao) AddNewChildIndex(c context.Context, rp *model.Reply) (err error) {
|
|
key := keyNewRootIdx(rp.Root)
|
|
conn := d.redis.Get(c)
|
|
defer conn.Close()
|
|
if err = conn.Send("ZADD", key, rp.Floor, rp.ID); err != nil {
|
|
log.Error("conn.Send error(%v)", err)
|
|
return
|
|
}
|
|
if err = conn.Send("EXPIRE", key, d.redisExpire); err != nil {
|
|
log.Error("conn.Send error(%v)", err)
|
|
return
|
|
}
|
|
if err = conn.Flush(); err != nil {
|
|
log.Error("conn.Flush error(%v)", err)
|
|
return
|
|
}
|
|
if _, err = conn.Receive(); err != nil {
|
|
log.Error("conn.Receive() error(%v)", err)
|
|
return
|
|
}
|
|
return
|
|
}
|
|
|
|
// DelAuditIndex delete audit reply cache.
|
|
func (d *Dao) DelAuditIndex(c context.Context, rp *model.Reply) (err error) {
|
|
key := keyAuditIdx(rp.Oid, rp.Type)
|
|
conn := d.redis.Get(c)
|
|
defer conn.Close()
|
|
if err = conn.Send("ZREM", key, rp.ID); err != nil {
|
|
log.Error("conn.Send(ZREM %s) error(%v)", key, err)
|
|
return
|
|
}
|
|
if err = conn.Flush(); err != nil {
|
|
log.Error("conn.Flush error(%v)", err)
|
|
return
|
|
}
|
|
if _, err = conn.Receive(); err != nil {
|
|
log.Error("conn.Receive() error(%v)", err)
|
|
return
|
|
}
|
|
return
|
|
}
|
|
|
|
// RemReplyFromRedis ...
|
|
func (d *Dao) RemReplyFromRedis(c context.Context, keyMapping map[string][]int64) (err error) {
|
|
conn := d.redis.Get(c)
|
|
defer conn.Close()
|
|
count := 0
|
|
for key, rpIDs := range keyMapping {
|
|
if len(rpIDs) == 0 {
|
|
continue
|
|
}
|
|
args := make([]interface{}, 0, 1+len(rpIDs))
|
|
args = append(args, key)
|
|
for _, rpID := range rpIDs {
|
|
args = append(args, rpID)
|
|
}
|
|
if err = conn.Send("ZREM", args...); err != nil {
|
|
log.Error("conn.Send(ZREM %s) error(%v)", key, err)
|
|
return
|
|
}
|
|
count++
|
|
}
|
|
if err = conn.Flush(); err != nil {
|
|
log.Error("conn.Flush error(%v)", err)
|
|
return
|
|
}
|
|
for i := 0; i < count; i++ {
|
|
if _, err = conn.Receive(); err != nil {
|
|
log.Error("conn.Receive() error(%v)", err)
|
|
return
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
// ExpireFolder ...
|
|
func (d *Dao) ExpireFolder(c context.Context, kind string, ID int64) (ok bool, err error) {
|
|
conn := d.redis.Get(c)
|
|
defer conn.Close()
|
|
if ok, err = redis.Bool(conn.Do("EXPIRE", keyFolderIdx(kind, ID), d.redisExpire)); err != nil {
|
|
log.Error("conn.Do(EXPIRE) error(%v)", err)
|
|
}
|
|
return
|
|
}
|
|
|
|
// AddFolder ...
|
|
func (d *Dao) AddFolder(c context.Context, keyMapping map[string][]*model.Reply) (err error) {
|
|
conn := d.redis.Get(c)
|
|
defer conn.Close()
|
|
count := 0
|
|
for key, rps := range keyMapping {
|
|
var args []interface{}
|
|
args = append(args, key)
|
|
for _, rp := range rps {
|
|
args = append(args, rp.Floor)
|
|
args = append(args, rp.ID)
|
|
}
|
|
if err = conn.Send("ZADD", args...); err != nil {
|
|
log.Error("conn.Send(ZADD %s) error(%v)", key, err)
|
|
return
|
|
}
|
|
count++
|
|
if err = conn.Send("EXPIRE", key, d.redisExpire); err != nil {
|
|
return
|
|
}
|
|
count++
|
|
}
|
|
if err = conn.Flush(); err != nil {
|
|
log.Error("conn.Flush error(%v)", err)
|
|
return
|
|
}
|
|
for i := 0; i < count; i++ {
|
|
if _, err = conn.Receive(); err != nil {
|
|
log.Error("conn.Receive() error(%v)", err)
|
|
return
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
// RemFolder ...
|
|
func (d *Dao) RemFolder(c context.Context, kind string, ID, rpID int64) (err error) {
|
|
conn := d.redis.Get(c)
|
|
defer conn.Close()
|
|
key := keyFolderIdx(kind, ID)
|
|
if _, err = conn.Do("ZREM", key, rpID); err != nil {
|
|
log.Error("conn.Do(ZREM) error(%v)", err)
|
|
}
|
|
return
|
|
}
|