Create & Init Project...

This commit is contained in:
2019-04-22 18:49:16 +08:00
commit fc4fa37393
25440 changed files with 4054998 additions and 0 deletions

View File

@@ -0,0 +1,81 @@
load(
"@io_bazel_rules_go//proto:def.bzl",
"go_proto_library",
)
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = [
"achieve.go",
"addit.go",
"attr.go",
"audit.go",
"following.go",
"log.go",
"rpc.go",
"stat.go",
],
embed = [":model_go_proto"],
importpath = "go-common/app/service/main/relation/model",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//library/time:go_default_library",
"@com_github_gogo_protobuf//gogoproto:go_default_library",
"@com_github_gogo_protobuf//proto:go_default_library",
],
)
proto_library(
name = "model_proto",
srcs = [
"model.proto",
],
tags = ["manual"],
visibility = ["//visibility:public"],
deps = [
"@gogo_special_proto//github.com/gogo/protobuf/gogoproto",
],
)
go_proto_library(
name = "model_go_proto",
compilers = ["@io_bazel_rules_go//proto:gogo_proto"],
importpath = "go-common/app/service/main/relation/model",
proto = ":model_proto",
tags = ["manual"],
visibility = ["//visibility:public"],
deps = [
"//library/time:go_default_library",
"@com_github_gogo_protobuf//gogoproto:go_default_library",
"@com_github_gogo_protobuf//proto:go_default_library",
"@com_github_gogo_protobuf//sortkeys:go_default_library",
"@com_github_gogo_protobuf//types:go_default_library",
"@io_bazel_rules_go//proto/wkt:any_go_proto",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [
":package-srcs",
"//app/service/main/relation/model/i64b:all-srcs",
"//app/service/main/relation/model/sets:all-srcs",
],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,12 @@
package model
// Achieve is
type Achieve struct {
Award string `json:"award"`
Mid int64 `json:"mid"`
}
// AchieveGetReply is
type AchieveGetReply struct {
AwardToken string `json:"award_token"`
}

View File

@@ -0,0 +1,12 @@
package model
// AchieveFlag is
type AchieveFlag uint64
// const
var (
EmptyAchieve = AchieveFlag(0)
FollowerAchieve1k = AchieveFlag(1 << 0)
FollowerAchieve5k = AchieveFlag(1 << 1)
FollowerAchieve10k = AchieveFlag(1 << 2)
)

View File

@@ -0,0 +1,51 @@
package model
// attribute bit. priority black > following > whisper > no relation.
const (
AttrNoRelation = uint32(0)
AttrWhisper = uint32(1)
AttrFollowing = uint32(1) << 1
AttrFriend = uint32(1) << 2
AttrBlack = uint32(1) << 7
// 128129,130 变为 0 时候status = 1
StatusOK = 0
StatusDel = 1
)
// relation act type.
const (
ActAddFollowing = int8(1)
ActDelFollowing = int8(2)
ActAddWhisper = int8(3)
ActDelWhisper = int8(4)
ActAddBlack = int8(5)
ActDelBalck = int8(6)
ActDelFollower = int8(7)
)
// Attr get real attribute by the specified priority.
func Attr(attribute uint32) uint32 {
if attribute&AttrBlack > 0 {
return AttrBlack
}
if attribute&AttrFriend > 0 {
return AttrFriend
}
if attribute&AttrFollowing > 0 {
return AttrFollowing
}
if attribute&AttrWhisper > 0 {
return AttrWhisper
}
return AttrNoRelation
}
// SetAttr set attribute.
func SetAttr(attribute uint32, mask uint32) uint32 {
return attribute | mask
}
// UnsetAttr unset attribute.
func UnsetAttr(attribute uint32, mask uint32) uint32 {
return attribute & ^mask // ^ 按位取反
}

View File

@@ -0,0 +1,19 @@
package model
// Audit member audit info
type Audit struct {
Mid int64 `json:"mid"`
BindTel bool `json:"bind_tel"`
BindMail bool `json:"bind_mail"`
Rank int64 `json:"rank"`
Blocked bool `json:"blocked"`
}
// PassportDetail passportDetail
type PassportDetail struct {
Mid int64 `json:"mid"`
Email string `json:"email"`
Phone string `json:"telphone"`
Spacesta int8 `json:"spacesta"`
JoinTime int64 `json:"join_time"`
}

View File

@@ -0,0 +1,68 @@
package model
var (
_emptyFollowings = make([]*Following, 0)
)
// Black get if black.
func (f *Following) Black() bool {
return AttrBlack == Attr(f.Attribute)
}
// Friend get if both way following.
func (f *Following) Friend() bool {
return AttrFriend == Attr(f.Attribute)
}
// Following get if following.
func (f *Following) Following() bool {
return AttrFollowing == Attr(f.Attribute) || Attr(f.Attribute) == AttrFriend
}
// Whisper get if whisper.
func (f *Following) Whisper() bool {
return AttrWhisper == Attr(f.Attribute)
}
// Filter filter followings by the given attribute.
func Filter(fs []*Following, attr uint32) (res []*Following) {
for _, f := range fs {
// NOTE: if current attribute evaluated by Attr() matched, then continue,
// this includes the situation that matches black, friend, whisper, and no-relation directly.
// Now we have following to deal with, since we know that the attribute friend
// can either do not exist or exists with following at the same time,
// to deal with this situation, we need to filter for items which have 1 on the bit that attr stands for,
// and especially, the attribute it self cannot be black because the attribute black has the highest priority,
// when it exists, it shadows other bits, including friend, following, whisper, no-relation,
// there is no need to do further calculate,
// more specifically, black when black included, the value of f.Attribute&attr may greater than 0
// when f.Attribute is 128+2 or 128+1 and the corresponding attr is 2 or 1,
// which is not as we expected.
if f.Attribute == 4 {
f.Attribute = 6
}
if (Attr(f.Attribute) == attr) || (!f.Black() && f.Attribute&attr > 0) {
res = append(res, f)
}
}
if len(res) == 0 {
res = _emptyFollowings
}
return
}
// SortFollowings sort followings by the mtime desc.
type SortFollowings []*Following
func (fs SortFollowings) Len() int {
return len(fs)
}
func (fs SortFollowings) Swap(i, j int) {
fs[i], fs[j] = fs[j], fs[i]
}
func (fs SortFollowings) Less(i, j int) bool {
if fs[i].MTime == fs[j].MTime {
return fs[i].Mid < fs[j].Mid
}
return fs[i].MTime.Time().After(fs[j].MTime.Time())
}

View File

@@ -0,0 +1,35 @@
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)
package(default_visibility = ["//visibility:public"])
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
)
go_library(
name = "go_default_library",
srcs = ["xints.go"],
importpath = "go-common/app/service/main/relation/model/i64b",
tags = ["automanaged"],
)
go_test(
name = "go_default_test",
srcs = ["xints_test.go"],
embed = [":go_default_library"],
rundir = ".",
tags = ["automanaged"],
)

