You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
452 lines
11 KiB
452 lines
11 KiB
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 |
|
}
|
|
|