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.
233 lines
5.6 KiB
233 lines
5.6 KiB
package dao |
|
|
|
import ( |
|
"context" |
|
"fmt" |
|
"io" |
|
"math" |
|
"strconv" |
|
"strings" |
|
"sync" |
|
|
|
"go-common/app/admin/main/member/model" |
|
"go-common/library/log" |
|
"go-common/library/sync/errgroup" |
|
xtime "go-common/library/time" |
|
|
|
"github.com/tsuna/gohbase/hrpc" |
|
) |
|
|
|
var ( |
|
tableFaceByMidHistory = "account:user_face" |
|
tableFaceByOPHistory = "account:user_face_admin" |
|
) |
|
|
|
// reverse returns its argument string reversed rune-wise left to right. |
|
func reverse(s string) string { |
|
r := []rune(s) |
|
for i, j := 0, len(r)-1; i < len(r)/2; i, j = i+1, j-1 { |
|
r[i], r[j] = r[j], r[i] |
|
} |
|
return string(r) |
|
} |
|
|
|
func rpad(s string, c string, l int) string { |
|
dt := l - len(s) |
|
if dt <= 0 { |
|
return s |
|
} |
|
return s + strings.Repeat(c, dt) |
|
} |
|
|
|
// func lpad(s string, c string, l int) string { |
|
// dt := l - len(s) |
|
// if dt <= 0 { |
|
// return s |
|
// } |
|
// return strings.Repeat(c, dt) + s |
|
// } |
|
|
|
func midKey(mid int64, time xtime.Time) string { |
|
suf := rpad(strconv.FormatInt(math.MaxInt64-int64(time), 10), "0", 10) |
|
return fmt.Sprintf("%s%s", rpad(reverse(strconv.FormatInt(mid, 10)), "0", 10), string(suf[len(suf)-10:])) |
|
} |
|
|
|
func operatorKey(operator string, time xtime.Time) string { |
|
suf := rpad(strconv.FormatInt(math.MaxInt64-int64(time), 10), "0", 10) |
|
return fmt.Sprintf("%s%s", rpad(operator, "-", 20), string(suf[len(suf)-10:])) |
|
} |
|
|
|
func scanTimes(tDuration xtime.Time) int { |
|
times := int(tDuration)/(60*60) + 5 |
|
if times > 50 { |
|
times = 50 |
|
} |
|
return times |
|
} |
|
|
|
// FaceHistoryByMid is. |
|
func (d *Dao) FaceHistoryByMid(ctx context.Context, arg *model.ArgFaceHistory) (model.FaceRecordList, error) { |
|
size := arg.ETime - arg.STime |
|
stimes := scanTimes(size) |
|
chunk := xtime.Time(size / xtime.Time(stimes)) |
|
if chunk == 0 { |
|
chunk = size |
|
} |
|
rendKey, rstartKey := midKey(arg.Mid, arg.STime), midKey(arg.Mid, arg.ETime) |
|
|
|
eg := errgroup.Group{} |
|
lock := sync.RWMutex{} |
|
records := make(model.FaceRecordList, 0) |
|
for i := 0; i < stimes; i++ { |
|
times := i + 1 |
|
stime := arg.STime + (chunk * xtime.Time(i)) |
|
etime := arg.STime + (chunk * xtime.Time(i+1)) |
|
eg.Go(func() error { |
|
endKey, startKey := midKey(arg.Mid, stime), midKey(arg.Mid, etime) |
|
log.Info("FaceHistoryByMid: range: start: %s end: %s with times: %d scaning key: start: %s end: %s", |
|
rstartKey, rendKey, times, startKey, endKey) |
|
|
|
scanner, err := d.fhbymidhbase.ScanRangeStr(ctx, tableFaceByMidHistory, startKey, endKey) |
|
if err != nil { |
|
log.Error("hbase.ScanRangeStr(%s,%+v) error(%v)", tableFaceByMidHistory, arg, err) |
|
return err |
|
} |
|
for { |
|
r, err := scanner.Next() |
|
if err != nil { |
|
if err != io.EOF { |
|
return err |
|
} |
|
break |
|
} |
|
lock.Lock() |
|
records = append(records, toMidFaceRecord(r)) |
|
lock.Unlock() |
|
} |
|
return nil |
|
}) |
|
|
|
if etime >= arg.ETime { |
|
break |
|
} |
|
} |
|
if err := eg.Wait(); err != nil { |
|
return nil, err |
|
} |
|
return records, nil |
|
} |
|
|
|
// FaceHistoryByOP is. |
|
func (d *Dao) FaceHistoryByOP(ctx context.Context, arg *model.ArgFaceHistory) (model.FaceRecordList, error) { |
|
size := arg.ETime - arg.STime |
|
stimes := scanTimes(size) |
|
chunk := xtime.Time(size / xtime.Time(stimes)) |
|
if chunk == 0 { |
|
chunk = size |
|
} |
|
rendKey, rstartKey := operatorKey(arg.Operator, arg.STime), operatorKey(arg.Operator, arg.ETime) |
|
|
|
eg := errgroup.Group{} |
|
lock := sync.RWMutex{} |
|
records := make(model.FaceRecordList, 0) |
|
for i := 0; i < stimes; i++ { |
|
times := i + 1 |
|
stime := arg.STime + (chunk * xtime.Time(i)) |
|
etime := arg.STime + (chunk * xtime.Time(i+1)) |
|
eg.Go(func() error { |
|
endKey, startKey := operatorKey(arg.Operator, stime), operatorKey(arg.Operator, etime) |
|
log.Info("FaceHistoryByOP: range: start: %s end: %s with times: %d scaning key: start: %s end: %s", |
|
rstartKey, rendKey, times, startKey, endKey) |
|
|
|
scanner, err := d.fhbyophbase.ScanRangeStr(ctx, tableFaceByOPHistory, startKey, endKey) |
|
if err != nil { |
|
log.Error("hbase.ScanRangeStr(%s,%+v) error(%v)", tableFaceByOPHistory, arg, err) |
|
return err |
|
} |
|
|
|
for { |
|
r, err := scanner.Next() |
|
if err != nil { |
|
if err != io.EOF { |
|
return err |
|
} |
|
break |
|
} |
|
lock.Lock() |
|
records = append(records, toOPFaceRecord(r)) |
|
lock.Unlock() |
|
} |
|
return nil |
|
}) |
|
|
|
if etime >= arg.ETime { |
|
break |
|
} |
|
} |
|
|
|
if err := eg.Wait(); err != nil { |
|
return nil, err |
|
} |
|
|
|
return records, nil |
|
} |
|
|
|
func toOPFaceRecord(res *hrpc.Result) *model.FaceRecord { |
|
l := &model.FaceRecord{} |
|
for _, c := range res.Cells { |
|
key := string(c.Row) |
|
qf := string(c.Qualifier) |
|
v := string(c.Value) |
|
log.Info("Retrieving op face record history: key(%s) qualifier(%s) value(%s)", key, qf, v) |
|
// fill fields |
|
switch qf { |
|
case "id": |
|
l.ID, _ = strconv.ParseInt(v, 10, 64) |
|
case "mid": |
|
l.Mid, _ = strconv.ParseInt(v, 10, 64) |
|
case "sta": |
|
l.Status = model.ParseStatus(v) |
|
case "op": |
|
l.Operator = v |
|
case "old": |
|
l.OldFace = v |
|
case "new": |
|
l.NewFace = v |
|
case "apply": |
|
l.ApplyTime = model.ParseApplyTime(v) |
|
case "mtm": |
|
l.ModifyTime, _ = model.ParseLogTime(v) |
|
} |
|
} |
|
return l |
|
} |
|
|
|
func toMidFaceRecord(res *hrpc.Result) *model.FaceRecord { |
|
l := &model.FaceRecord{} |
|
for _, c := range res.Cells { |
|
key := string(c.Row) |
|
qf := string(c.Qualifier) |
|
v := string(c.Value) |
|
log.Info("Retrieving mid face record history: key(%s) qualifier(%s) value(%s)", key, qf, v) |
|
// fill fields |
|
switch qf { |
|
// case "id": |
|
// l.ID, _ = strconv.ParseInt(v, 10, 64) |
|
case "mid": |
|
l.Mid, _ = strconv.ParseInt(v, 10, 64) |
|
case "s": |
|
l.Status = model.ParseStatus(v) |
|
case "op": |
|
l.Operator = v |
|
case "of": |
|
l.OldFace = v |
|
case "nf": |
|
l.NewFace = v |
|
case "at": |
|
l.ApplyTime = model.ParseApplyTime(v) |
|
case "mt": |
|
l.ModifyTime, _ = model.ParseLogTime(v) |
|
} |
|
} |
|
return l |
|
}
|
|
|