View File

@@ -0,0 +1,91 @@
package i64b
import (
"database/sql/driver"
"encoding/binary"
)
//Int64Bytes be used to MySql\Protobuf varbinary converting.
type Int64Bytes []int64
// MarshalTo marshal int64 slice to bytes,each int64 will occupy Fixed 8 bytes.
//if the argument data not supplied with the full size,it will return the actual written size
func (is Int64Bytes) MarshalTo(data []byte) (int, error) {
for i, n := range is {
start := i * 8
end := (i + 1) * 8
if len(data) < end {
return start, nil
}
bs := data[start:end]
binary.BigEndian.PutUint64(bs, uint64(n))
}
return 8 * len(is), nil
}
// Size return the total size it will occupy in bytes
func (is Int64Bytes) Size() int {
return len(is) * 8
}
// Unmarshal parse the data into int64 slice
func (is *Int64Bytes) Unmarshal(data []byte) error {
return is.Scan(data)
}
// Scan parse the data into int64 slice
func (is *Int64Bytes) Scan(src interface{}) (err error) {
switch sc := src.(type) {
case []byte:
var res []int64
for i := 0; i < len(sc) && i+8 <= len(sc); i += 8 {
ui := binary.BigEndian.Uint64(sc[i : i+8])
res = append(res, int64(ui))
}
*is = res
}
return
}
// Value marshal int64 slice to driver.Value,each int64 will occupy Fixed 8 bytes
func (is Int64Bytes) Value() (driver.Value, error) {
return is.Bytes(), nil
}
// Bytes marshal int64 slice to bytes,each int64 will occupy Fixed 8 bytes
func (is Int64Bytes) Bytes() []byte {
res := make([]byte, 0, 8*len(is))
for _, i := range is {
bs := make([]byte, 8)
binary.BigEndian.PutUint64(bs, uint64(i))
res = append(res, bs...)
}
return res
}
// Evict get rid of the sepcified num from the slice
func (is *Int64Bytes) Evict(e int64) (ok bool) {
res := make([]int64, len(*is)-1)
for _, v := range *is {
if v != e {
res = append(res, v)
} else {
ok = true
}
}
*is = res
return
}
// Exist judge the sepcified num is in the slice or not
func (is Int64Bytes) Exist(i int64) (e bool) {
for _, v := range is {
if v == i {
e = true
return
}
}
return
}

View File

@@ -0,0 +1,74 @@
package i64b
import (
"testing"
)
func TestMarshalAndUnmarshal(t *testing.T) {
var a = Int64Bytes{1, 2, 3}
data := make([]byte, a.Size())
n, err := a.MarshalTo(data)
if n != 24 {
t.Logf("marshal size must be 24")
t.FailNow()
}
if err != nil {
t.Fatalf("err:%v", err)
}
var b Int64Bytes
err = b.Unmarshal(data)
if err != nil {
t.Fatalf("err:%v", err)
}
if b[0] != 1 || b[1] != 2 || b[2] != 3 {
t.Logf("unmarshal failed!b:%v", b)
t.FailNow()
}
}
func TestUncompleteMarshal(t *testing.T) {
var a = Int64Bytes{1, 2, 3}
data := make([]byte, a.Size()-7)
n, err := a.MarshalTo(data)
if n != 16 {
t.Logf("marshal size must be 16")
t.FailNow()
}
if err != nil {
t.Fatalf("err:%v", err)
}
var b Int64Bytes
err = b.Unmarshal(data)
if err != nil {
t.Fatalf("err:%v", err)
}
if b[0] != 1 || b[1] != 2 {
t.Logf("unmarshal failed!b:%v", b)
t.FailNow()
}
}
func TestNilMarshal(t *testing.T) {
var a = Int64Bytes{1, 2, 3}
var data []byte
n, err := a.MarshalTo(data)
if n != 0 {
t.Logf("marshal size must be 0")
t.FailNow()
}
if err != nil {
t.Fatalf("err:%v", err)
}
var b Int64Bytes
err = b.Unmarshal(data)
if err != nil {
t.Fatalf("err:%v", err)
}
if b != nil {
t.Logf("unmarshal failed!b:%v", b)
t.FailNow()
}
}

View File

@@ -0,0 +1,26 @@
package model
// Reverse is
func (rl *RelationLog) Reverse() *RelationLog {
content := make(map[string]string, len(rl.Content))
for k, v := range rl.Content {
content[k] = v
}
reversed := &RelationLog{
// reverse
Mid: rl.Fid,
Fid: rl.Mid,
Ts: rl.Ts,
Source: rl.Source,
Ip: rl.Ip,
Buvid: rl.Buvid,
// reverse
FromAttr: rl.FromRevAttr,
ToAttr: rl.ToRevAttr,
FromRevAttr: rl.FromAttr,
ToRevAttr: rl.ToAttr,
Content: content,
}
return reversed
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,87 @@
syntax = "proto3";
package account.service.relation;
import "github.com/gogo/protobuf/gogoproto/gogo.proto";
option go_package = "model";
option (gogoproto.goproto_enum_prefix_all) = false;
option (gogoproto.goproto_getters_all) = false;
option (gogoproto.unmarshaler_all) = true;
option (gogoproto.marshaler_all) = true;
option (gogoproto.sizer_all) = true;
message Tag {
int64 id = 1 [(gogoproto.jsontag) = "id"];
string name = 2 [(gogoproto.jsontag) = "name"];
int64 status = 3 [(gogoproto.jsontag) = "status"];
int64 ctime = 4 [(gogoproto.jsontag) = "-", (gogoproto.casttype) = "go-common/library/time.Time", (gogoproto.customname) = "CTime"];
int64 mtime = 5 [(gogoproto.jsontag) = "mtime", (gogoproto.casttype) = "go-common/library/time.Time", (gogoproto.customname) = "MTime"];
}
message Tags {
map<int64,Tag> tags= 1 ;
}
message TagUser {
int64 fid = 1 [(gogoproto.jsontag) = "fid"];
repeated int64 tag = 2 [(gogoproto.jsontag) = "tag"];
int64 ctime = 3 [(gogoproto.jsontag) = "-", (gogoproto.casttype) = "go-common/library/time.Time", (gogoproto.customname) = "CTime"];
int64 mtime = 4 [(gogoproto.jsontag) = "mtime", (gogoproto.casttype) = "go-common/library/time.Time", (gogoproto.customname) = "MTime"];
}
message TagCount {
int64 tagid = 1 [(gogoproto.jsontag) = "tagid"];
string name = 2 [(gogoproto.jsontag) = "name"];
int64 count = 3 [(gogoproto.jsontag) = "count"];
}
message TagCountList {
repeated TagCount tag_count_list = 1;
}
message Following {
int64 mid = 1 [(gogoproto.jsontag) = "mid"];
uint32 attribute = 2 [(gogoproto.jsontag) = "attribute"];
uint32 source = 3 [(gogoproto.jsontag) = "-"];
int64 ctime = 4 [(gogoproto.jsontag) = "-", (gogoproto.casttype) = "go-common/library/time.Time", (gogoproto.customname) = "CTime"];
int64 mtime = 5 [(gogoproto.jsontag) = "mtime", (gogoproto.casttype) = "go-common/library/time.Time", (gogoproto.customname) = "MTime"];
repeated int64 tag = 6 [(gogoproto.jsontag) = "tag"];
int32 special = 7 [(gogoproto.jsontag) = "special"];
}
message FollowingList {
repeated Following following_list = 1;
}
message Stat {
int64 mid = 1 [(gogoproto.jsontag) = "mid"];
int64 following = 2 [(gogoproto.jsontag) = "following"];
int64 whisper = 3 [(gogoproto.jsontag) = "whisper"];
int64 black = 4 [(gogoproto.jsontag) = "black"];
int64 follower = 5 [(gogoproto.jsontag) = "follower"];
int64 ctime = 6 [(gogoproto.jsontag) = "-", (gogoproto.casttype) = "go-common/library/time.Time", (gogoproto.customname) = "CTime"];
int64 mtime = 7 [(gogoproto.jsontag) = "-", (gogoproto.casttype) = "go-common/library/time.Time", (gogoproto.customname) = "MTime"];
}
message FollowingTags {
uint32 attr = 1 [(gogoproto.jsontag) = "attr"];
int64 ts = 2 [(gogoproto.jsontag) = "ts", (gogoproto.casttype) = "go-common/library/time.Time"];
repeated int64 tag_ids = 3 [(gogoproto.jsontag) = "tag"];
int32 special =4 [(gogoproto.jsontag) = "special"];
}
message GlobalRec {
repeated int64 fids = 1 [(gogoproto.jsontag) = "fids"];
}
message RelationLog {
int64 mid = 1 [(gogoproto.jsontag) = "mid"];
int64 fid = 2 [(gogoproto.jsontag) = "fid"];
int64 ts = 3 [(gogoproto.jsontag) = "ts"];
uint32 source = 4 [(gogoproto.jsontag) = "source"];
string ip = 5 [(gogoproto.jsontag) = "ip"];
string buvid = 6 [(gogoproto.jsontag) = "buvid"];
uint32 from_attr = 7 [(gogoproto.jsontag) = "from_attr"];
uint32 to_attr = 8 [(gogoproto.jsontag) = "to_attr"];
uint32 from_rev_attr = 9 [(gogoproto.jsontag) = "from_rev_attr"];
uint32 to_rev_attr = 10 [(gogoproto.jsontag) = "to_rev_attr"];
map<string, string> content = 11 [(gogoproto.jsontag) = "content"];
}

View File

@@ -0,0 +1,104 @@
package model
// ArgMid mid
type ArgMid struct {
Mid int64 `json:"mid" form:"mid" validate:"required" params:"mid;Required;Min(1)"`
RealIP string
}
// ArgSameFollowing is
type ArgSameFollowing struct {
Mid1 int64 `json:"mid1" form:"mid1" validate:"required" params:"mid1;Required;Min(1)"`
Mid2 int64 `json:"mid2" form:"mid2" validate:"required" params:"mid2;Required;Min(1)"`
}
// ArgMids mids
type ArgMids struct {
Mids []int64
RealIP string
}
// ArgFollowing following
type ArgFollowing struct {
Mid int64
Fid int64 `json:"fid" form:"fid" validate:"required" params:"fid;Required;Min(1)"`
Source uint8
RealIP string
Action int8
Infoc map[string]string
}
// ArgRelation relation
type ArgRelation struct {
Mid, Fid int64
RealIP string
}
// ArgRelations relations
type ArgRelations struct {
Mid int64
Fids []int64
RealIP string
}
// ArgTag tag
type ArgTag struct {
Mid int64
Tag string
RealIP string
}
// ArgTagId tag id
type ArgTagId struct {
Mid int64
TagId int64
RealIP string
}
// ArgTagDel tag del
type ArgTagDel struct {
Mid int64
TagId int64
RealIP string
}
// ArgTagUpdate tag update
type ArgTagUpdate struct {
Mid int64
TagId int64
New string
RealIP string
}
// ArgTagsMoveUsers tags move users
type ArgTagsMoveUsers struct {
Mid int64
BeforeID int64
AfterTagIds string
Fids string
RealIP string
}
// ArgPrompt rpc promt arg.
type ArgPrompt struct {
Mid int64 `form:"mid" params:"mid"`
Fid int64 `form:"fid" validate:"required" params:"fid;Required;Min(1)"`
Btype int8 `form:"btype" validate:"required,min=1" params:"btype;Required;Min(1)"`
}
// ArgAchieveGet is
type ArgAchieveGet struct {
Award string `form:"award" validate:"required"`
Mid int64 `form:"mid" validate:"required"`
}
// ArgAchieve is
type ArgAchieve struct {
AwardToken string `form:"award_token" validate:"required"`
}
// FollowerNotifySetting show the follower-notify setting state
type FollowerNotifySetting struct {
Mid int64 `json:"mid"`
Enabled bool `json:"enabled"`
}

View File

@@ -0,0 +1,31 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = [
"int64.go",
"sets.go",
],
importpath = "go-common/app/service/main/relation/model/sets",
tags = ["automanaged"],
visibility = ["//visibility:public"],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,185 @@
package sets
import (
"reflect"
"sort"
)
// Int64 is sets.Int64 is a set of int64s, implemented via map[int64]struct{} for minimal memory consumption.
type Int64 map[int64]Empty
// NewInt64 creates a Int64 from a list of values.
func NewInt64(items ...int64) Int64 {
ss := Int64{}
ss.Insert(items...)
return ss
}
// Int64KeySet creates a Int64 from a keys of a map[int64](? extends interface{}).
// If the value passed in is not actually a map, this will panic.
func Int64KeySet(theMap interface{}) Int64 {
v := reflect.ValueOf(theMap)
ret := Int64{}
for _, keyValue := range v.MapKeys() {
ret.Insert(keyValue.Interface().(int64))
}
return ret
}
// Insert adds items to the set.
func (s Int64) Insert(items ...int64) {
for _, item := range items {
s[item] = Empty{}
}
}
// Delete removes all items from the set.
func (s Int64) Delete(items ...int64) {
for _, item := range items {
delete(s, item)
}
}
// Has returns true if and only if item is contained in the set.
func (s Int64) Has(item int64) bool {
_, contained := s[item]
return contained
}
// HasAll returns true if and only if all items are contained in the set.
func (s Int64) HasAll(items ...int64) bool {
for _, item := range items {
if !s.Has(item) {
return false
}
}
return true
}
// HasAny returns true if any items are contained in the set.
func (s Int64) HasAny(items ...int64) bool {
for _, item := range items {
if s.Has(item) {
return true
}
}
return false
}
// Difference returns a set of objects that are not in s2
// For example:
// s1 = {a1, a2, a3}
// s2 = {a1, a2, a4, a5}
// s1.Difference(s2) = {a3}
// s2.Difference(s1) = {a4, a5}
func (s Int64) Difference(s2 Int64) Int64 {
result := NewInt64()
for key := range s {
if !s2.Has(key) {
result.Insert(key)
}
}
return result
}
// Union returns a new set which includes items in either s1 or s2.
// For example:
// s1 = {a1, a2}
// s2 = {a3, a4}
// s1.Union(s2) = {a1, a2, a3, a4}
// s2.Union(s1) = {a1, a2, a3, a4}
func (s1 Int64) Union(s2 Int64) Int64 {
result := NewInt64()
for key := range s1 {
result.Insert(key)
}
for key := range s2 {
result.Insert(key)
}
return result
}
// Intersection returns a new set which includes the item in BOTH s1 and s2
// For example:
// s1 = {a1, a2}
// s2 = {a2, a3}
// s1.Intersection(s2) = {a2}
func (s1 Int64) Intersection(s2 Int64) Int64 {
var walk, other Int64
result := NewInt64()
if s1.Len() < s2.Len() {
walk = s1
other = s2
} else {
walk = s2
other = s1
}
for key := range walk {
if other.Has(key) {
result.Insert(key)
}
}
return result
}
// IsSuperset returns true if and only if s1 is a superset of s2.
func (s1 Int64) IsSuperset(s2 Int64) bool {
for item := range s2 {
if !s1.Has(item) {
return false
}
}
return true
}
// Equal returns true if and only if s1 is equal (as a set) to s2.
// Two sets are equal if their membership is identical.
// (In practice, this means same elements, order doesn't matter)
func (s1 Int64) Equal(s2 Int64) bool {
return len(s1) == len(s2) && s1.IsSuperset(s2)
}
type sortableSliceOfInt64 []int64
func (s sortableSliceOfInt64) Len() int { return len(s) }
func (s sortableSliceOfInt64) Less(i, j int) bool { return lessInt64(s[i], s[j]) }
func (s sortableSliceOfInt64) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
// List returns the contents as a sorted int64 slice.
func (s Int64) List() []int64 {
res := make(sortableSliceOfInt64, 0, len(s))
for key := range s {
res = append(res, key)
}
sort.Sort(res)
return []int64(res)
}
// UnsortedList returns the slice with contents in random order.
func (s Int64) UnsortedList() []int64 {
res := make([]int64, 0, len(s))
for key := range s {
res = append(res, key)
}
return res
}
// Returns a single element from the set.
func (s Int64) PopAny() (int64, bool) {
for key := range s {
s.Delete(key)
return key, true
}
var zeroValue int64
return zeroValue, false
}
// Len returns the size of the set.
func (s Int64) Len() int {
return len(s)
}
func lessInt64(lhs, rhs int64) bool {
return lhs < rhs
}

View File

@@ -0,0 +1,4 @@
package sets
// Empty is
type Empty struct{}

View File

@@ -0,0 +1,43 @@
package model
// Stat struct of Stat.
// type Stat struct {
// Mid int64 `json:"-"`
// Following int64 `json:"following"`
// Whisper int64 `json:"whisper"`
// Black int64 `json:"black"`
// Follower int64 `json:"follower"`
// CTime time.Time `json:"-"`
// MTime time.Time `json:"-"`
// }
// Count get count of following, including attr following, whisper.
func (st *Stat) Count() int {
return int(st.Following + st.Whisper)
}
// BlackCount get count of black, including attr black.
func (st *Stat) BlackCount() int {
return int(st.Black)
}
// Empty get if the stat is empty.
func (st *Stat) Empty() bool {
return st.Following == 0 && st.Whisper == 0 && st.Black == 0 && st.Follower == 0
}
// Fill fill by the incoming stat with its non-negative fields.
func (st *Stat) Fill(ost *Stat) {
if ost.Following >= 0 {
st.Following = ost.Following
}
if ost.Whisper >= 0 {
st.Whisper = ost.Whisper
}
if ost.Black >= 0 {
st.Black = ost.Black
}
if ost.Follower >= 0 {
st.Follower = ost.Follower
}
}