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,23 @@
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [
":package-srcs",
"//app/service/live/xuser/api/grpc:all-srcs",
"//app/service/live/xuser/cmd:all-srcs",
"//app/service/live/xuser/conf:all-srcs",
"//app/service/live/xuser/dao:all-srcs",
"//app/service/live/xuser/model:all-srcs",
"//app/service/live/xuser/server/grpc:all-srcs",
"//app/service/live/xuser/server/http:all-srcs",
"//app/service/live/xuser/service:all-srcs",
],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,43 @@
### v1.1.8
1. try fix vip info reply panic
2. 添加guard client
3. 添加获取某个主播全量守护数量接口
4. 添加multi守护接口
5. 用户经验接口替换
6. 替换用户经验接口
### v1.1.7
1. 优化房管admin change message
### v1.1.6
1. xuser vip获取db记录时优化sql: no rows in result set error
2. 迁移某个主播最近开通的总督弹窗接口
### v1.1.5
1. 大航海读接口迁移
2. 异步更新经验
### v1.1.4
1. 添加新接口判断是否房管不返回用户信息
### v1.1.3
1.兼容vip cache中vip/svip可能是int/string的问题
### v1.1.2
1.修复vip 从db/cache获取数据时可能是过期数据导致vip/svip状态返回不正确的问题
### v1.1.1
1. 添加房管不再发送私信
### v1.1.0
1. 5.36房管 & 房管全量接口 gRPC api
2. 添加主播等级颜色
### v1.0.1
1. 购买姥爷更新流水表来源字段
2. 添加用户经验接口字段
### v1.0.0
1. 直播vip buy & vip info grpc api
2. 经验
3. 购买大航海

View File

@@ -0,0 +1,10 @@
# Owner
xiehaishen
wujunchen
kuangxibin
# Author
xiehaishen
# Reviewer
xiehaishen

View File

@@ -0,0 +1,9 @@
# Owner
xiehaishen
wujunchen
# Author
xiehaishen
# Reviewer
xiehaishen

View File

@@ -0,0 +1,14 @@
# See the OWNERS docs at https://go.k8s.io/owners
approvers:
- kuangxibin
- wujunchen
- xiehaishen
labels:
- live
- service
- service/live/xuser
options:
no_parent_owners: true
reviewers:
- xiehaishen

View File

@@ -0,0 +1,16 @@
# xuser-service
## 项目简介
1.直播用户信息服务,目前包含
> 姥爷
> 大航海
> 经验
> 房管
## 编译环境
## 依赖包
## 编译执行

View File

@@ -0,0 +1,58 @@
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",
)
proto_library(
name = "grpc_proto",
srcs = ["xuser.proto"],
tags = ["automanaged"],
deps = ["@gogo_special_proto//github.com/gogo/protobuf/gogoproto"],
)
go_proto_library(
name = "grpc_go_proto",
compilers = ["@io_bazel_rules_go//proto:gogofast_proto"],
importpath = "go-common/app/service/live/xuser/api/grpc",
proto = ":grpc_proto",
tags = ["automanaged"],
deps = ["@com_github_gogo_protobuf//gogoproto:go_default_library"],
)
go_library(
name = "go_default_library",
srcs = [],
embed = [":grpc_go_proto"],
importpath = "go-common/app/service/live/xuser/api/grpc",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"@com_github_gogo_protobuf//gogoproto:go_default_library",
"@com_github_gogo_protobuf//proto:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [
":package-srcs",
"//app/service/live/xuser/api/grpc/client:all-srcs",
"//app/service/live/xuser/api/grpc/v1:all-srcs",
],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,41 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_binary",
"go_library",
)
go_binary(
name = "client",
embed = [":go_default_library"],
tags = ["automanaged"],
)
go_library(
name = "go_default_library",
srcs = ["main.go"],
importpath = "go-common/app/service/live/xuser/api/grpc/client",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/service/live/xuser/api/grpc/v1:go_default_library",
"//library/net/rpc/warden:go_default_library",
"//library/time:go_default_library",
"//vendor/github.com/davecgh/go-spew/spew:go_default_library",
],
)
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,76 @@
package main
import (
"context"
"flag"
"github.com/davecgh/go-spew/spew"
"log"
"time"
"go-common/app/service/live/xuser/api/grpc/v1"
"go-common/library/net/rpc/warden"
xtime "go-common/library/time"
)
var name, addr string
func init() {
flag.StringVar(&name, "name", "lily", "name")
flag.StringVar(&addr, "addr", "127.0.0.1:9004", "server addr")
}
func main() {
flag.Parse()
cfg := &warden.ClientConfig{
Dial: xtime.Duration(time.Second * 3),
Timeout: xtime.Duration(time.Second * 3),
}
cc, err := warden.NewClient(cfg).Dial(context.Background(), addr)
if err != nil {
log.Fatalf("new client failed!err:=%v", err)
return
}
client := v1.NewRoomAdminClient(cc)
//resp, _ := client.IsAny(context.Background(), &v1.RoomAdminShowEntryReq{
// Uid: 1001,
//})
//spew.Dump("IsAny", resp)
//
//resp2, _ := client.GetByUid(context.Background(), &v1.RoomAdminGetByUidReq{
// Uid: 1001,
// Page: 1,
//})
//spew.Dump("GetByUid", resp2)
//
//resp3, err := client.SearchForAdmin(context.Background(), &v1.RoomAdminSearchForAdminReq{
// Uid: 28007796,
// KeyWord: "1001",
//})
//spew.Dump("SearchForAdmin", resp3, err)
//
//resp4, err := client.GetByAnchor(context.Background(), &v1.RoomAdminGetByAnchorReq{
// Page: 0,
// Uid: 28007796,
//})
//spew.Dump("GetByAnchor", resp4, err)
//
//resp5, err := client.Dismiss(context.Background(), &v1.RoomAdminDismissAdminReq{
// Uid: 1001,
// AnchorId: 28007796,
//})
//spew.Dump("Dismiss", resp5, err)
//
//resp6, err := client.Appoint(context.Background(), &v1.RoomAdminAddReq{
// Uid: 1001,
// AnchorId: 28007796,
//})
//spew.Dump("Appoint", resp6, err)
resp7, err := client.IsAdminShort(context.Background(), &v1.RoomAdminIsAdminShortReq{
Uid: 2101323,
Roomid: 5171,
})
spew.Dump("IsAdminShort", resp7, err)
}

View File

@@ -0,0 +1,77 @@
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",
)
proto_library(
name = "v1_proto",
srcs = [
"api.proto",
"exp.proto",
"guard.proto",
"vip.proto",
],
tags = ["automanaged"],
deps = [
"//app/service/live/xuser/api/grpc:grpc_proto",
"@gogo_special_proto//github.com/gogo/protobuf/gogoproto",
],
)
go_proto_library(
name = "v1_go_proto",
compilers = ["@io_bazel_rules_go//proto:gogofast_grpc"],
importpath = "go-common/app/service/live/xuser/api/grpc/v1",
proto = ":v1_proto",
tags = ["automanaged"],
deps = [
"//app/service/live/xuser/api/grpc:grpc_go_proto",
"@com_github_gogo_protobuf//gogoproto:go_default_library",
],
)
go_library(
name = "go_default_library",
srcs = [
"api.bm.go",
"client.go",
"exp.bm.go",
"guard.bm.go",
"vip.bm.go",
],
embed = [":v1_go_proto"],
importpath = "go-common/app/service/live/xuser/api/grpc/v1",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/service/live/xuser/api/grpc:go_default_library",
"//library/net/http/blademaster:go_default_library",
"//library/net/http/blademaster/binding:go_default_library",
"//library/net/rpc/warden:go_default_library",
"@com_github_gogo_protobuf//gogoproto:go_default_library",
"@com_github_gogo_protobuf//proto:go_default_library",
"@org_golang_google_grpc//:go_default_library",
"@org_golang_x_net//context:go_default_library",
],
)
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,61 @@
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",
)
proto_library(
name = "v1_proto",
srcs = ["api.proto"],
tags = ["automanaged"],
deps = ["@gogo_special_proto//github.com/gogo/protobuf/gogoproto"],
)
go_proto_library(
name = "v1_go_proto",
compilers = ["@io_bazel_rules_go//proto:gogofast_grpc"],
importpath = "go-common/app/service/live/xuser/api/grpc/v1",
proto = ":v1_proto",
tags = ["automanaged"],
deps = ["@com_github_gogo_protobuf//gogoproto:go_default_library"],
)
go_library(
name = "go_default_library",
srcs = [
"api.bm.go",
"client.go",
],
embed = [":v1_go_proto"],
importpath = "go-common/app/service/live/xuser/api/grpc/v1",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//library/net/http/blademaster:go_default_library",
"//library/net/rpc/warden:go_default_library",
"@com_github_gogo_protobuf//gogoproto:go_default_library",
"@com_github_gogo_protobuf//proto:go_default_library",
"@org_golang_google_grpc//:go_default_library",
"@org_golang_x_net//context:go_default_library",
],
)
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,198 @@
// Code generated by protoc-gen-bm v0.1, DO NOT EDIT.
// source: api/grpc/v1/api.proto
/*
Package v1 is a generated blademaster stub package.
This code was generated with go-common/app/tool/bmgen/protoc-gen-bm v0.1.
It is generated from these files:
api/grpc/v1/api.proto
*/
package v1
import (
"context"
bm "go-common/library/net/http/blademaster"
"go-common/library/net/http/blademaster/binding"
)
// to suppressed 'imported but not used warning'
var _ *bm.Context
var _ context.Context
var _ binding.StructValidator
// ===================
// RoomAdmin Interface
// ===================
// History 相关服务
type RoomAdmin interface {
// 根据登录态获取功能入口是否显示, 需要登录态
IsAny(ctx context.Context, req *RoomAdminShowEntryReq) (resp *RoomAdminShowEntryResp, err error)
// 获取用户拥有的的所有房管身份
GetByUid(ctx context.Context, req *RoomAdminGetByUidReq) (resp *RoomAdminGetByUidResp, err error)
// 辞职房管
Resign(ctx context.Context, req *RoomAdminResignRoomAdminReq) (resp *RoomAdminResignRoomAdminResp, err error)
// 查询需要添加的房管
SearchForAdmin(ctx context.Context, req *RoomAdminSearchForAdminReq) (resp *RoomAdminSearchForAdminResp, err error)
// 获取主播拥有的的所有房管
GetByAnchor(ctx context.Context, req *RoomAdminGetByAnchorReq) (resp *RoomAdminGetByAnchorResp, err error)
// 获取主播拥有的的所有房管,房间号维度
GetByRoom(ctx context.Context, req *RoomAdminGetByRoomReq) (resp *RoomAdminGetByRoomResp, err error)
// 撤销房管
Dismiss(ctx context.Context, req *RoomAdminDismissAdminReq) (resp *RoomAdminDismissAdminResp, err error)
// 任命房管
Appoint(ctx context.Context, req *RoomAdminAddReq) (resp *RoomAdminAddResp, err error)
// 是否房管
IsAdmin(ctx context.Context, req *RoomAdminIsAdminReq) (resp *RoomAdminIsAdminResp, err error)
// 是否房管, 不额外返回用户信息, 不判断是否主播自己
IsAdminShort(ctx context.Context, req *RoomAdminIsAdminShortReq) (resp *RoomAdminIsAdminShortResp, err error)
}
var v1RoomAdminSvc RoomAdmin
// @params RoomAdminShowEntryReq
// @router GET /xlive/xuser/v1/roomAdmin/is_any
// @response RoomAdminShowEntryResp
func roomAdminIsAny(c *bm.Context) {
p := new(RoomAdminShowEntryReq)
if err := c.BindWith(p, binding.Default(c.Request.Method, c.Request.Header.Get("Content-Type"))); err != nil {
return
}
resp, err := v1RoomAdminSvc.IsAny(c, p)
c.JSON(resp, err)
}
// @params RoomAdminGetByUidReq
// @router GET /xlive/xuser/v1/roomAdmin/get_by_uid
// @response RoomAdminGetByUidResp
func roomAdminGetByUid(c *bm.Context) {
p := new(RoomAdminGetByUidReq)
if err := c.BindWith(p, binding.Default(c.Request.Method, c.Request.Header.Get("Content-Type"))); err != nil {
return
}
resp, err := v1RoomAdminSvc.GetByUid(c, p)
c.JSON(resp, err)
}
// @params RoomAdminResignRoomAdminReq
// @router GET /xlive/xuser/v1/roomAdmin/resign
// @response RoomAdminResignRoomAdminResp
func roomAdminResign(c *bm.Context) {
p := new(RoomAdminResignRoomAdminReq)
if err := c.BindWith(p, binding.Default(c.Request.Method, c.Request.Header.Get("Content-Type"))); err != nil {
return
}
resp, err := v1RoomAdminSvc.Resign(c, p)
c.JSON(resp, err)
}
// @params RoomAdminSearchForAdminReq
// @router GET /xlive/xuser/v1/roomAdmin/search_for_admin
// @response RoomAdminSearchForAdminResp
func roomAdminSearchForAdmin(c *bm.Context) {
p := new(RoomAdminSearchForAdminReq)
if err := c.BindWith(p, binding.Default(c.Request.Method, c.Request.Header.Get("Content-Type"))); err != nil {
return
}
resp, err := v1RoomAdminSvc.SearchForAdmin(c, p)
c.JSON(resp, err)
}
// @params RoomAdminGetByAnchorReq
// @router GET /xlive/xuser/v1/roomAdmin/get_by_anchor
// @response RoomAdminGetByAnchorResp
func roomAdminGetByAnchor(c *bm.Context) {
p := new(RoomAdminGetByAnchorReq)
if err := c.BindWith(p, binding.Default(c.Request.Method, c.Request.Header.Get("Content-Type"))); err != nil {
return
}
resp, err := v1RoomAdminSvc.GetByAnchor(c, p)
c.JSON(resp, err)
}
// @params RoomAdminGetByRoomReq
// @router GET /xlive/xuser/v1/roomAdmin/get_by_room
// @response RoomAdminGetByRoomResp
func roomAdminGetByRoom(c *bm.Context) {
p := new(RoomAdminGetByRoomReq)
if err := c.BindWith(p, binding.Default(c.Request.Method, c.Request.Header.Get("Content-Type"))); err != nil {
return
}
resp, err := v1RoomAdminSvc.GetByRoom(c, p)
c.JSON(resp, err)
}
// @params RoomAdminDismissAdminReq
// @router GET /xlive/xuser/v1/roomAdmin/dismiss
// @response RoomAdminDismissAdminResp
func roomAdminDismiss(c *bm.Context) {
p := new(RoomAdminDismissAdminReq)
if err := c.BindWith(p, binding.Default(c.Request.Method, c.Request.Header.Get("Content-Type"))); err != nil {
return
}
resp, err := v1RoomAdminSvc.Dismiss(c, p)
c.JSON(resp, err)
}
// @params RoomAdminAddReq
// @router GET /xlive/xuser/v1/roomAdmin/appoint
// @response RoomAdminAddResp
func roomAdminAppoint(c *bm.Context) {
p := new(RoomAdminAddReq)
if err := c.BindWith(p, binding.Default(c.Request.Method, c.Request.Header.Get("Content-Type"))); err != nil {
return
}
resp, err := v1RoomAdminSvc.Appoint(c, p)
c.JSON(resp, err)
}
// @params RoomAdminIsAdminReq
// @router GET /xlive/xuser/v1/roomAdmin/is_admin
// @response RoomAdminIsAdminResp
func roomAdminIsAdmin(c *bm.Context) {
p := new(RoomAdminIsAdminReq)
if err := c.BindWith(p, binding.Default(c.Request.Method, c.Request.Header.Get("Content-Type"))); err != nil {
return
}
resp, err := v1RoomAdminSvc.IsAdmin(c, p)
c.JSON(resp, err)
}
// @params RoomAdminIsAdminShortReq
// @router GET /xlive/xuser/v1/roomAdmin/is_admin_short
// @response RoomAdminIsAdminShortResp
func roomAdminIsAdminShort(c *bm.Context) {
p := new(RoomAdminIsAdminShortReq)
if err := c.BindWith(p, binding.Default(c.Request.Method, c.Request.Header.Get("Content-Type"))); err != nil {
return
}
resp, err := v1RoomAdminSvc.IsAdminShort(c, p)
c.JSON(resp, err)
}
// RegisterV1RoomAdminService Register the blademaster route with middleware map
// midMap is the middleware map, the key is defined in proto
func RegisterV1RoomAdminService(e *bm.Engine, svc RoomAdmin, midMap map[string]bm.HandlerFunc) {
v1RoomAdminSvc = svc
e.GET("/xlive/xuser/v1/roomAdmin/is_any", roomAdminIsAny)
e.GET("/xlive/xuser/v1/roomAdmin/get_by_uid", roomAdminGetByUid)
e.GET("/xlive/xuser/v1/roomAdmin/resign", roomAdminResign)
e.GET("/xlive/xuser/v1/roomAdmin/search_for_admin", roomAdminSearchForAdmin)
e.GET("/xlive/xuser/v1/roomAdmin/get_by_anchor", roomAdminGetByAnchor)
e.GET("/xlive/xuser/v1/roomAdmin/get_by_room", roomAdminGetByRoom)
e.GET("/xlive/xuser/v1/roomAdmin/dismiss", roomAdminDismiss)
e.GET("/xlive/xuser/v1/roomAdmin/appoint", roomAdminAppoint)
e.GET("/xlive/xuser/v1/roomAdmin/is_admin", roomAdminIsAdmin)
e.GET("/xlive/xuser/v1/roomAdmin/is_admin_short", roomAdminIsAdminShort)
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,250 @@
syntax = "proto3";
package live.xuser.v1;
option go_package = "v1";
import "github.com/gogo/protobuf/gogoproto/gogo.proto";
// History 相关服务
service RoomAdmin {
// 根据登录态获取功能入口是否显示, 需要登录态
rpc is_any (RoomAdminShowEntryReq) returns (RoomAdminShowEntryResp);
// 获取用户拥有的的所有房管身份
rpc get_by_uid (RoomAdminGetByUidReq) returns (RoomAdminGetByUidResp);
// 辞职房管
rpc resign (RoomAdminResignRoomAdminReq) returns (RoomAdminResignRoomAdminResp);
// 查询需要添加的房管
rpc search_for_admin (RoomAdminSearchForAdminReq) returns (RoomAdminSearchForAdminResp);
// 获取主播拥有的的所有房管
rpc get_by_anchor (RoomAdminGetByAnchorReq) returns (RoomAdminGetByAnchorResp);
// 获取主播拥有的的所有房管,房间号维度
rpc get_by_room (RoomAdminGetByRoomReq) returns (RoomAdminGetByRoomResp);
// 撤销房管
rpc dismiss (RoomAdminDismissAdminReq) returns (RoomAdminDismissAdminResp);
// 任命房管
rpc appoint (RoomAdminAddReq) returns (RoomAdminAddResp);
// 是否房管
rpc is_admin (RoomAdminIsAdminReq) returns (RoomAdminIsAdminResp);
// 是否房管, 不额外返回用户信息, 不判断是否主播自己
rpc is_admin_short (RoomAdminIsAdminShortReq) returns (RoomAdminIsAdminShortResp);
}
// 历史记录请求参数定义
message RoomAdminShowEntryReq {
int64 uid = 1 [(gogoproto.moretags) = 'form:"uid" validate:"required"'];
}
// 获取接口返回响应
message RoomAdminShowEntryResp {
// 是否有房管
int64 has_admin = 1;
}
message RoomAdminSearchForAdminReq {
int64 uid = 1 [(gogoproto.moretags) = 'form:"uid" validate:"required"'];
string key_word = 2 [(gogoproto.moretags) = 'form:"key_word" validate:"required"'];
}
message RoomAdminSearchForAdminResp {
repeated Data data = 3 [(gogoproto.jsontag) = "data"];
message Data {
// 用户id
int64 uid = 1 [(gogoproto.jsontag) = "uid"];
// 是否房管
int64 is_admin = 2 [(gogoproto.jsontag) = "is_admin"];
// 用户名
string uname = 4 [(gogoproto.jsontag) = "uname"];
// 用户头像
string face = 5 [(gogoproto.jsontag) = "face"];
// 粉丝勋章名称
string medal_name = 6 [(gogoproto.jsontag) = "medal_name"];
// 粉丝勋章等级
int64 level = 7 [(gogoproto.jsontag) = "level"];
}
}
message RoomAdminResignRoomAdminReq {
// 房间号
int64 roomid = 1 [(gogoproto.moretags) = 'form:"roomid" validate:"required"'];
// 用户uid
int64 uid = 2 [(gogoproto.moretags) = 'form:"uid" validate:"required"'];
}
message RoomAdminResignRoomAdminResp {
}
message RoomAdminGetByUidReq {
// 用户uid
int64 uid = 2 [(gogoproto.moretags) = 'form:"uid" validate:"required"'];
// 页数
int64 page = 1 [(gogoproto.moretags) = 'form:"page"'];
}
message RoomAdminGetByUidResp {
//
Page page = 1;
//
repeated Data data = 3 [(gogoproto.jsontag) = "data"];
message Data {
// 用户id
int64 uid = 1 [(gogoproto.jsontag) = "uid"];
// 房间号
int64 roomid = 2 [(gogoproto.jsontag) = "roomid"];
// 主播的用户id
int64 anchor_id = 3 [(gogoproto.jsontag) = "anchor_id"];
// 主播用户名
string uname = 4 [(gogoproto.jsontag) = "uname"];
// 主播封面
string anchor_cover = 5 [(gogoproto.jsontag) = "anchor_cover"];
// 上任时间
string ctime = 6 [(gogoproto.jsontag) = "ctime"];
}
message Page {
// 当前页码
int64 page = 1;
// 每页大小
int64 page_size = 2;
// 总页数
int64 total_page = 3;
// 总记录数
int64 total_count = 4;
}
}
message RoomAdminGetByAnchorReq {
// 页数
int64 page = 1 [(gogoproto.moretags) = 'form:"page"'];
// 用户uid
int64 uid = 2 [(gogoproto.moretags) = 'form:"uid" validate:"required"'];
}
message RoomAdminGetByRoomReq {
// 房间号
int64 roomid = 1 [(gogoproto.moretags) = 'form:"roomid" validate:"required"'];
}
message RoomAdminGetByAnchorResp {
//
Page page = 1;
//
repeated Data data = 3 [(gogoproto.jsontag) = "data"];
message Data {
// 用户id
int64 uid = 1 [(gogoproto.jsontag) = "uid"];
// 用户名
string uname = 4 [(gogoproto.jsontag) = "uname"];
// 用户头像
string face = 5 [(gogoproto.jsontag) = "face"];
// 上任时间
string ctime = 6 [(gogoproto.jsontag) = "ctime"];
// 粉丝勋章名称
string medal_name = 8 [(gogoproto.jsontag) = "medal_name"];
// 粉丝勋章等级
int64 level = 9 [(gogoproto.jsontag) = "level"];
// 房间号
int64 roomid = 10 [(gogoproto.jsontag) = "roomid"];
}
message Page {
// 当前页码
int64 page = 1;
// 每页大小
int64 page_size = 2;
// 总页数
int64 total_page = 3;
// 总记录数
int64 total_count = 4;
}
}
message RoomAdminDismissAdminReq {
// 房管的用户uid
int64 uid = 1 [(gogoproto.moretags) = 'form:"uid" validate:"required"'];
// 主播uid
int64 anchor_id = 2 [(gogoproto.moretags) = 'form:"anchor_id" validate:"required"'];
}
message RoomAdminDismissAdminResp {
}
message RoomAdminAddReq {
// 房管的uid
int64 uid = 1 [(gogoproto.moretags) = 'form:"uid" validate:"required"'];
// 主播uid
int64 anchor_id = 2 [(gogoproto.moretags) = 'form:"anchor_id" validate:"required"'];
}
message RoomAdminAddResp {
message UI {
// 用户id
int64 uid = 1 [(gogoproto.jsontag) = "uid"];
// 用户名
string uname = 2 [(gogoproto.jsontag) = "uname"];
}
// banner
UI userinfo = 1 [(gogoproto.jsontag) = "userinfo"];
// 房管的用户id
int64 uid = 2 [(gogoproto.jsontag) = "uid"];
// 房间号
int64 roomid = 3 [(gogoproto.jsontag) = "roomid"];
}
message RoomAdminIsAdminShortReq {
// 房管的uid
int64 uid = 1 [(gogoproto.moretags) = 'form:"uid" validate:"required"'];
// 房间号
int64 roomid = 3 [(gogoproto.moretags) = 'form:"roomid" validate:"required"'];
}
message RoomAdminIsAdminReq {
// 房管的uid
int64 uid = 1 [(gogoproto.moretags) = 'form:"uid" validate:"required"'];
// 主播uid
int64 anchor_id = 2 [(gogoproto.moretags) = 'form:"anchor_id" validate:"required"'];
// 房间号
int64 roomid = 3 [(gogoproto.moretags) = 'form:"roomid" validate:"required"'];
}
message RoomAdminIsAdminShortResp {
// 是否房管 0:不是,1:是
int64 result = 1 [(gogoproto.jsontag) = "result"];
}
message RoomAdminIsAdminResp {
message UI {
// 用户id
int64 uid = 1 [(gogoproto.jsontag) = "uid"];
// 用户名
string uname = 2 [(gogoproto.jsontag) = "uname"];
}
// banner
UI userinfo = 1 [(gogoproto.jsontag) = "userinfo"];
// 房管的用户id
int64 uid = 2 [(gogoproto.jsontag) = "uid"];
// 房间号
int64 roomid = 3 [(gogoproto.jsontag) = "roomid"];
}
message RoomAdminGetByRoomResp {
repeated Data data = 1 [(gogoproto.jsontag) = "data"];
message Data {
// 上任时间
string ctime = 1 [(gogoproto.jsontag) = "ctime"];
// 房管的用户id
int64 uid = 2 [(gogoproto.jsontag) = "uid"];
// 房间号
int64 roomid = 3 [(gogoproto.jsontag) = "roomid"];
}
}

View File

@@ -0,0 +1,305 @@
## 根据登录态获取功能入口是否显示, 需要登录态
`GET http://api.live.bilibili.com/xlive/xuser/v1/roomAdmin/is_any`
### 请求参数
|参数名|必选|类型|描述|
|:---|:---|:---|:---|
|uid|是|integer||
```json
{
"code": 0,
"message": "ok",
"data": {
// 是否有房管
"has_admin": 0
}
}
```
## 获取用户拥有的的所有房管身份
`GET http://api.live.bilibili.com/xlive/xuser/v1/roomAdmin/get_by_uid`
### 请求参数
|参数名|必选|类型|描述|
|:---|:---|:---|:---|
|uid|是|integer| 用户uid|
|page|否|integer| 页数|
```json
{
"code": 0,
"message": "ok",
"data": {
"page": {
// 当前页码
"page": 0,
// 每页大小
"page_size": 0,
// 总页数
"total_page": 0,
// 总记录数
"total_count": 0
},
"data": [
{
// 用户id
"uid": 0,
// 房间号
"roomid": 0,
// 主播的用户id
"anchor_id": 0,
// 主播用户名
"uname": "",
// 主播封面
"anchor_cover": "",
// 上任时间
"ctime": ""
}
]
}
}
```
## 辞职房管
`GET http://api.live.bilibili.com/xlive/xuser/v1/roomAdmin/resign`
### 请求参数
|参数名|必选|类型|描述|
|:---|:---|:---|:---|
|roomid|是|integer| 房间号|
|uid|是|integer| 用户uid|
```json
{
"code": 0,
"message": "ok",
"data": {
}
}
```
## 查询需要添加的房管
`GET http://api.live.bilibili.com/xlive/xuser/v1/roomAdmin/search_for_admin`
### 请求参数
|参数名|必选|类型|描述|
|:---|:---|:---|:---|
|uid|是|integer||
|key_word|是|string||
```json
{
"code": 0,
"message": "ok",
"data": {
"data": [
{
// 用户id
"uid": 0,
// 是否房管
"is_admin": 0,
// 用户名
"uname": "",
// 用户头像
"face": "",
// 粉丝勋章名称
"medal_name": "",
// 粉丝勋章等级
"level": 0
}
]
}
}
```
## 获取主播拥有的的所有房管
`GET http://api.live.bilibili.com/xlive/xuser/v1/roomAdmin/get_by_anchor`
### 请求参数
|参数名|必选|类型|描述|
|:---|:---|:---|:---|
|page|否|integer| 页数|
|uid|是|integer| 用户uid|
```json
{
"code": 0,
"message": "ok",
"data": {
"page": {
// 当前页码
"page": 0,
// 每页大小
"page_size": 0,
// 总页数
"total_page": 0,
// 总记录数
"total_count": 0
},
"data": [
{
// 用户id
"uid": 0,
// 用户名
"uname": "",
// 用户头像
"face": "",
// 上任时间
"ctime": "",
// 粉丝勋章名称
"medal_name": "",
// 粉丝勋章等级
"level": 0,
// 房间号
"roomid": 0
}
]
}
}
```
## 获取主播拥有的的所有房管,房间号维度
`GET http://api.live.bilibili.com/xlive/xuser/v1/roomAdmin/get_by_room`
### 请求参数
|参数名|必选|类型|描述|
|:---|:---|:---|:---|
|roomid|是|integer| 房间号|
```json
{
"code": 0,
"message": "ok",
"data": {
"data": [
{
// 上任时间
"ctime": "",
// 房管的用户id
"uid": 0,
// 房间号
"roomid": 0
}
]
}
}
```
## 撤销房管
`GET http://api.live.bilibili.com/xlive/xuser/v1/roomAdmin/dismiss`
### 请求参数
|参数名|必选|类型|描述|
|:---|:---|:---|:---|
|uid|是|integer| 房管的用户uid|
|anchor_id|是|integer| 主播uid|
```json
{
"code": 0,
"message": "ok",
"data": {
}
}
```
## 任命房管
`GET http://api.live.bilibili.com/xlive/xuser/v1/roomAdmin/appoint`
### 请求参数
|参数名|必选|类型|描述|
|:---|:---|:---|:---|
|uid|是|integer| 房管的uid|
|anchor_id|是|integer| 主播uid|
```json
{
"code": 0,
"message": "ok",
"data": {
// banner
"userinfo": {
// 用户id
"uid": 0,
// 用户名
"uname": ""
},
// 房管的用户id
"uid": 0,
// 房间号
"roomid": 0
}
}
```
## 是否房管
`GET http://api.live.bilibili.com/xlive/xuser/v1/roomAdmin/is_admin`
### 请求参数
|参数名|必选|类型|描述|
|:---|:---|:---|:---|
|uid|是|integer| 房管的uid|
|anchor_id|是|integer| 主播uid|
|roomid|是|integer| 房间号|
```json
{
"code": 0,
"message": "ok",
"data": {
// banner
"userinfo": {
// 用户id
"uid": 0,
// 用户名
"uname": ""
},
// 房管的用户id
"uid": 0,
// 房间号
"roomid": 0
}
}
```
## 是否房管, 不额外返回用户信息, 不判断是否主播自己
`GET http://api.live.bilibili.com/xlive/xuser/v1/roomAdmin/is_admin_short`
### 请求参数
|参数名|必选|类型|描述|
|:---|:---|:---|:---|
|uid|是|integer| 房管的uid|
|roomid|是|integer| 房间号|
```json
{
"code": 0,
"message": "ok",
"data": {
// 是否房管 0:不是,1:是
"result": 0
}
}
```

View File

@@ -0,0 +1,45 @@
package v1
import (
"context"
"go-common/library/net/rpc/warden"
"google.golang.org/grpc"
)
// AppID unique app id for service discovery
const AppID = "live.xuser"
// Client client
type Client struct {
UserExpClient
VipClient
GuardClient
}
// NewXuserRoomAdminClient new member grpc client
func NewXuserRoomAdminClient(cfg *warden.ClientConfig, opts ...grpc.DialOption) (RoomAdminClient, error) {
client := warden.NewClient(cfg, opts...)
conn, err := client.Dial(context.Background(), "discovery://default/"+AppID)
//conn, err := client.Dial(context.Background(), "127.0.0.1:9000")
if err != nil {
return nil, err
}
return NewRoomAdminClient(conn), nil
}
// NewClient new resource gRPC client
func NewClient(cfg *warden.ClientConfig, opts ...grpc.DialOption) (*Client, error) {
client := warden.NewClient(cfg, opts...)
conn, err := client.Dial(context.Background(), "discovery://default/"+AppID)
if err != nil {
return nil, err
}
cli := &Client{}
cli.UserExpClient = NewUserExpClient(conn)
cli.GuardClient = NewGuardClient(conn)
cli.VipClient = NewVipClient(conn)
return cli, nil
}

View File

@@ -0,0 +1,70 @@
// Code generated by protoc-gen-bm v0.1, DO NOT EDIT.
// source: api/grpc/v1/exp.proto
/*
Package v1 is a generated blademaster stub package.
This code was generated with go-common/app/tool/bmgen/protoc-gen-bm v0.1.
It is generated from these files:
api/grpc/v1/exp.proto
*/
package v1
import (
"context"
bm "go-common/library/net/http/blademaster"
"go-common/library/net/http/blademaster/binding"
)
// to suppressed 'imported but not used warning'
var _ *bm.Context
var _ context.Context
var _ binding.StructValidator
// =================
// UserExp Interface
// =================
// UserExp 相关服务
type UserExp interface {
// GetUserExpMulti 获取用户经验与等级信息,支持批量
GetUserExp(ctx context.Context, req *GetUserExpReq) (resp *GetUserExpResp, err error)
// AddUserExp 增加用户经验,不支持批量
AddUserExp(ctx context.Context, req *AddUserExpReq) (resp *AddUserExpResp, err error)
}
var v1UserExpSvc UserExp
// @params GetUserExpReq
// @router GET /xlive/xuser/v1/userExp/GetUserExp
// @response GetUserExpResp
func userExpGetUserExp(c *bm.Context) {
p := new(GetUserExpReq)
if err := c.BindWith(p, binding.Default(c.Request.Method, c.Request.Header.Get("Content-Type"))); err != nil {
return
}
resp, err := v1UserExpSvc.GetUserExp(c, p)
c.JSON(resp, err)
}
// @params AddUserExpReq
// @router GET /xlive/xuser/v1/userExp/AddUserExp
// @response AddUserExpResp
func userExpAddUserExp(c *bm.Context) {
p := new(AddUserExpReq)
if err := c.BindWith(p, binding.Default(c.Request.Method, c.Request.Header.Get("Content-Type"))); err != nil {
return
}
resp, err := v1UserExpSvc.AddUserExp(c, p)
c.JSON(resp, err)
}
// RegisterV1UserExpService Register the blademaster route with middleware map
// midMap is the middleware map, the key is defined in proto
func RegisterV1UserExpService(e *bm.Engine, svc UserExp, midMap map[string]bm.HandlerFunc) {
v1UserExpSvc = svc
e.GET("/xlive/xuser/v1/userExp/GetUserExp", userExpGetUserExp)
e.GET("/xlive/xuser/v1/userExp/AddUserExp", userExpAddUserExp)
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,92 @@
syntax = "proto3";
package live.xuser.v1;
option go_package = "v1";
import "github.com/gogo/protobuf/gogoproto/gogo.proto";
// UserExp 相关服务
service UserExp {
// GetUserExpMulti 获取用户经验与等级信息,支持批量
rpc GetUserExp(GetUserExpReq) returns (GetUserExpResp);
// AddUserExp 增加用户经验,不支持批量
rpc AddUserExp(AddUserExpReq) returns (AddUserExpResp);
}
message AddUserExpReq {
UserExpChunk userInfo = 1 [(gogoproto.moretags) = 'form:"userInfo" validate:"required"'];
}
message UserExpChunk {
int64 uid = 1 [(gogoproto.moretags) = 'form:"uid" validate:"gt=0,required"'];
int64 req_biz = 2 [(gogoproto.moretags) = 'form:"req_biz" validate:"required"'];
int64 type = 3 [(gogoproto.moretags) = 'form:"type" validate:"gt=0,required"'];
int64 num = 4 [(gogoproto.moretags) = 'form:"num" validate:"gt=0,required"'];
}
message AddUserExpResp{
}
// GetUserExpReq 请求
message GetUserExpReq {
repeated int64 uids = 1 [(gogoproto.moretags) = 'form:"uids" validate:"gt=0,required"'];
}
// GetUserExpResp 响应
message GetUserExpResp {
map<int64, LevelInfo> data = 1 [(gogoproto.jsontag) = "data"];
}
message LevelInfo {
int64 uid = 1 [(gogoproto.jsontag) = "uid"];
UserLevelInfo userLevel = 2 [(gogoproto.jsontag) = "userLevel"];
AnchorLevelInfo anchorLevel = 3 [(gogoproto.jsontag) = "anchorLevel"];
}
message UserLevelInfo {
// 当前用户等级
int64 level = 1 [(gogoproto.jsontag) = "level"];
// 下一等级
int64 nextLevel = 2 [(gogoproto.jsontag) = "nextLevel"];
// 当前等级对应的经验
int64 userExpLeft = 3 [(gogoproto.jsontag) = "userExpLeft"];
// 下一等级对应的经验
int64 userExpRight = 4 [(gogoproto.jsontag) = "userExpRight"];
// 用户当前经验
int64 userExp = 5 [(gogoproto.jsontag) = "userExp"];
// 升级到下一等级对应的经验
int64 userExpNextLevel = 6 [(gogoproto.jsontag) = "userExpNextLevel"];
// 当前等级颜色
int64 color = 7 [(gogoproto.jsontag) = "color"];
// 下一等级左侧对应的经验
int64 userExpNextLeft = 8 [(gogoproto.jsontag) = "userExpNextLeft"];
// 下一等级右侧对应的经验
int64 userExpNextRight = 9 [(gogoproto.jsontag) = "userExpNextRight"];
int64 isLevelTop = 10 [(gogoproto.jsontag) = "isLevelTop"];
}
message AnchorLevelInfo {
// 当前用户等级
int64 level = 1 [(gogoproto.jsontag) = "level"];
// 下一等级
int64 nextLevel = 2 [(gogoproto.jsontag) = "nextLevel"];
// 当前等级对应的经验
int64 userExpLeft = 3 [(gogoproto.jsontag) = "userExpLeft"];
// 下一等级对应的经验
int64 userExpRight = 4 [(gogoproto.jsontag) = "userExpRight"];
// 用户当前经验
int64 userExp = 5 [(gogoproto.jsontag) = "userExp"];
// 升级到下一等级对应的经验
int64 userExpNextLevel = 6 [(gogoproto.jsontag) = "userExpNextLevel"];
// 当前等级颜色
int64 color = 7 [(gogoproto.jsontag) = "color"];
// 下一等级左侧对应的经验
int64 userExpNextLeft = 8 [(gogoproto.jsontag) = "userExpNextLeft"];
// 下一等级右侧对应的经验
int64 userExpNextRight = 9 [(gogoproto.jsontag) = "userExpNextRight"];
// 主播积分,userExp/100
int64 anchorScore = 10 [(gogoproto.jsontag) = "anchorScore"];
int64 isLevelTop = 11 [(gogoproto.jsontag) = "isLevelTop"];
}

View File

@@ -0,0 +1,94 @@
## GetUserExpMulti 获取用户经验与等级信息,支持批量
`GET http://api.live.bilibili.com/xlive/xuser/v1/userExp/GetUserExp`
### 请求参数
|参数名|必选|类型|描述|
|:---|:---|:---|:---|
|uids|是|多个integer||
```json
{
"code": 0,
"message": "ok",
"data": {
"data": {
"1": {
"uid": 0,
"userLevel": {
// 当前用户等级
"level": 0,
// 下一等级
"nextLevel": 0,
// 当前等级对应的经验
"userExpLeft": 0,
// 下一等级对应的经验
"userExpRight": 0,
// 用户当前经验
"userExp": 0,
// 升级到下一等级对应的经验
"userExpNextLevel": 0,
// 当前等级颜色
"color": 0,
// 下一等级左侧对应的经验
"userExpNextLeft": 0,
// 下一等级右侧对应的经验
"userExpNextRight": 0,
"isLevelTop": 0
},
"anchorLevel": {
// 当前用户等级
"level": 0,
// 下一等级
"nextLevel": 0,
// 当前等级对应的经验
"userExpLeft": 0,
// 下一等级对应的经验
"userExpRight": 0,
// 用户当前经验
"userExp": 0,
// 升级到下一等级对应的经验
"userExpNextLevel": 0,
// 当前等级颜色
"color": 0,
// 下一等级左侧对应的经验
"userExpNextLeft": 0,
// 下一等级右侧对应的经验
"userExpNextRight": 0,
// 主播积分,userExp/100
"anchorScore": 0,
"isLevelTop": 0
}
}
}
}
}
```
## AddUserExp 增加用户经验,不支持批量
`GET http://api.live.bilibili.com/xlive/xuser/v1/userExp/AddUserExp`
### 请求参数
```json
{
"userInfo": {
"uid": 0,
"req_biz": 0,
"type": 0,
"num": 0
}
}
```
```json
{
"code": 0,
"message": "ok",
"data": {
}
}
```

View File

@@ -0,0 +1,177 @@
// Code generated by protoc-gen-bm v0.1, DO NOT EDIT.
// source: guard.proto
/*
Package v1 is a generated blademaster stub package.
This code was generated with go-common/app/tool/bmgen/protoc-gen-bm v0.1.
It is generated from these files:
guard.proto
*/
package v1
import (
"context"
bm "go-common/library/net/http/blademaster"
"go-common/library/net/http/blademaster/binding"
)
// to suppressed 'imported but not used warning'
var _ *bm.Context
var _ context.Context
var _ binding.StructValidator
var PathGuardBuy = "/live.xuser.v1.Guard/Buy"
var PathGuardGetByUIDTargetID = "/live.xuser.v1.Guard/GetByUIDTargetID"
var PathGuardGetByTargetIdsBatch = "/live.xuser.v1.Guard/GetByTargetIdsBatch"
var PathGuardGetByUIDTargetIds = "/live.xuser.v1.Guard/GetByUIDTargetIds"
var PathGuardGetByUIDForGift = "/live.xuser.v1.Guard/GetByUIDForGift"
var PathGuardGetByUIDBatch = "/live.xuser.v1.Guard/GetByUIDBatch"
var PathGuardGetAnchorRecentTopGuard = "/live.xuser.v1.Guard/GetAnchorRecentTopGuard"
var PathGuardGetTopListGuard = "/live.xuser.v1.Guard/GetTopListGuard"
var PathGuardGetTopListGuardNum = "/live.xuser.v1.Guard/GetTopListGuardNum"
var PathGuardClearUIDCache = "/live.xuser.v1.Guard/ClearUIDCache"
// ===============
// Guard Interface
// ===============
type GuardBMServer interface {
// Buy 购买大航海
Buy(ctx context.Context, req *GuardBuyReq) (resp *GuardBuyReply, err error)
// GetByUIDTargetID 获取我与目标用户守护关系,不支持批量(P0级)
GetByUIDTargetID(ctx context.Context, req *GetByUidTargetIdReq) (resp *GetByUidTargetIdResp, err error)
// GetByTargetIdsBatch 获取我与目标用户守护关系,支持批量(P2级,必要时刻降级)
GetByTargetIdsBatch(ctx context.Context, req *GetByTargetIdsReq) (resp *GetByTargetIdsResp, err error)
// GetByUIDTargetIds 根据uids批量获取所有守护关系,粉丝勋章使用
GetByUIDTargetIds(ctx context.Context, req *GetByUidTargetIdsReq) (resp *GetByUidTargetIdsResp, err error)
// GetByUID 获取我所有的守护,不支持批量(P0级)
GetByUIDForGift(ctx context.Context, req *GetByUidReq) (resp *GetByUidResp, err error)
// GetByUIDBatch 根据uids获取所有的守护,支持批量(P2级)
GetByUIDBatch(ctx context.Context, req *GetByUidBatchReq) (resp *GetByUidBatchResp, err error)
// GetAnchorRecentTopGuard 获取最近的提督弹窗提醒
GetAnchorRecentTopGuard(ctx context.Context, req *GetAnchorRecentTopGuardReq) (resp *GetAnchorRecentTopGuardResp, err error)
// GetTopListGuard 获取某个up主的守护排行榜
GetTopListGuard(ctx context.Context, req *GetTopListGuardReq) (resp *GetTopListGuardResp, err error)
// GetTopListGuardNum 获取某个up主所有的守护数量,和GetTopListGuard接口的区别是此接口用于房间页首屏,逻辑比较简单,因此拆分开来
GetTopListGuardNum(ctx context.Context, req *GetTopListGuardNumReq) (resp *GetTopListGuardNumResp, err error)
// ClearUIDCache 清除cache
ClearUIDCache(ctx context.Context, req *ClearUIDCacheReq) (resp *ClearUIDCacheResp, err error)
}
var v1GuardSvc GuardBMServer
func guardBuy(c *bm.Context) {
p := new(GuardBuyReq)
if err := c.BindWith(p, binding.Default(c.Request.Method, c.Request.Header.Get("Content-Type"))); err != nil {
return
}
resp, err := v1GuardSvc.Buy(c, p)
c.JSON(resp, err)
}
func guardGetByUIDTargetID(c *bm.Context) {
p := new(GetByUidTargetIdReq)
if err := c.BindWith(p, binding.Default(c.Request.Method, c.Request.Header.Get("Content-Type"))); err != nil {
return
}
resp, err := v1GuardSvc.GetByUIDTargetID(c, p)
c.JSON(resp, err)
}
func guardGetByTargetIdsBatch(c *bm.Context) {
p := new(GetByTargetIdsReq)
if err := c.BindWith(p, binding.Default(c.Request.Method, c.Request.Header.Get("Content-Type"))); err != nil {
return
}
resp, err := v1GuardSvc.GetByTargetIdsBatch(c, p)
c.JSON(resp, err)
}
func guardGetByUIDTargetIds(c *bm.Context) {
p := new(GetByUidTargetIdsReq)
if err := c.BindWith(p, binding.Default(c.Request.Method, c.Request.Header.Get("Content-Type"))); err != nil {
return
}
resp, err := v1GuardSvc.GetByUIDTargetIds(c, p)
c.JSON(resp, err)
}
func guardGetByUIDForGift(c *bm.Context) {
p := new(GetByUidReq)
if err := c.BindWith(p, binding.Default(c.Request.Method, c.Request.Header.Get("Content-Type"))); err != nil {
return
}
resp, err := v1GuardSvc.GetByUIDForGift(c, p)
c.JSON(resp, err)
}
func guardGetByUIDBatch(c *bm.Context) {
p := new(GetByUidBatchReq)
if err := c.BindWith(p, binding.Default(c.Request.Method, c.Request.Header.Get("Content-Type"))); err != nil {
return
}
resp, err := v1GuardSvc.GetByUIDBatch(c, p)
c.JSON(resp, err)
}
func guardGetAnchorRecentTopGuard(c *bm.Context) {
p := new(GetAnchorRecentTopGuardReq)
if err := c.BindWith(p, binding.Default(c.Request.Method, c.Request.Header.Get("Content-Type"))); err != nil {
return
}
resp, err := v1GuardSvc.GetAnchorRecentTopGuard(c, p)
c.JSON(resp, err)
}
func guardGetTopListGuard(c *bm.Context) {
p := new(GetTopListGuardReq)
if err := c.BindWith(p, binding.Default(c.Request.Method, c.Request.Header.Get("Content-Type"))); err != nil {
return
}
resp, err := v1GuardSvc.GetTopListGuard(c, p)
c.JSON(resp, err)
}
func guardGetTopListGuardNum(c *bm.Context) {
p := new(GetTopListGuardNumReq)
if err := c.BindWith(p, binding.Default(c.Request.Method, c.Request.Header.Get("Content-Type"))); err != nil {
return
}
resp, err := v1GuardSvc.GetTopListGuardNum(c, p)
c.JSON(resp, err)
}
func guardClearUIDCache(c *bm.Context) {
p := new(ClearUIDCacheReq)
if err := c.BindWith(p, binding.Default(c.Request.Method, c.Request.Header.Get("Content-Type"))); err != nil {
return
}
resp, err := v1GuardSvc.ClearUIDCache(c, p)
c.JSON(resp, err)
}
// RegisterGuardBMServer Register the blademaster route
func RegisterGuardBMServer(e *bm.Engine, server GuardBMServer) {
v1GuardSvc = server
e.GET("/live.xuser.v1.Guard/Buy", guardBuy)
e.GET("/live.xuser.v1.Guard/GetByUIDTargetID", guardGetByUIDTargetID)
e.GET("/live.xuser.v1.Guard/GetByTargetIdsBatch", guardGetByTargetIdsBatch)
e.GET("/live.xuser.v1.Guard/GetByUIDTargetIds", guardGetByUIDTargetIds)
e.GET("/live.xuser.v1.Guard/GetByUIDForGift", guardGetByUIDForGift)
e.GET("/live.xuser.v1.Guard/GetByUIDBatch", guardGetByUIDBatch)
e.GET("/live.xuser.v1.Guard/GetAnchorRecentTopGuard", guardGetAnchorRecentTopGuard)
e.GET("/live.xuser.v1.Guard/GetTopListGuard", guardGetTopListGuard)
e.GET("/live.xuser.v1.Guard/GetTopListGuardNum", guardGetTopListGuardNum)
e.GET("/live.xuser.v1.Guard/ClearUIDCache", guardClearUIDCache)
}

View File

@@ -0,0 +1,382 @@
<!-- package=live.xuser.v1 -->
- [/live.xuser.v1.Guard/Buy](#live.xuser.v1.GuardBuy) Buy 购买大航海
- [/live.xuser.v1.Guard/GetByUIDTargetID](#live.xuser.v1.GuardGetByUIDTargetID) GetByUIDTargetID 获取我与目标用户守护关系,不支持批量(P0级)
- [/live.xuser.v1.Guard/GetByTargetIdsBatch](#live.xuser.v1.GuardGetByTargetIdsBatch) GetByTargetIdsBatch 获取我与目标用户守护关系,支持批量(P2级,必要时刻降级)
- [/live.xuser.v1.Guard/GetByUIDTargetIds](#live.xuser.v1.GuardGetByUIDTargetIds) GetByUIDTargetIds 根据uids批量获取所有守护关系,粉丝勋章使用
- [/live.xuser.v1.Guard/GetByUIDForGift](#live.xuser.v1.GuardGetByUIDForGift) GetByUID 获取我所有的守护,不支持批量(P0级)
- [/live.xuser.v1.Guard/GetByUIDBatch](#live.xuser.v1.GuardGetByUIDBatch) GetByUIDBatch 根据uids获取所有的守护,支持批量(P2级)
- [/live.xuser.v1.Guard/GetAnchorRecentTopGuard](#live.xuser.v1.GuardGetAnchorRecentTopGuard) GetAnchorRecentTopGuard 获取最近的提督弹窗提醒
- [/live.xuser.v1.Guard/GetTopListGuard](#live.xuser.v1.GuardGetTopListGuard) GetTopListGuard 获取某个up主的守护排行榜
- [/live.xuser.v1.Guard/GetTopListGuardNum](#live.xuser.v1.GuardGetTopListGuardNum) GetTopListGuardNum 获取某个up主所有的守护数量,和GetTopListGuard接口的区别是此接口用于房间页首屏,逻辑比较简单,因此拆分开来
- [/live.xuser.v1.Guard/ClearUIDCache](#live.xuser.v1.GuardClearUIDCache) ClearUIDCache 清除cache
## /live.xuser.v1.Guard/Buy
### Buy 购买大航海
#### 方法GET
#### 请求参数
|参数名|必选|类型|描述|
|:---|:---|:---|:---|
|order_id|是|string||
|uid|是|integer||
|ruid|是|integer||
|guard_level|是|integer||
|num|是|integer||
|platform|是|integer||
|source|是|string||
#### 响应
```javascript
{
"code": 0,
"message": "ok",
"data": {
"status": 0
}
}
```
## /live.xuser.v1.Guard/GetByUIDTargetID
### GetByUIDTargetID 获取我与目标用户守护关系,不支持批量(P0级)
#### 方法GET
#### 请求参数
|参数名|必选|类型|描述|
|:---|:---|:---|:---|
|uid|是|integer||
|target_id|是|integer||
|sort_type|否|integer||
#### 响应
```javascript
{
"code": 0,
"message": "ok",
"data": {
"data": {
"1": {
// 主键
"id": 0,
// uid
"uid": 0,
// target_id
"target_id": 0,
// 守护类型 1为总督2为提督3为舰长
"privilege_type": 0,
// start_time
"start_time": "",
// expired_time
"expired_time": "",
// ctime
"ctime": "",
// utime
"utime": ""
}
}
}
}
```
## /live.xuser.v1.Guard/GetByTargetIdsBatch
### GetByTargetIdsBatch 获取我与目标用户守护关系,支持批量(P2级,必要时刻降级)
#### 方法GET
#### 请求参数
```javascript
{
"targetIDs": [
{
"target_id": 0,
"sort_type": 0
}
]
}
```
#### 响应
```javascript
{
"code": 0,
"message": "ok",
"data": {
}
}
```
## /live.xuser.v1.Guard/GetByUIDTargetIds
### GetByUIDTargetIds 根据uids批量获取所有守护关系,粉丝勋章使用
#### 方法GET
#### 请求参数
```javascript
{
"uid": 0,
"targetIDs": [
{
"target_id": 0,
"sort_type": 0
}
]
}
```
#### 响应
```javascript
{
"code": 0,
"message": "ok",
"data": {
"data": {
"1": {
// 主键
"id": 0,
// uid
"uid": 0,
// target_id
"target_id": 0,
// 守护类型 1为总督2为提督3为舰长
"privilege_type": 0,
// start_time
"start_time": "",
// expired_time
"expired_time": "",
// ctime
"ctime": "",
// utime
"utime": ""
}
}
}
}
```
## /live.xuser.v1.Guard/GetByUIDForGift
### GetByUID 获取我所有的守护,不支持批量(P0级)
#### 方法GET
#### 请求参数
|参数名|必选|类型|描述|
|:---|:---|:---|:---|
|uid|是|integer||
#### 响应
```javascript
{
"code": 0,
"message": "ok",
"data": {
"data": {
"1": {
// 主键
"id": 0,
// uid
"uid": 0,
// target_id
"target_id": 0,
// 守护类型 1为总督2为提督3为舰长
"privilege_type": 0,
// start_time
"start_time": "",
// expired_time
"expired_time": "",
// ctime
"ctime": "",
// utime
"utime": ""
}
}
}
}
```
## /live.xuser.v1.Guard/GetByUIDBatch
### GetByUIDBatch 根据uids获取所有的守护,支持批量(P2级)
#### 方法GET
#### 请求参数
|参数名|必选|类型|描述|
|:---|:---|:---|:---|
|uids|是|多个integer||
#### 响应
```javascript
{
"code": 0,
"message": "ok",
"data": {
"data": {
"1": {
"list": [
{
// 主键
"id": 0,
// uid
"uid": 0,
// target_id
"target_id": 0,
// 守护类型 1为总督2为提督3为舰长
"privilege_type": 0,
// start_time
"start_time": "",
// expired_time
"expired_time": "",
// ctime
"ctime": "",
// utime
"utime": ""
}
]
}
}
}
}
```
## /live.xuser.v1.Guard/GetAnchorRecentTopGuard
### GetAnchorRecentTopGuard 获取最近的提督弹窗提醒
#### 方法GET
#### 请求参数
|参数名|必选|类型|描述|
|:---|:---|:---|:---|
|uid|是|integer||
#### 响应
```javascript
{
"code": 0,
"message": "ok",
"data": {
// 主键
"cnt": 0,
"list": [
{
"uid": 0,
"end_time": 0,
"is_open": 0
}
]
}
}
```
## /live.xuser.v1.Guard/GetTopListGuard
### GetTopListGuard 获取某个up主的守护排行榜
#### 方法GET
#### 请求参数
|参数名|必选|类型|描述|
|:---|:---|:---|:---|
|uid|是|integer||
|page|否|integer||
|page_size|否|integer||
#### 响应
```javascript
{
"code": 0,
"message": "ok",
"data": {
// 守护总数量
"num": 0,
"page": 0,
"now": 0,
"list": [
{
"uid": 0,
"ruid": 0,
"rank": 0,
"guard_level": 0
}
],
"top3": [
{
"uid": 0,
"ruid": 0,
"rank": 0,
"guard_level": 0
}
]
}
}
```
## /live.xuser.v1.Guard/GetTopListGuardNum
### GetTopListGuardNum 获取某个up主所有的守护数量,和GetTopListGuard接口的区别是此接口用于房间页首屏,逻辑比较简单,因此拆分开来
#### 方法GET
#### 请求参数
|参数名|必选|类型|描述|
|:---|:---|:---|:---|
|uid|是|integer||
#### 响应
```javascript
{
"code": 0,
"message": "ok",
"data": {
"total_count": 0
}
}
```
## /live.xuser.v1.Guard/ClearUIDCache
### ClearUIDCache 清除cache
#### 方法GET
#### 请求参数
|参数名|必选|类型|描述|
|:---|:---|:---|:---|
|uid|是|integer||
|magic_key|是|string||
#### 响应
```javascript
{
"code": 0,
"message": "ok",
"data": {
}
}
```

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,204 @@
syntax = "proto3";
package live.xuser.v1;
option go_package = "v1";
import "github.com/gogo/protobuf/gogoproto/gogo.proto";
import "app/service/live/xuser/api/grpc/xuser.proto";
message GuardBuyReq {
string order_id = 1 [(gogoproto.moretags) = "validate:\"required\""];
int64 uid = 2 [(gogoproto.moretags) = "validate:\"gt=0,required\""];
int64 ruid = 3 [(gogoproto.moretags) = "validate:\"gt=0,required\""];
int32 guard_level = 4 [(gogoproto.moretags) = "validate:\"gt=0,required\"", (gogoproto.casttype) = "int"];
int32 num = 5 [(gogoproto.moretags) = "validate:\"gt=0,required\"", (gogoproto.casttype) = "int"];
Platform platform = 6 [(gogoproto.moretags) = "validate:\"required\""];
string source = 7 [(gogoproto.moretags) = "validate:\"required\""];
}
message GuardBuyReply {
int32 status = 1 [(gogoproto.jsontag) = "status", (gogoproto.casttype) = "int"];
}
// GetByUidTargetIdReq 1:1请求
message ClearUIDCacheReq {
int64 uid = 1 [(gogoproto.moretags) = 'form:"uid" validate:"gt=0,required"'];
string magic_key = 2 [(gogoproto.moretags) = 'form:"magic_key" validate:"alpha,required"'];
}
message ClearUIDCacheResp {
}
// GetByUidTargetIdReq 1:1请求
message GetByUidTargetIdReq {
int64 uid = 1 [(gogoproto.moretags) = 'form:"uid" validate:"gt=0,required"'];
int64 target_id = 2 [(gogoproto.moretags) = 'form:"target_id" validate:"required"'];
int64 sort_type = 3 [(gogoproto.moretags) = 'form:"sort_type" validate:"gte=0"'];
}
message GetByUidTargetIdResp {
map<int64, DaHangHaiInfo> data = 1 [(gogoproto.jsontag) = "data"];
}
message AllDaHangHaiInfo {
map<int64,DaHangHaiInfo> guardInfo = 1 [(gogoproto.moretags) = 'validate:"required"'];
}
message FilterDaHangHaiInfo {
map<int64,DaHangHaiInfo> guardInfo = 1 [(gogoproto.moretags) = 'validate:"required"'];
}
message DaHangHaiInfo {
// 主键
int64 id = 1 [(gogoproto.jsontag) = "id"];
// uid
int64 uid = 2 [(gogoproto.jsontag) = "uid"];
// target_id
int64 target_id = 3 [(gogoproto.jsontag) = "target_id"];
// 守护类型 1为总督2为提督3为舰长
int64 privilege_type = 4 [(gogoproto.jsontag) = "privilege_type"];
// start_time
string start_time = 5 [(gogoproto.jsontag) = "start_time"];
// expired_time
string expired_time = 6 [(gogoproto.jsontag) = "expired_time"];
// ctime
string ctime = 7 [(gogoproto.jsontag) = "ctime"];
// utime
string utime = 8 [(gogoproto.jsontag) = "utime"];
}
message DaHangHaiInfoList {
repeated DaHangHaiInfo list = 1 [(gogoproto.jsontag) = "list"];
}
message GetByUidTargetIdsReq {
int64 uid = 1 [(gogoproto.moretags) = 'form:"uid" validate:"gt=0,required"'];
repeated TargetIds targetIDs = 2 [(gogoproto.moretags) = 'form:"targetIDs" validate:"required"'];
}
message GetByUidTargetIdsResp {
map<int64, DaHangHaiInfo> data = 1 [(gogoproto.jsontag) = "data"];
}
message TargetIds {
int64 target_id = 1 [(gogoproto.moretags) = 'form:"target_id" validate:"gt=0,required"'];
int64 sort_type = 2 [(gogoproto.moretags) = 'form:"sort_type" validate:"gt=0"'];
}
// GetByTargetIdsBatchReq 根据uids批量获取所有守护关系
message GetByTargetIdsReq {
repeated TargetIds targetIDs = 1 [(gogoproto.moretags) = 'form:"targetIDs" validate:"required"'];
}
// TODO 实现
message GetByTargetIdsResp {
}
// GetAllShouHuReq 目标全部守护数据
message GetByUidReq {
int64 uid = 1 [(gogoproto.moretags) = 'form:"uid" validate:"gt=0,required"'];
}
message GetByUidResp {
map<int64, DaHangHaiInfo> data = 1 [(gogoproto.jsontag) = "data"];
}
// GetAllShouHuBatchReq 目标全部守护数据(批量)
message GetByUidBatchReq {
repeated int64 uids = 1 [(gogoproto.moretags) = 'form:"uids" validate:"required"'];
}
message GetByUidBatchResp {
map<int64, DaHangHaiInfoList> data = 1 [(gogoproto.jsontag) = "data"];
}
message GetAnchorRecentTopGuardReq{
int64 uid = 1 [(gogoproto.moretags) = 'form:"uid" validate:"gt=0,required"'];
}
message GetAnchorRecentTopGuardResp{
// 主键
int64 cnt = 1 [(gogoproto.jsontag) = "cnt"];
repeated GetAnchorRecentTopGuardList list = 2 [(gogoproto.jsontag) = "list"];
}
message GetAnchorRecentTopGuardList{
int64 uid = 1 [(gogoproto.jsontag) = "uid"];
int64 end_time = 2 [(gogoproto.jsontag) = "end_time"];
int64 is_open = 3 [(gogoproto.jsontag) = "is_open"];
}
message GetTopListGuardReq{
int64 uid = 1 [(gogoproto.moretags) = 'form:"uid" validate:"gt=0,required"'];
int64 page = 2 [(gogoproto.moretags) = 'form:"page" validate:"gte=1"'];
int64 page_size = 3 [(gogoproto.moretags) = 'form:"page_size" validate:"gte=1"'];
}
message GetTopListGuardResp{
// 守护总数量
int64 num = 1 [(gogoproto.jsontag) = "num"];
int64 page = 2 [(gogoproto.jsontag) = "page"];
int64 now = 3 [(gogoproto.jsontag) = "now"];
repeated TopListGuard list = 4[(gogoproto.jsontag) = "list"];
repeated TopListGuard top3 = 5[(gogoproto.jsontag) = "top3"];
}
message TopListGuard{
int64 uid = 1 [(gogoproto.jsontag) = "uid"];
int64 ruid = 2 [(gogoproto.jsontag) = "ruid"];
int64 rank = 4 [(gogoproto.jsontag) = "rank"];
int64 guard_level = 3 [(gogoproto.jsontag) = "guard_level"];
}
message GetTopListGuardNumReq{
int64 uid = 1 [(gogoproto.moretags) = 'form:"uid" validate:"gt=0,required"'];
}
message GetTopListGuardNumResp{
int64 total_count = 1 [(gogoproto.jsontag) = "total_count"];
}
service Guard {
// Buy 购买大航海
rpc Buy(GuardBuyReq) returns (GuardBuyReply);
// GetByUIDTargetID 获取我与目标用户守护关系,不支持批量(P0级)
rpc GetByUIDTargetID (GetByUidTargetIdReq) returns (GetByUidTargetIdResp);
// GetByTargetIdsBatch 获取我与目标用户守护关系,支持批量(P2级,必要时刻降级)
rpc GetByTargetIdsBatch (GetByTargetIdsReq) returns (GetByTargetIdsResp);
// GetByUIDTargetIds 根据uids批量获取所有守护关系,粉丝勋章使用
rpc GetByUIDTargetIds (GetByUidTargetIdsReq) returns (GetByUidTargetIdsResp);
// GetByUID 获取我所有的守护,不支持批量(P0级)
rpc GetByUIDForGift (GetByUidReq) returns (GetByUidResp);
// GetByUIDBatch 根据uids获取所有的守护,支持批量(P2级)
rpc GetByUIDBatch (GetByUidBatchReq) returns (GetByUidBatchResp);
// GetAnchorRecentTopGuard 获取最近的提督弹窗提醒
rpc GetAnchorRecentTopGuard (GetAnchorRecentTopGuardReq) returns (GetAnchorRecentTopGuardResp);
// GetTopListGuard 获取某个up主的守护排行榜
rpc GetTopListGuard (GetTopListGuardReq) returns (GetTopListGuardResp);
// GetTopListGuardNum 获取某个up主所有的守护数量,和GetTopListGuard接口的区别是此接口用于房间页首屏,逻辑比较简单,因此拆分开来
rpc GetTopListGuardNum (GetTopListGuardNumReq) returns (GetTopListGuardNumResp);
// ClearUIDCache 清除cache
rpc ClearUIDCache (ClearUIDCacheReq) returns (ClearUIDCacheResp);
}

View File

@@ -0,0 +1,69 @@
// Code generated by protoc-gen-bm v0.1, DO NOT EDIT.
// source: api/grpc/v1/vip.proto
/*
Package v1 is a generated blademaster stub package.
This code was generated with go-common/app/tool/bmgen/protoc-gen-bm v0.1.
It is generated from these files:
api/grpc/v1/vip.proto
*/
package v1
import (
"context"
bm "go-common/library/net/http/blademaster"
"go-common/library/net/http/blademaster/binding"
)
// to suppressed 'imported but not used warning'
var _ *bm.Context
var _ context.Context
var _ binding.StructValidator
// =============
// Vip Interface
// =============
type Vip interface {
// Info 返回用户vip信息
Info(ctx context.Context, req *UidReq) (resp *InfoReply, err error)
// Buy 购买月费/年费姥爷
Buy(ctx context.Context, req *BuyReq) (resp *BuyReply, err error)
}
var v1VipSvc Vip
// @params UidReq
// @router GET /xlive/xuser/v1/vip/Info
// @response InfoReply
func vipInfo(c *bm.Context) {
p := new(UidReq)
if err := c.BindWith(p, binding.Default(c.Request.Method, c.Request.Header.Get("Content-Type"))); err != nil {
return
}
resp, err := v1VipSvc.Info(c, p)
c.JSON(resp, err)
}
// @params BuyReq
// @router GET /xlive/xuser/v1/vip/Buy
// @response BuyReply
func vipBuy(c *bm.Context) {
p := new(BuyReq)
if err := c.BindWith(p, binding.Default(c.Request.Method, c.Request.Header.Get("Content-Type"))); err != nil {
return
}
resp, err := v1VipSvc.Buy(c, p)
c.JSON(resp, err)
}
// RegisterV1VipService Register the blademaster route with middleware map
// midMap is the middleware map, the key is defined in proto
func RegisterV1VipService(e *bm.Engine, svc Vip, midMap map[string]bm.HandlerFunc) {
v1VipSvc = svc
e.GET("/xlive/xuser/v1/vip/Info", vipInfo)
e.GET("/xlive/xuser/v1/vip/Buy", vipBuy)
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,41 @@
syntax = "proto3";
package live.xuser.v1;
option go_package = "v1";
import "github.com/gogo/protobuf/gogoproto/gogo.proto";
import "app/service/live/xuser/api/grpc/xuser.proto";
message UidReq {
int64 uid = 1 [(gogoproto.moretags) = "validate:\"gt=0,required\""];
}
message Info {
int32 vip = 1 [(gogoproto.jsontag) = "vip", (gogoproto.casttype) = "int"];
int32 svip = 2 [(gogoproto.jsontag) = "svip", (gogoproto.casttype) = "int"];
string vip_time = 3 [(gogoproto.jsontag) = "vip_time"];
string svip_time = 4 [(gogoproto.jsontag) = "svip_time"];
}
message InfoReply {
Info info = 1;
}
message BuyReq {
string order_id = 1 [(gogoproto.moretags) = "validate:\"required\""];
int64 uid = 2 [(gogoproto.moretags) = "validate:\"gt=0,required\""];
int32 good_id = 3 [(gogoproto.moretags) = "validate:\"gt=0,required\"", (gogoproto.casttype) = "int"];
int32 good_num = 4 [(gogoproto.moretags) = "validate:\"gt=0,required\"", (gogoproto.casttype) = "int"];
Platform platform = 5 [(gogoproto.moretags) = "validate:\"required\""];
string source = 6 [(gogoproto.moretags) = "validate:\"required\""];
}
message BuyReply {
int32 status = 1 [(gogoproto.jsontag) = "status", (gogoproto.casttype) = "int"];
}
service Vip {
// Info 返回用户vip信息
rpc Info(UidReq) returns (InfoReply);
// Buy 购买月费/年费姥爷
rpc Buy(BuyReq) returns (BuyReply);
}

View File

@@ -0,0 +1,50 @@
## Info 返回用户vip信息
`GET http://api.live.bilibili.com/xlive/xuser/v1/vip/Info`
### 请求参数
|参数名|必选|类型|描述|
|:---|:---|:---|:---|
|uid|是|integer||
```json
{
"code": 0,
"message": "ok",
"data": {
"info": {
"vip": 0,
"svip": 0,
"vip_time": "",
"svip_time": ""
}
}
}
```
## Buy 购买月费/年费姥爷
`GET http://api.live.bilibili.com/xlive/xuser/v1/vip/Buy`
### 请求参数
|参数名|必选|类型|描述|
|:---|:---|:---|:---|
|order_id|是|string||
|uid|是|integer||
|good_id|是|integer||
|good_num|是|integer||
|platform|是|integer||
|source|是|string||
```json
{
"code": 0,
"message": "ok",
"data": {
"status": 0
}
}
```

View File

@@ -0,0 +1,76 @@
// Code generated by protoc-gen-gogo. DO NOT EDIT.
// source: app/service/live/xuser/api/grpc/xuser.proto
package grpc
import proto "github.com/gogo/protobuf/proto"
import fmt "fmt"
import math "math"
import _ "github.com/gogo/protobuf/gogoproto"
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package
type Platform int32
const (
Platform_UNKNOWN Platform = 0
Platform_PC Platform = 1
Platform_ANDROID Platform = 2
Platform_IOS Platform = 3
Platform_H5 Platform = 4
)
var Platform_name = map[int32]string{
0: "UNKNOWN",
1: "PC",
2: "ANDROID",
3: "IOS",
4: "H5",
}
var Platform_value = map[string]int32{
"UNKNOWN": 0,
"PC": 1,
"ANDROID": 2,
"IOS": 3,
"H5": 4,
}
func (x Platform) String() string {
return proto.EnumName(Platform_name, int32(x))
}
func (Platform) EnumDescriptor() ([]byte, []int) {
return fileDescriptor_xuser_ffbdb239d50de63c, []int{0}
}
func init() {
proto.RegisterEnum("live.xuser.Platform", Platform_name, Platform_value)
}
func init() {
proto.RegisterFile("app/service/live/xuser/api/grpc/xuser.proto", fileDescriptor_xuser_ffbdb239d50de63c)
}
var fileDescriptor_xuser_ffbdb239d50de63c = []byte{
// 185 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xd2, 0x4e, 0x2c, 0x28, 0xd0,
0x2f, 0x4e, 0x2d, 0x2a, 0xcb, 0x4c, 0x4e, 0xd5, 0xcf, 0xc9, 0x2c, 0x4b, 0xd5, 0xaf, 0x28, 0x2d,
0x4e, 0x2d, 0xd2, 0x4f, 0x2c, 0xc8, 0xd4, 0x4f, 0x2f, 0x2a, 0x48, 0x86, 0x70, 0xf5, 0x0a, 0x8a,
0xf2, 0x4b, 0xf2, 0x85, 0xb8, 0x40, 0x0a, 0xf4, 0xc0, 0x22, 0x52, 0xba, 0xe9, 0x99, 0x25, 0x19,
0xa5, 0x49, 0x7a, 0xc9, 0xf9, 0xb9, 0xfa, 0xe9, 0xf9, 0xe9, 0xf9, 0xfa, 0x60, 0x25, 0x49, 0xa5,
0x69, 0x60, 0x1e, 0x98, 0x03, 0x66, 0x41, 0xb4, 0x6a, 0xd9, 0x72, 0x71, 0x04, 0xe4, 0x24, 0x96,
0xa4, 0xe5, 0x17, 0xe5, 0x0a, 0x71, 0x73, 0xb1, 0x87, 0xfa, 0x79, 0xfb, 0xf9, 0x87, 0xfb, 0x09,
0x30, 0x08, 0xb1, 0x71, 0x31, 0x05, 0x38, 0x0b, 0x30, 0x82, 0x04, 0x1d, 0xfd, 0x5c, 0x82, 0xfc,
0x3d, 0x5d, 0x04, 0x98, 0x84, 0xd8, 0xb9, 0x98, 0x3d, 0xfd, 0x83, 0x05, 0x98, 0x41, 0xb2, 0x1e,
0xa6, 0x02, 0x2c, 0x4e, 0x42, 0x27, 0x1e, 0xc9, 0x31, 0x5e, 0x78, 0x24, 0xc7, 0xf8, 0xe0, 0x91,
0x1c, 0x63, 0x14, 0x0b, 0xc8, 0x65, 0x49, 0x6c, 0x60, 0x93, 0x8d, 0x01, 0x01, 0x00, 0x00, 0xff,
0xff, 0x65, 0xc5, 0x84, 0x8d, 0xc3, 0x00, 0x00, 0x00,
}

View File

@@ -0,0 +1,13 @@
syntax = "proto3";
package live.xuser;
option go_package = "grpc";
import "github.com/gogo/protobuf/gogoproto/gogo.proto";
enum Platform {
UNKNOWN = 0;
PC = 1;
ANDROID = 2;
IOS = 3;
H5 = 4;
}

View File

@@ -0,0 +1 @@
# HTTP API文档

View File

@@ -0,0 +1,209 @@
## 根据登录态获取功能入口是否显示, 需要登录态
`GET http://api.live.bilibili.com/xlive/xuser/v1/roomAdmin/is_any`
### 请求参数
|参数名|必选|类型|描述|
|:---|:---|:---|:---|
|uid|否|integer||
```json
{
"code": 0,
"message": "ok",
"data": {
// 是否有房管
"has_admin": 0
}
}
```
## 获取用户拥有的的所有房管身份
`GET http://api.live.bilibili.com/xlive/xuser/v1/roomAdmin/get_by_uid`
### 请求参数
|参数名|必选|类型|描述|
|:---|:---|:---|:---|
|uid|否|integer| 用户uid|
|page|否|integer| 页数|
```json
{
"code": 0,
"message": "ok",
"data": {
"page": {
// 当前页码
"page": 0,
// 每页大小
"page_size": 0,
// 总页数
"total_page": 0,
// 总记录数
"total_count": 0
},
"data": [
{
// 用户id
"uid": 0,
// 房间号
"roomid": 0,
// 主播的用户id
"anchor_id": 0,
// 主播用户名
"uname": "",
// 主播封面
"anchor_cover": "",
// 上任时间
"ctime": ""
}
]
}
}
```
## 辞职房管
`GET http://api.live.bilibili.com/xlive/xuser/v1/roomAdmin/resign`
### 请求参数
|参数名|必选|类型|描述|
|:---|:---|:---|:---|
|roomid|否|integer| 房间号|
|uid|否|integer| 用户uid|
```json
{
"code": 0,
"message": "ok",
"data": {
}
}
```
## 查询需要添加的房管
`GET http://api.live.bilibili.com/xlive/xuser/v1/roomAdmin/search_for_admin`
### 请求参数
|参数名|必选|类型|描述|
|:---|:---|:---|:---|
|uid|否|integer||
|key_word|否|string||
```json
{
"code": 0,
"message": "ok",
"data": {
"data": [
{
// 用户id
"uid": 0,
// 是否房管
"is_admin": 0,
// 用户名
"uname": "",
// 用户头像
"face": "",
// 粉丝勋章名称
"medal_name": "",
// 粉丝勋章等级
"level": 0
}
]
}
}
```
## 获取主播拥有的的所有房管
`GET http://api.live.bilibili.com/xlive/xuser/v1/roomAdmin/get_by_anchor`
### 请求参数
|参数名|必选|类型|描述|
|:---|:---|:---|:---|
|page|否|integer| 页数|
|uid|否|integer| 用户uid|
```json
{
"code": 0,
"message": "ok",
"data": {
"page": {
// 当前页码
"page": 0,
// 每页大小
"page_size": 0,
// 总页数
"total_page": 0,
// 总记录数
"total_count": 0
},
"data": [
{
// 用户id
"uid": 0,
// 用户名
"uname": "",
// 用户头像
"face": "",
// 上任时间
"ctime": "",
// 粉丝勋章名称
"medal_name": "",
// 粉丝勋章等级
"level": 0
}
]
}
}
```
## 撤销房管
`GET http://api.live.bilibili.com/xlive/xuser/v1/roomAdmin/dismiss`
### 请求参数
|参数名|必选|类型|描述|
|:---|:---|:---|:---|
|uid|否|integer| 房管的用户uid|
|anchor_id|否|integer| 主播uid|
```json
{
"code": 0,
"message": "ok",
"data": {
}
}
```
## 任命房管
`GET http://api.live.bilibili.com/xlive/xuser/v1/roomAdmin/appoint`
### 请求参数
|参数名|必选|类型|描述|
|:---|:---|:---|:---|
|uid|否|integer| 房管的uid|
|anchor_id|否|integer| 主播uid|
```json
{
"code": 0,
"message": "ok",
"data": {
}
}
```

View File

@@ -0,0 +1,46 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_binary",
"go_library",
)
go_binary(
name = "cmd",
embed = [":go_default_library"],
tags = ["automanaged"],
)
go_library(
name = "go_default_library",
srcs = ["main.go"],
data = ["test.toml"],
importpath = "go-common/app/service/live/xuser/cmd",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/service/live/xuser/conf:go_default_library",
"//app/service/live/xuser/server/grpc:go_default_library",
"//app/service/live/xuser/server/http:go_default_library",
"//app/service/live/xuser/service:go_default_library",
"//library/ecode/tip:go_default_library",
"//library/log:go_default_library",
"//library/net/trace:go_default_library",
"//library/queue/databus/report:go_default_library",
],
)
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,64 @@
package main
import (
"context"
"flag"
"fmt"
"go-common/app/service/live/xuser/server/grpc"
"go-common/library/net/trace"
"go-common/library/queue/databus/report"
"os"
"os/signal"
"syscall"
"time"
"go-common/app/service/live/xuser/conf"
"go-common/app/service/live/xuser/server/http"
"go-common/app/service/live/xuser/service"
ecode "go-common/library/ecode/tip"
"go-common/library/log"
)
func main() {
flag.Parse()
if err := conf.Init(); err != nil {
panic(err)
}
log.Init(conf.Conf.Log)
defer log.Close()
log.Info("xuser-service start")
if conf.TraceInit {
trace.Init(conf.Conf.Tracer)
defer trace.Close()
}
report.InitUser(conf.Conf.Report)
ecode.Init(conf.Conf.Ecode)
svc := service.New(conf.Conf)
http.Init(conf.Conf, svc)
// start grpc server
svr, err := grpc.New(svc)
if err != nil {
panic(fmt.Sprintf("start xuser grpc server fail! %s", err))
}
c := make(chan os.Signal, 1)
signal.Notify(c, syscall.SIGHUP, syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT)
for {
s := <-c
log.Info("get a signal %s", s.String())
switch s {
case syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT:
svc.Close()
if svr != nil {
svr.Shutdown(context.Background())
}
log.Info("xuser-service exit")
time.Sleep(time.Second)
return
case syscall.SIGHUP:
default:
return
}
}
}

View File

@@ -0,0 +1,183 @@
[log]
stdout=true
[liveUserMysql]
addr = "172.16.38.117:3312"
dsn = "live:oWni@ElNs0P0C(dphdj*F1y4@tcp(172.16.38.117:3312)/live-user?timeout=5s&readTimeout=5s&writeTimeout=5s&parseTime=true&loc=Local&charset=utf8,utf8mb4"
readDSN = ["live:oWni@ElNs0P0C(dphdj*F1y4@tcp(172.16.38.117:3312)/live-user?timeout=5s&readTimeout=5s&writeTimeout=5s&parseTime=true&loc=Local&charset=utf8,utf8mb4"]
active = 20
idle = 10
idleTimeout ="4h"
queryTimeout = "5s"
execTimeout = "5s"
tranTimeout = "5s"
[UserExpMySQL]
addr = "172.22.34.101:3312"
dsn = "livetestuat:livetestuat20180711/live_user_exp?timeout=2s&readTimeout=2s&writeTimeout=2s&parseTime=true&loc=Local&charset=utf8,utf8mb4"
readDSN = ["livetestuat:livetestuat20180711@tcp(172.22.34.101:3312)/live_user_exp?timeout=2s&readTimeout=2s&writeTimeout=2s&parseTime=true&loc=Local&charset=utf8,utf8mb4"]
active = 20
idle = 10
idleTimeout ="4h"
queryTimeout = "2s"
execTimeout = "2s"
tranTimeout = "5s"
[ExpMemcache]
name = "xuser-service"
proto = "tcp"
addr = "172.18.33.82:11211"
active = 50
idle = 10
dialTimeout = "1s"
readTimeout = "1s"
writeTimeout = "1s"
idleTimeout = "10s"
expire = "24h"
[Switch]
QueryExp = 1
[UserExpExpire]
ExpireTime = 86400
[vipRedis]
name = "xuser-vip-redis"
proto = "tcp"
addr = "127.0.0.1:6379"
idle = 10
active = 10
dialTimeout = "1s"
readTimeout = "1s"
writeTimeout = "1s"
idleTimeout = "10s"
expire = "1m"
[guardRedis]
name = "xuser-vip-redis"
proto = "tcp"
addr = "127.0.0.1:6379"
idle = 10
active = 10
dialTimeout = "1s"
readTimeout = "1s"
writeTimeout = "1s"
idleTimeout = "10s"
expire = "1m"
[bmClient]
key = "837f620f5c0a8010"
secret = "2a9057021014b4b843b635664c69d5df"
dial = "500ms"
timeout = "1s"
keepAlive = "60s"
[httpClient.breaker]
window = "3s"
sleep = "100ms"
bucket = 10
ratio = 0.5
request = 100
[liveRPC]
[liverpc.room]
AppID = "live.room"
ConnTimeout = "50ms"
Addr = "172.18.33.82:20200"
[liverpc.user]
AppID = "live.user"
ConnTimeout = "50ms"
Addr = "172.18.33.82:20802"
[liverpc.banned]
AppID = "live.banned"
ConnTimeout = "50ms"
Addr = "172.18.33.82:20822"
[liverpc.fans_medal]
AppID = "live.fansmedal"
ConnTimeout = "50ms"
Addr = "172.18.33.82:20800"
[liveVipChangePub]
key = "ec4c0820d525d67b"
secret = "2bdf3bd4ecab041b5d5640a1da4f7f81"
group = "LiveVipChange-LiveLive-P"
topic = "LiveVipChange-T"
action = "pub"
buffer = 128
name = "xuser-vip/databus"
proto = "tcp"
addr = "172.16.33.158:6205"
active = 1
idle = 1
dialTimeout = "1s"
readTimeout = "1s"
writeTimeout = "1s"
idleTimeout = "10s"
[liveEntryEffectPub]
key = "ec4c0820d525d67b"
secret = "e20f8f664bf10722efeb6aac0cc16011"
group = "LiveEntryEffect-LiveLive-P"
topic = "LiveEntryEffect-T"
action = "pub"
buffer = 128
name = "xuser-guard/databus"
proto = "tcp"
addr = "172.18.33.50:6205"
active = 1
idle = 1
dialTimeout = "1s"
readTimeout = "1s"
writeTimeout = "1s"
idleTimeout = "10s"
[liveAppMySQL]
addr = "172.22.34.101:3312"
dsn = "livetestuat:livetestuat20180711@tcp(172.22.34.101:3312)/live-app?timeout=2s&readTimeout=2s&writeTimeout=2s&parseTime=true&loc=Local&charset=utf8,utf8mb4"
readDSN = ["live:livetestuat20180711@tcp(172.22.34.101:3312)/live-app?timeout=2s&readTimeout=2s&writeTimeout=2s&parseTime=true&loc=Local&charset=utf8,utf8mb4"]
active = 20
idle = 10
idleTimeout ="4h"
queryTimeout = "2s"
execTimeout = "2s"
tranTimeout = "5s"
[liveAppORM]
addr = "172.22.34.101:3312"
dsn = "livetestuat:livetestuat20180711@tcp(172.22.34.101:3312)/live-app?timeout=2s&readTimeout=2s&writeTimeout=2s&parseTime=true&loc=Local&charset=utf8,utf8mb4"
readDSN = ["live:livetestuat20180711@tcp(172.22.34.101:3312)/live-app?timeout=2s&readTimeout=2s&writeTimeout=2s&parseTime=true&loc=Local&charset=utf8,utf8mb4"]
active = 20
idle = 10
idleTimeout ="4h"
queryTimeout = "2s"
execTimeout = "2s"
tranTimeout = "5s"
[redis]
name = "xuser-redis"
proto = "tcp"
addr = "127.0.0.1:6379"
idle = 10
active = 10
dialTimeout = "1s"
readTimeout = "1s"
writeTimeout = "1s"
idleTimeout = "10s"
expire = "1m"
[memcache]
name = "xuser-memcache"
proto = "tcp"
addr = "127.0.0.1:11211"
active = 50
idle = 10
dialTimeout = "1s"
readTimeout = "1s"
writeTimeout = "1s"
idleTimeout = "10s"
expire = "24h"

View File

@@ -0,0 +1,49 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = [
"conf.go",
"log_hash.go",
"tool.go",
],
importpath = "go-common/app/service/live/xuser/conf",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//library/cache/memcache:go_default_library",
"//library/cache/redis:go_default_library",
"//library/conf:go_default_library",
"//library/database/orm:go_default_library",
"//library/database/sql:go_default_library",
"//library/ecode/tip:go_default_library",
"//library/log:go_default_library",
"//library/net/http/blademaster:go_default_library",
"//library/net/http/blademaster/middleware/verify:go_default_library",
"//library/net/rpc:go_default_library",
"//library/net/rpc/liverpc:go_default_library",
"//library/net/rpc/warden:go_default_library",
"//library/net/trace:go_default_library",
"//library/queue/databus:go_default_library",
"//vendor/github.com/BurntSushi/toml:go_default_library",
],
)
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,138 @@
package conf
import (
"errors"
"flag"
"go-common/library/net/rpc/warden"
"go-common/library/queue/databus"
"go-common/library/cache/memcache"
"go-common/library/cache/redis"
"go-common/library/conf"
"go-common/library/database/orm"
"go-common/library/database/sql"
eCode "go-common/library/ecode/tip"
"go-common/library/log"
bm "go-common/library/net/http/blademaster"
"go-common/library/net/http/blademaster/middleware/verify"
"go-common/library/net/rpc"
liverRPC "go-common/library/net/rpc/liverpc"
"go-common/library/net/trace"
"github.com/BurntSushi/toml"
)
var (
confPath string
// TraceInit weather need init trace
TraceInit bool
client *conf.Client
// Conf config
Conf = &Config{}
)
// Config .
type Config struct {
Log *log.Config
BM *bm.ServerConfig
BMClient *bm.ClientConfig
Verify *verify.Config
Tracer *trace.Config
VipRedis *redis.Config
GuardRedis *redis.Config
Redis *redis.Config
Memcache *memcache.Config
ExpMemcache *memcache.Config
LiveUserMysql *sql.Config
LiveAppMySQL *sql.Config
LiveAppORM *orm.Config
Ecode *eCode.Config
LiveVipChangePub *databus.Config
UserExpMySQL *sql.Config
LiveRPC map[string]*liverRPC.ClientConfig
LiveEntryEffectPub *databus.Config
GuardCfg *GuardCfg
AccountRPC *rpc.ClientConfig
Switch *ConfigSwitch
UserExpExpire *UserExpExpireConf
UserDaHangHaiExpire *UserDhhExpireConf
// report
Report *databus.Config
XanchorClient *warden.ClientConfig
}
// GuardCfg config for guard
type GuardCfg struct {
OpenEntryEffectDatabus bool
EnableGuardBroadcast bool
DanmuHost string
}
// ConfigSwitch config for query
type ConfigSwitch struct {
QueryExp int
}
// UserExpExpireConf config for cache expire
type UserExpExpireConf struct {
ExpireTime int32
}
// UserDhhExpireConf config for cache expire
type UserDhhExpireConf struct {
ExpireTime int32
}
func init() {
flag.StringVar(&confPath, "conf", "", "default config path")
flag.BoolVar(&TraceInit, "traceInit", true, "default trace init")
}
// Init init conf
func Init() error {
if confPath != "" {
return local()
}
return remote()
}
func local() (err error) {
_, err = toml.DecodeFile(confPath, &Conf)
return
}
func remote() (err error) {
if client, err = conf.New(); err != nil {
return
}
if err = load(); err != nil {
return
}
go func() {
for range client.Event() {
log.Info("config reload")
if load() != nil {
log.Error("config reload error (%v)", err)
}
}
}()
return
}
func load() (err error) {
var (
s string
ok bool
tmpConf *Config
)
if s, ok = client.Toml2(); !ok {
return errors.New("load config center error")
}
if _, err = toml.Decode(s, &tmpConf); err != nil {
return errors.New("could not decode config")
}
*Conf = *tmpConf
return
}

View File

@@ -0,0 +1,19 @@
package conf
const (
// GetFromDHHDBError 回源db错误
GetFromDHHDBError = "get_from_dhh_db"
// ScanFromDHHDBError scan db错误
ScanFromDHHDBError = "scan_db"
// _INFO_ADD_ANCHOREXP_RETRY_UPDATEDB = "AI1"
// _ERROR_ADD_USEREXP_UPDATEDB_RETRYALL_FAIL = "UE1"
// _ERROR_QUERY_AFTER_ADD_USEREXP_UPDATEDB_FAIL = "UE2"
// _ERROR_QUERY_AFTER_ADD_ANCHOREXP_UPDATEDB_FAIL = "AE1"
// _INFO_ADD_USEREXP_RETRY_UPDATEDB = "UI1"
// _ERROR_GETEXP_DB_FAIL = "GE1"
// _ERROR_ASYNC_CACHE_FAIL = "SE1"
// _ERROR_ASYNC_CACHE_FAIL2 = "SE2"
// _ERROR_ADD_ANCHOREXP_UPDATEDB_RETRYALL_FAIL = "AE2"
// _ERROR_ASYNC_CACHE_FAIL3 = "SE3"
// _ERROR_QUERY_CACHE_FAIL = "CE1"
)

View File

@@ -0,0 +1,17 @@
package conf
import (
"go-common/library/log"
"time"
)
// RecordTimeCostLog 记录日志
func RecordTimeCostLog(nowTime int64, desc string) {
log.Info(desc+"|%d", nowTime)
}
// RecordTimeCost ...
// 记录时间
func RecordTimeCost() (NowTime int64) {
return time.Now().UnixNano() / 1000000 // 用毫秒
}

View File

@@ -0,0 +1,55 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_test",
"go_library",
)
go_test(
name = "go_default_test",
srcs = ["api_test.go"],
embed = [":go_default_library"],
tags = ["automanaged"],
deps = [
"//app/service/live/xuser/conf:go_default_library",
"//vendor/github.com/smartystreets/goconvey/convey:go_default_library",
],
)
go_library(
name = "go_default_library",
srcs = ["api.go"],
importpath = "go-common/app/service/live/xuser/dao",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/service/live/banned_service/api/liverpc:go_default_library",
"//app/service/live/fans_medal/api/liverpc:go_default_library",
"//app/service/live/room/api/liverpc:go_default_library",
"//app/service/live/xuser/conf:go_default_library",
"//library/net/rpc/liverpc:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [
":package-srcs",
"//app/service/live/xuser/dao/account:all-srcs",
"//app/service/live/xuser/dao/exp:all-srcs",
"//app/service/live/xuser/dao/guard:all-srcs",
"//app/service/live/xuser/dao/roomAdmin:all-srcs",
"//app/service/live/xuser/dao/vip:all-srcs",
"//app/service/live/xuser/dao/xanchor:all-srcs",
],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,40 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = [
"dao.go",
"userInfo.go",
],
importpath = "go-common/app/service/live/xuser/dao/account",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/service/live/xuser/conf:go_default_library",
"//app/service/main/account/api:go_default_library",
"//app/service/main/account/model:go_default_library",
"//app/service/main/account/rpc/client:go_default_library",
"//library/ecode:go_default_library",
"//library/log:go_default_library",
"//vendor/github.com/pkg/errors:go_default_library",
],
)
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,35 @@
package account
import (
"context"
"go-common/app/service/live/xuser/conf"
account "go-common/app/service/main/account/rpc/client"
)
// Dao dao
type Dao struct {
c *conf.Config
accountRPC *account.Service3
}
// New init mysql db
func New(c *conf.Config) (dao *Dao) {
dao = &Dao{
c: c,
accountRPC: account.New3(nil),
}
return
}
// Close close the resource.
func (d *Dao) Close() {
}
// Ping dao ping
func (d *Dao) Ping(c context.Context) error {
// TODO: if you need use mc,redis, please add
// check
return nil
}

View File

@@ -0,0 +1,46 @@
package account
import (
"context"
"github.com/pkg/errors"
v1 "go-common/app/service/main/account/api"
accountM "go-common/app/service/main/account/model"
"go-common/library/ecode"
"go-common/library/log"
)
// GetUserCard3 ...
// 调用account grpc接口cards获取用户信息
func (d *Dao) GetUserCard3(c context.Context, UIDs []int64) (userResult map[int64]*accountM.Card, err error) {
userResult = make(map[int64]*accountM.Card)
lens := len(UIDs)
if lens <= 0 {
return
}
ret, err := d.accountRPC.Cards3(c, &accountM.ArgMids{Mids: UIDs})
if err != nil {
err = errors.WithMessage(ecode.AccountGRPCError, "GET SEA PATROL FAIL")
log.Error("Call main.Account.Cards Error.Infos(%+v) error(%+v)", UIDs, err)
}
// 整理数据
for _, item := range ret {
if item != nil {
userResult[item.Mid] = item
}
}
return
}
// GetUserInfo ...
// 调用account grpc接口info获取用户信息
func (d *Dao) GetUserInfo(c context.Context, UID int64) (userResult *v1.Info, err error) {
userResult = &v1.Info{}
ret, err := d.accountRPC.Info3(c, &accountM.ArgMid{Mid: UID})
if err != nil {
err = errors.WithMessage(ecode.AccountGRPCError, "GET SEA PATROL FAIL")
log.Error("Call main.Account.Cards Error.Infos(%+v) error(%+v)", UID, err)
return
}
userResult = ret
return
}

View File

@@ -0,0 +1,47 @@
package dao
import (
banned_api "go-common/app/service/live/banned_service/api/liverpc"
fans_medal "go-common/app/service/live/fans_medal/api/liverpc"
room_api "go-common/app/service/live/room/api/liverpc"
"go-common/app/service/live/xuser/conf"
"go-common/library/net/rpc/liverpc"
)
// Dao dao
type Dao struct {
c *conf.Config
}
// New init mysql db
func New(c *conf.Config) (dao *Dao) {
dao = &Dao{
c: c,
}
return
}
// RoomAPI .
var RoomAPI *room_api.Client
// FansMedalAPI .
var FansMedalAPI *fans_medal.Client
// BannedAPI .
var BannedAPI *banned_api.Client
// InitAPI init all service APIs
func InitAPI() {
RoomAPI = room_api.New(getConf("room"))
FansMedalAPI = fans_medal.New(getConf("fans_medal"))
BannedAPI = banned_api.New(getConf("banned"))
}
func getConf(appName string) *liverpc.ClientConfig {
c := conf.Conf.LiveRPC
if c != nil {
return c[appName]
}
return nil
}

View File

@@ -0,0 +1,46 @@
package dao
import (
"go-common/app/service/live/xuser/conf"
"testing"
"github.com/smartystreets/goconvey/convey"
)
func TestDaoNew(t *testing.T) {
convey.Convey("New", t, func(ctx convey.C) {
var (
c = &conf.Config{}
)
ctx.Convey("When everything gose positive", func(ctx convey.C) {
dao := New(c)
ctx.Convey("Then dao should not be nil.", func(ctx convey.C) {
ctx.So(dao, convey.ShouldNotBeNil)
})
})
})
}
func TestDaoInitAPI(t *testing.T) {
convey.Convey("InitAPI", t, func(ctx convey.C) {
ctx.Convey("When everything gose positive", func(ctx convey.C) {
InitAPI()
ctx.Convey("No return values", func(ctx convey.C) {
})
})
})
}
func TestDaogetConf(t *testing.T) {
convey.Convey("getConf", t, func(ctx convey.C) {
var (
appName = ""
)
ctx.Convey("When everything gose positive", func(ctx convey.C) {
p1 := getConf(appName)
ctx.Convey("Then p1 should not be nil.", func(ctx convey.C) {
ctx.So(p1, convey.ShouldNotBeNil)
})
})
})
}

View File

@@ -0,0 +1,42 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = [
"dao.go",
"memcache.go",
"mysql.go",
],
importpath = "go-common/app/service/live/xuser/dao/exp",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/service/live/xuser/conf:go_default_library",
"//app/service/live/xuser/model/exp:go_default_library",
"//library/cache/memcache:go_default_library",
"//library/database/sql:go_default_library",
"//library/log:go_default_library",
"//library/stat/prom:go_default_library",
"//library/sync/errgroup:go_default_library",
"//library/xstr:go_default_library",
],
)
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,46 @@
package exp
import (
"context"
"go-common/app/service/live/xuser/conf"
"go-common/library/cache/memcache"
xsql "go-common/library/database/sql"
)
// Dao exp dao
type Dao struct {
c *conf.Config
db *xsql.DB
memcache *memcache.Pool
}
// NewExpDao init mysql db
func NewExpDao(c *conf.Config) (dao *Dao) {
dao = &Dao{
c: c,
db: xsql.NewMySQL(c.UserExpMySQL),
memcache: memcache.NewPool(c.ExpMemcache),
}
return
}
// Close close the resource.
func (d *Dao) Close() {
d.db.Close()
d.memcache.Close()
}
// Ping dao ping
func (d *Dao) Ping(ctx context.Context) error {
// TODO: add mc,redis... if you use
return nil
}
func (d *Dao) getExpire() (respExpire int32) {
if t := conf.Conf.UserExpExpire; t != nil {
respExpire = t.ExpireTime
} else {
respExpire = _emptyExpire
}
return
}

View File

@@ -0,0 +1,152 @@
package exp
import (
"context"
expModel "go-common/app/service/live/xuser/model/exp"
gmc "go-common/library/cache/memcache"
"go-common/library/log"
"go-common/library/stat/prom"
"go-common/library/sync/errgroup"
"math/rand"
"strconv"
"time"
)
// redis cache
const (
_prefixExp = "json_e_" // 用户经验cache key,json协议
_emptyExpire = 20 * 24 * 3600
_errorMcLogPrefix = "xuser.exp.dao.memcache"
_promGetSuccess = "xuser_exp_mc:获取用户经验cache成功"
_promDelSuccess = "xuser_exp_mc:成功删除用户经验cache"
_promDelErr = "xuser_exp_mc:删除用户经验cache失败"
_promGetErr = "xuser_exp_mc:批量获取用户经验key失败"
_promScanErr = "xuser_exp_mc:解析用户经验key失败"
// _recExpire = 5 * 24 * 3600
)
var (
errorsCount = prom.BusinessErrCount
infosCount = prom.BusinessInfoCount
cacheHitCount = prom.CacheHit
cacheMissCount = prom.CacheMiss
)
// PromError prometheus error count.
func PromError(name string) {
errorsCount.Incr(name)
}
// PromInfo prometheus info count.
func PromInfo(name string) {
infosCount.Incr(name)
}
// PromCacheHit prometheus cache hit count.
func PromCacheHit(name string) {
cacheHitCount.Incr(name)
}
// PromCacheMiss prometheus cache hit count.
func PromCacheMiss(name string) {
cacheMissCount.Incr(name)
}
func expKey(mid int64) string {
return _prefixExp + strconv.FormatInt(mid, 10)
}
// SetExpListCache 批量设置用户经验cache
func (d *Dao) SetExpListCache(c context.Context, expList map[int64]*expModel.LevelInfo) (err error) {
return d.setExpListCache(c, expList)
}
// DelExpFromMemCache 删除获取用户经验cache,不支持批量
func (d *Dao) DelExpFromMemCache(c context.Context, mid int64) (err error) {
return d.delExpFromMemCache(c, expKey(mid))
}
// GetExpFromMemCache 对外接口
func (d *Dao) GetExpFromMemCache(ctx context.Context, mids []int64) (expList map[int64]*expModel.LevelInfo, missedUids []int64, err error) {
return d.getExpFromMemCache(ctx, mids)
}
// getExpFromMemCache 批量获取用户经验cache
func (d *Dao) getExpFromMemCache(ctx context.Context, mids []int64) (expList map[int64]*expModel.LevelInfo, arrayMissedUids []int64, err error) {
var expKeys []string
expList = make(map[int64]*expModel.LevelInfo)
mapMissedUids := make(map[int64]bool)
arrayMissedUids = make([]int64, 0)
for _, uid := range mids {
expKeys = append(expKeys, expKey(uid))
mapMissedUids[uid] = true
}
group := errgroup.Group{}
group.Go(func() error {
conn := d.memcache.Get(context.TODO())
defer conn.Close()
resp, err := conn.GetMulti(expKeys)
if err != nil {
PromError(_promGetErr)
log.Error(_errorMcLogPrefix+"|conn.Gets(%v) error(%v)", expKeys, err)
return nil
}
// miss uids
for _, item := range resp {
element := &expModel.LevelInfo{}
if err = conn.Scan(item, &element); err != nil {
PromError(_promScanErr)
log.Error(_errorMcLogPrefix+"|item.Scan(%s) error(%v)", item.Value, err)
return err
}
expList[element.UID] = element
mapMissedUids[element.UID] = false
}
return nil
})
group.Wait()
for k, v := range mapMissedUids {
if v {
arrayMissedUids = append(arrayMissedUids, k)
}
}
PromInfo(_promGetSuccess)
return
}
func (d *Dao) delExpFromMemCache(ctx context.Context, key string) (err error) {
conn := d.memcache.Get(ctx)
if err = conn.Delete(key); err != nil {
if err == gmc.ErrNotFound {
err = nil
} else {
log.Error(_errorMcLogPrefix+"|Delete(%s) error(%v)", key, err)
PromError(_promDelErr)
}
}
PromInfo(_promDelSuccess)
conn.Close()
return
}
func (d *Dao) setExpListCache(ctx context.Context, expList map[int64]*expModel.LevelInfo) (err error) {
expire := d.getExpire()
rand.Seed(time.Now().UnixNano())
expire = expire + rand.Int31n(3600)
conn := d.memcache.Get(context.TODO())
defer conn.Close()
for _, v := range expList {
item := &gmc.Item{Key: expKey(v.UID),
Object: &expModel.LevelInfo{UID: v.UID, UserLevel: v.UserLevel, AnchorLevel: v.AnchorLevel, CTime: v.CTime, MTime: v.MTime},
Expiration: expire, Flags: gmc.FlagJSON}
err := conn.Set(item)
if err != nil {
PromError("mc:设置上报")
log.Error(_errorMcLogPrefix+"|conn.Set(%v) error(%v)", expKey(v.UID), err)
continue
}
}
return
}

View File

@@ -0,0 +1,111 @@
package exp
import (
"context"
"database/sql"
"fmt"
expm "go-common/app/service/live/xuser/model/exp"
"go-common/library/log"
"go-common/library/xstr"
)
const (
_shard = 10
_insExp = "INSERT IGNORE INTO user_exp_%d (uid,uexp,rexp) VALUES(?,?,?)"
_selExp = "SELECT uid,uexp,rexp,ctime,mtime FROM user_exp_%d where uid=?"
_inSelExp = "SELECT uid,uexp,rexp,ctime,mtime FROM user_exp_%d where uid IN (%s)"
_addUexp = "INSERT INTO user_exp_%d(uid,uexp,rexp) VALUES(?,?,0) ON DUPLICATE KEY UPDATE uexp=uexp+%d"
_addRexp = "INSERT INTO user_exp_%d(uid,uexp,rexp) VALUES(?,0,?) ON DUPLICATE KEY UPDATE rexp=rexp+%d"
_errorDBLogPrefix = "xuser.exp.dao.mysql"
)
// InitExp 初始化用户经验,用于首次查询
func (d *Dao) InitExp(c context.Context, uid int64, uexp int64, rexp int64) (row int64, err error) {
res, err := d.db.Exec(c, fmt.Sprintf(_insExp, uid%_shard), uid, uexp, rexp)
if err != nil {
log.Error(_errorDBLogPrefix+"|InitExp d.exp.Exec err: %v", err)
return
}
return res.RowsAffected()
}
// Exp 查询一条记录
func (d *Dao) Exp(c context.Context, uid int64) (exp *expm.Exp, err error) {
row := d.db.QueryRow(c, fmt.Sprintf(_selExp, uid%_shard), uid)
exp = &expm.Exp{}
if err = row.Scan(&exp.UID, &exp.Uexp, &exp.Rexp, &exp.CTime, &exp.MTime); err == sql.ErrNoRows {
// 查询结果为空时,初始化数据
_, err = d.InitExp(c, uid, 0, 0)
}
if err != nil {
log.Error(_errorDBLogPrefix+"|Exp row.Scan err: %v", err)
return
}
return
}
// MultiExp 批量查询
func (d *Dao) MultiExp(c context.Context, uids []int64) (exps []*expm.Exp, err error) {
exps = make([]*expm.Exp, 0)
var (
suffix int64
uidGroup [_shard][]int64
um = make(map[int64]struct{}, len(uids))
)
for _, uid := range uids {
suffix = uid % _shard
uidGroup[suffix] = append(uidGroup[suffix], uid)
um[uid] = struct{}{}
}
for index, uids := range uidGroup {
if 0 == len(uids) {
continue
}
rows, err1 := d.db.Query(c, fmt.Sprintf(_inSelExp, index, xstr.JoinInts(uids)))
if err1 != nil {
err = err1
log.Error(_errorDBLogPrefix+"|MultiExp d.exp.Query err: %v", err)
return
}
for rows.Next() {
ele := &expm.Exp{}
if err = rows.Scan(&ele.UID, &ele.Uexp, &ele.Rexp, &ele.CTime, &ele.MTime); err != nil {
log.Error(_errorDBLogPrefix+"|MultiExp rows.Scan err: %v", err)
return
}
exps = append(exps, ele)
delete(um, ele.UID)
}
}
// 初始化不存在的数据,补齐数据
for uid := range um {
d.InitExp(c, uid, 0, 0)
ele := &expm.Exp{UID: uid, Uexp: 0, Rexp: 0}
exps = append(exps, ele)
}
return
}
// AddUexp 添加用户经验
func (d *Dao) AddUexp(c context.Context, uid int64, uexp int64) (affect int64, err error) {
upSQL := fmt.Sprintf(_addUexp, uid%_shard, uexp)
res, err := d.db.Exec(c, upSQL, uid, uexp)
if err != nil {
log.Error(_errorDBLogPrefix+"|Exec(%s) error(%v)", upSQL, err)
return
}
return res.RowsAffected()
}
// AddRexp 添加主播经验
func (d *Dao) AddRexp(c context.Context, uid int64, rexp int64) (affect int64, err error) {
upSQL := fmt.Sprintf(_addRexp, uid%_shard, rexp)
res, err := d.db.Exec(c, upSQL, uid, rexp)
if err != nil {
log.Error(_errorDBLogPrefix+"|AddRexp|Exec(%s) error(%v)", upSQL, err)
return
}
return res.RowsAffected()
}

View File

@@ -0,0 +1,45 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = [
"cache.go",
"dao.go",
"mysql.go",
"redis.go",
],
importpath = "go-common/app/service/live/xuser/dao/guard",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/service/live/xuser/conf:go_default_library",
"//app/service/live/xuser/model:go_default_library",
"//app/service/live/xuser/model/dhh:go_default_library",
"//library/cache/memcache:go_default_library",
"//library/cache/redis:go_default_library",
"//library/database/sql:go_default_library",
"//library/log:go_default_library",
"//library/stat/prom:go_default_library",
"//library/xstr:go_default_library",
"//vendor/github.com/pkg/errors:go_default_library",
],
)
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,48 @@
package guard
import (
"context"
"fmt"
"go-common/library/cache/redis"
"go-common/library/log"
)
// redis cache
const (
_lockKey = "saveGuard:%s"
_cacheKeyUid = "live_user:guard:uid:v1:%d"
_cacheKeyTargetId = "live_user:guard:target_id:v1:%d"
)
// LockOrder lock for same order
func (d *GuardDao) LockOrder(ctx context.Context, orderID string) (ok bool, err error) {
conn := d.redis.Get(ctx)
defer conn.Close()
_, err = redis.String(conn.Do("SET", fmt.Sprintf(_lockKey, orderID), 1, "EX", 3*86400, "NX"))
if err == redis.ErrNil {
log.Info("LockOrder(%s) is ErrNil!", orderID)
return false, nil
}
if err != nil {
return false, err
}
return true, nil
}
// UnlockOrder release lock for same order
func (d *GuardDao) UnlockOrder(ctx context.Context, orderID string) (err error) {
conn := d.redis.Get(ctx)
defer conn.Close()
_, err = redis.String(conn.Do("DEL", fmt.Sprintf(_lockKey, orderID)))
return
}
// ClearCache delete cache for guard
func (d *GuardDao) ClearCache(ctx context.Context, uid int64, ruid int64) (err error) {
conn := d.redis.Get(ctx)
defer conn.Close()
_, err = redis.String(conn.Do("DEL", fmt.Sprintf(_cacheKeyUid, uid)))
_, err = redis.String(conn.Do("DEL", fmt.Sprintf(_cacheKeyTargetId, ruid)))
return
}

View File

@@ -0,0 +1,47 @@
package guard
import (
"context"
"go-common/app/service/live/xuser/conf"
"go-common/library/cache/redis"
xsql "go-common/library/database/sql"
)
// GuardDao vip dao
type GuardDao struct {
c *conf.Config
db *xsql.DB
redis *redis.Pool
}
// NewGuardDao init mysql db
func NewGuardDao(c *conf.Config) (dao *GuardDao) {
dao = &GuardDao{
c: c,
db: xsql.NewMySQL(c.LiveAppMySQL),
redis: redis.NewPool(c.GuardRedis),
}
return
}
// Close close the resource.
func (d *GuardDao) Close() {
d.db.Close()
d.redis.Close()
}
// Ping dao ping
func (d *GuardDao) Ping(ctx context.Context) error {
// TODO: add mc,redis... if you use
return nil
}
func (d *GuardDao) getExpire() (respExpire int32) {
if t := conf.Conf.UserDaHangHaiExpire; t != nil {
respExpire = t.ExpireTime
} else {
respExpire = _emptyExpire
}
return
}

View File

@@ -0,0 +1,197 @@
package guard
import (
"context"
"fmt"
"go-common/library/xstr"
"time"
confm "go-common/app/service/live/xuser/conf"
"go-common/app/service/live/xuser/model"
dhhm "go-common/app/service/live/xuser/model/dhh"
"go-common/library/log"
"github.com/pkg/errors"
)
const (
_guardTable = "ap_user_privilege"
)
var (
// add guard info
_addGuardInfo = "REPLACE INTO `%s` (`uid`,`target_id`,`privilege_type`,`start_time`,`expired_time`) VALUES(?,?,?,?,?);"
// get guard info
_getGuardInfo = "SELECT `id`,`uid`,`target_id`,`privilege_type`,`start_time`,`expired_time` FROM `%s` WHERE `uid`=? AND `expired_time`>=? ORDER BY `privilege_type` ASC;"
// get guard info
_getGuardInfo2 = "SELECT `id`,`uid`,`target_id`,`privilege_type`,`start_time`,`expired_time` FROM `%s` WHERE `uid`=? AND `target_id`=? AND `expired_time`>=? ORDER BY `privilege_type` ASC;"
// update guard info
_updGuardInfo = "UPDATE `%s` SET `expired_time`=date_add(expired_time, interval ? day) WHERE `uid`=? AND `target_id`=? AND `expired_time`>=? AND `privilege_type`%s?"
// upsert guard info
_upsertGuardInfo = "INSERT INTO `%s` (`uid`,`target_id`,`privilege_type`,`start_time`,`expired_time`) VALUES(?,?,?,?,?) ON DUPLICATE KEY UPDATE `start_time`=?,`expired_time`=?;"
// 查询大航海信息
_selUID = "SELECT id,uid,target_id,privilege_type,start_time,expired_time,ctime,utime FROM ap_user_privilege where uid IN (%s) AND expired_time >= '%s' "
_selAnchorUID = "SELECT id,uid,target_id,privilege_type,start_time,expired_time,ctime,utime FROM ap_user_privilege where target_id IN (%s) AND expired_time >= '%s' "
_errorDBLogPrefix = "xuser.dahanghai.dao.mysql|"
)
// GetByUIDs 批量查询
func (d *GuardDao) GetByUIDs(c context.Context, uids []int64) (dhhs []*dhhm.DHHDB, err error) {
reqStartTime := confm.RecordTimeCost()
dhhs = make([]*dhhm.DHHDB, 0)
tm := time.Now()
timeNow := tm.Format("2006-1-2 15:04:05")
rows, err1 := d.db.Query(c, fmt.Sprintf(_selUID, xstr.JoinInts(uids), timeNow))
if err1 != nil {
reqAfterTime := confm.RecordTimeCost()
err = err1
log.Error(_errorDBLogPrefix+confm.GetFromDHHDBError+"|GetByUIDs err: %v|cost:%dms", err, reqAfterTime-reqStartTime)
return
}
for rows.Next() {
ele := &dhhm.DHHDB{}
if err = rows.Scan(&ele.ID, &ele.UID, &ele.TargetId, &ele.PrivilegeType, &ele.StartTime, &ele.ExpiredTime, &ele.Ctime, &ele.Utime); err != nil {
log.Error(_errorDBLogPrefix+confm.ScanFromDHHDBError+"|GetByUIDs rows.Scan err: %v", err)
return
}
dhhs = append(dhhs, ele)
}
return
}
// GetByUIDsWithMap 批量查询
func (d *GuardDao) GetByUIDsWithMap(c context.Context, uids []int64) (dhhs map[int64][]*dhhm.DHHDB, err error) {
reqStartTime := confm.RecordTimeCost()
dhhs = make(map[int64][]*dhhm.DHHDB)
tm := time.Now()
timeNow := tm.Format("2006-1-2 15:04:05")
rows, err1 := d.db.Query(c, fmt.Sprintf(_selUID, xstr.JoinInts(uids), timeNow))
if err1 != nil {
reqAfterTime := confm.RecordTimeCost()
err = err1
log.Error(_errorDBLogPrefix+confm.GetFromDHHDBError+"|GetByUIDs err: %v|cost:%dms", err, reqAfterTime-reqStartTime)
return
}
for rows.Next() {
ele := &dhhm.DHHDB{}
if err = rows.Scan(&ele.ID, &ele.UID, &ele.TargetId, &ele.PrivilegeType, &ele.StartTime, &ele.ExpiredTime, &ele.Ctime, &ele.Utime); err != nil {
log.Error(_errorDBLogPrefix+confm.ScanFromDHHDBError+"|GetByUIDs rows.Scan err: %v", err)
return
}
if _, exist := dhhs[ele.UID]; !exist {
dhhs[ele.UID] = make([]*dhhm.DHHDB, 0)
}
dhhs[ele.UID] = append(dhhs[ele.UID], ele)
}
return
}
// GetByAnchorUIDs 批量查询
func (d *GuardDao) GetByAnchorUIDs(c context.Context, uids []int64) (dhhs []*dhhm.DHHDB, err error) {
reqStartTime := confm.RecordTimeCost()
dhhs = make([]*dhhm.DHHDB, 0)
tm := time.Now()
timeNow := tm.Format("2006-1-2 15:04:05")
rows, err1 := d.db.Query(c, fmt.Sprintf(_selAnchorUID, xstr.JoinInts(uids), timeNow))
if err1 != nil {
reqAfterTime := confm.RecordTimeCost()
err = err1
log.Error(_errorDBLogPrefix+confm.GetFromDHHDBError+"|GetByUIDs err: %v|cost:%dms", err, reqAfterTime-reqStartTime)
return
}
for rows.Next() {
ele := &dhhm.DHHDB{}
if err = rows.Scan(&ele.ID, &ele.UID, &ele.TargetId, &ele.PrivilegeType, &ele.StartTime, &ele.ExpiredTime, &ele.Ctime, &ele.Utime); err != nil {
log.Error(_errorDBLogPrefix+confm.ScanFromDHHDBError+"|GetByUIDs rows.Scan err: %v", err)
return
}
dhhs = append(dhhs, ele)
}
return
}
// GetGuardByUID get guard info by uid
func (d *GuardDao) GetGuardByUID(ctx context.Context, uid int64) (info []*model.GuardInfo, err error) {
sql := fmt.Sprintf(_getGuardInfo, _guardTable)
rows, err := d.db.Query(ctx, sql, uid, time.Now().Format("2006-01-02 15:04:05"))
if err != nil {
log.Error("[dao.guard.mysql|GetGuardByUID] get user guard record error(%v), uid(%d)", err, uid)
return nil, err
}
defer rows.Close()
for rows.Next() {
var inf model.GuardInfo
err = rows.Scan(&inf.Id, &inf.Uid, &inf.TargetId, &inf.PrivilegeType, &inf.StartTime, &inf.ExpiredTime)
if err != nil {
log.Error("[dao.guard.mysql|GetGuardByUID] scan user guard record error(%v), uid(%d)", err, uid)
return nil, err
}
info = append(info, &inf)
}
return
}
// GetGuardByUIDRuid get guard info by uid and ruid
func (d *GuardDao) GetGuardByUIDRuid(ctx context.Context, uid int64, ruid int64) (info []*model.GuardInfo, err error) {
sql := fmt.Sprintf(_getGuardInfo2, _guardTable)
rows, err := d.db.Query(ctx, sql, uid, ruid, time.Now().Format("2006-01-02 15:04:05"))
if err != nil {
log.Error("[dao.guard.mysql|GetGuardByUIDRuid] get user guard record error(%v), uid(%d), ruid(%d)", err, uid, ruid)
return nil, err
}
defer rows.Close()
for rows.Next() {
var inf model.GuardInfo
err = rows.Scan(&inf.Id, &inf.Uid, &inf.TargetId, &inf.PrivilegeType, &inf.StartTime, &inf.ExpiredTime)
if err != nil {
log.Error("[dao.guard.mysql|GetGuardByUIDRuid] scan user guard record error(%v), uid(%d), ruid(%d)", err, uid, ruid)
return nil, err
}
info = append(info, &inf)
}
return
}
// AddGuard insert guard
func (d *GuardDao) AddGuard(ctx context.Context, req *model.GuardBuy) (err error) {
sql := fmt.Sprintf(_addGuardInfo, _guardTable)
now := time.Now()
endTime := time.Date(now.Year(), now.Month(), now.Day(), int(23), int(59), int(59), int(999), time.Local).AddDate(0, 0, req.Num*30)
res, err := d.db.Exec(ctx, sql, req.Uid, req.Ruid, req.GuardLevel, now.Format("2006-01-02 15:04:05"), endTime.Format("2006-01-02 15:04:05"))
if err != nil {
// unique key exists error
log.Error("[dao.guard.mysql|AddGuard] add user guard record error(%v), req(%v)", err, req)
return
}
if _, err = res.LastInsertId(); err != nil {
err = errors.WithStack(err)
log.Error("[dao.guard.mysql|AddGuard] get last insert id error(%v), req(%v)", err, req)
}
return
}
// UpdateGuard update guard info
func (d *GuardDao) UpdateGuard(ctx context.Context, req *model.GuardBuy, cond string) (err error) {
sql := fmt.Sprintf(_updGuardInfo, _guardTable, cond)
_, err = d.db.Exec(ctx, sql, req.Num*30, req.Uid, req.Ruid, time.Now().Format("2006-01-02 15:04:05"), req.GuardLevel)
if err != nil {
log.Error("[dao.guard.mysql|UpdateGuard] update user guard record error(%v), req(%v)", err, req)
return
}
return
}
// UpsertGuard upsert guard info
func (d *GuardDao) UpsertGuard(ctx context.Context, req *model.GuardBuy, expiredTime string) (err error) {
sql := fmt.Sprintf(_upsertGuardInfo, _guardTable)
now := time.Now()
_, err = d.db.Exec(ctx, sql, req.Uid, req.Ruid, req.GuardLevel, now.Format("2006-01-02 15:04:05"), expiredTime, now.Format("2006-01-02 15:04:05"), expiredTime)
if err != nil {
log.Error("[dao.guard.mysql|UpsertGuard] upsert user guard record error(%v), req(%v)", err, req)
return
}
return
}

View File

@@ -0,0 +1,361 @@
package guard
import (
"context"
"encoding/json"
"github.com/pkg/errors"
dahanghaiModel "go-common/app/service/live/xuser/model/dhh"
gmc "go-common/library/cache/memcache"
"go-common/library/cache/redis"
"go-common/library/log"
"go-common/library/stat/prom"
"math/rand"
"strconv"
"time"
)
// redis cache
const (
_prefixUID = "live_user:guard:uid:v1:" // 用户侧key
_prefixTopList = "GOVERNOR_SHOW_TID:" // 最近购买总督key
_prefixAnchorID = "live_user:guard:target_id:v1:" // 主播侧key
_emptyExpire = 3600
_errorRedisLogPrefix = "xuser.dahanghai.dao.redis"
_promGetSuccess = "xuser_dahanghai_redis:获取用户大航海cache成功"
_promDelSuccess = "xuser_dahanghai_redis:成功删除用户大航海cache"
_promDelErr = "xuser_dahanghai_redis:删除用户大航海cache失败"
_promGetErr = "xuser_dahanghai_redis:批量获取用户大航海key失败"
// _promScanErr = "xuser_dahanghai_redis:解析用户大航海key失败"
)
var (
errorsCount = prom.BusinessErrCount
infosCount = prom.BusinessInfoCount
cacheHitCount = prom.CacheHit
cacheMissCount = prom.CacheMiss
)
// PromError prometheus error count.
func PromError(name string) {
errorsCount.Incr(name)
}
// PromInfo prometheus info count.
func PromInfo(name string) {
infosCount.Incr(name)
}
// PromCacheHit prometheus cache hit count.
func PromCacheHit(name string) {
cacheHitCount.Incr(name)
}
// PromCacheMiss prometheus cache hit count.
func PromCacheMiss(name string) {
cacheMissCount.Incr(name)
}
func dahanghaiUIDKey(mid int64) string {
return _prefixUID + strconv.FormatInt(mid, 10)
}
func guardAnchorUIDKey(mid int64) string {
return _prefixAnchorID + strconv.FormatInt(mid, 10)
}
func recentGuardTopKey(mid int64) string {
return _prefixTopList + strconv.FormatInt(mid, 10)
}
// SetDHHListCache ... 批量设置用户守护cache
func (d *GuardDao) SetDHHListCache(c context.Context, dhhList []dahanghaiModel.DaHangHaiRedis2, uid int64) (err error) {
return d.setDHHListCache(c, dhhList, uid)
}
// SetAnchorGuardListCache ... 批量设置主播维度守护信息cache
func (d *GuardDao) SetAnchorGuardListCache(c context.Context, dhhList []dahanghaiModel.DaHangHaiRedis2, uid int64) (err error) {
return d.setAnchorGuardListCache(c, dhhList, uid)
}
// DelDHHFromRedis 删除获取用户守护cache,不支持批量
func (d *GuardDao) DelDHHFromRedis(c context.Context, mid int64) (err error) {
return d.delDHHFromRedis(c, dahanghaiUIDKey(mid))
}
// GetUIDAllGuardFromRedis 获取单个用户的全量守护信息
func (d *GuardDao) GetUIDAllGuardFromRedis(ctx context.Context, mids []int64) (dhhList []*dahanghaiModel.DaHangHaiRedis2, err error) {
return d.getUIDAllGuardFromRedis(ctx, mids)
}
// GetUIDAllGuardFromRedisBatch 获取批量用户的全量守护信息
func (d *GuardDao) GetUIDAllGuardFromRedisBatch(ctx context.Context, mids []int64) (dhhList []*dahanghaiModel.DaHangHaiRedis2, err error) {
return d.getUIDAllGuardFromRedisBatch(ctx, mids)
}
// GetAnchorAllGuardFromRedis 获取单个主播的全量被守护信息(同一个主播仅获取最高级别)
func (d *GuardDao) GetAnchorAllGuardFromRedis(ctx context.Context, anchorUIDs []int64) (dhhList []*dahanghaiModel.DaHangHaiRedis2, err error) {
return d.getAnchorAllGuardFromRedis(ctx, anchorUIDs)
}
// GetGuardTopListCache 获取单个用户的全量守护信息
func (d *GuardDao) GetGuardTopListCache(ctx context.Context, uid int64) (dhhList []*dahanghaiModel.DaHangHaiRedis2, err error) {
return d.getGuardTopListCache(ctx, uid)
}
// GetAnchorRecentTopGuardCache 获取单个主播最近的总督信息
func (d *GuardDao) GetAnchorRecentTopGuardCache(ctx context.Context, uid int64) (resp map[int64]int64, err error) {
return d.getAnchorRecentTopGuardCache(ctx, uid)
}
func (d *GuardDao) getGuardTopListCache(ctx context.Context, uid int64) (dhhList []*dahanghaiModel.DaHangHaiRedis2, err error) {
return
}
// getUIDAllGuardFromRedis 批量获取用户cache
func (d *GuardDao) getUIDAllGuardFromRedis(ctx context.Context, mids []int64) (dhhList []*dahanghaiModel.DaHangHaiRedis2, err error) {
var (
conn = d.redis.Get(ctx)
args = redis.Args{}
cacheResult [][]byte
)
defer conn.Close()
for _, uid := range mids {
args = args.Add(dahanghaiUIDKey(uid))
}
if cacheResult, err = redis.ByteSlices(conn.Do("MGET", args...)); err != nil {
if err == redis.ErrNil {
err = nil
} else {
PromError(_promGetErr)
log.Error(_errorRedisLogPrefix+"|conn.MGET(%v) error(%v)", args, err)
err = errors.Wrapf(err, "redis.StringMap(conn.Do(MGET,%v)", args)
}
return
}
dhhList = make([]*dahanghaiModel.DaHangHaiRedis2, 0)
dhhListSingle := &dahanghaiModel.DaHangHaiRedis2{}
if len(cacheResult) > 0 {
for k, v := range cacheResult {
if v == nil {
return nil, nil
}
if len(v) > 0 {
if err = json.Unmarshal([]byte(v), &dhhList); err != nil {
if err = json.Unmarshal([]byte(v), &dhhListSingle); err != nil {
log.Error("[dao.dahanghai.cache|GetDHHFromRedis] json.Unmarshal rawInfo error(%v), uid(%d), reply(%s)",
err, k, v)
return nil, nil
}
dhhList = append(dhhList, dhhListSingle)
}
}
}
}
PromInfo(_promGetSuccess)
return
}
// getUIDAllGuardFromRedis 批量获取用户cache
func (d *GuardDao) getUIDAllGuardFromRedisBatch(ctx context.Context, mids []int64) (dhhList []*dahanghaiModel.DaHangHaiRedis2, err error) {
var (
conn = d.redis.Get(ctx)
args = redis.Args{}
cacheResult [][]byte
)
defer conn.Close()
for _, uid := range mids {
args = args.Add(dahanghaiUIDKey(uid))
}
if cacheResult, err = redis.ByteSlices(conn.Do("MGET", args...)); err != nil {
if err == redis.ErrNil {
err = nil
} else {
PromError(_promGetErr)
log.Error(_errorRedisLogPrefix+"|conn.MGET(%v) error(%v)", args, err)
err = errors.Wrapf(err, "redis.StringMap(conn.Do(MGET,%v)", args)
}
return
}
dhhList = make([]*dahanghaiModel.DaHangHaiRedis2, 0)
if len(cacheResult) > 0 {
for k, v := range cacheResult {
if v == nil {
continue
}
dhhListLoop := make([]*dahanghaiModel.DaHangHaiRedis2, 0)
dhhListSingle := &dahanghaiModel.DaHangHaiRedis2{}
if len(v) > 0 {
if err = json.Unmarshal([]byte(v), &dhhListLoop); err != nil {
if err = json.Unmarshal([]byte(v), &dhhListSingle); err != nil {
log.Error("[dao.dahanghai.cache|GetDHHFromRedis] json.Unmarshal rawInfo error(%v), uid(%d), reply(%s)",
err, k, v)
return nil, nil
}
dhhList = append(dhhList, dhhListSingle)
} else {
dhhList = append(dhhList, dhhListLoop...)
}
}
}
}
PromInfo(_promGetSuccess)
return
}
// getAnchorAllGuardFromRedis 批量获取用户cache
func (d *GuardDao) getAnchorAllGuardFromRedis(ctx context.Context, mids []int64) (dhhList []*dahanghaiModel.DaHangHaiRedis2, err error) {
var (
conn = d.redis.Get(ctx)
args = redis.Args{}
cacheResult [][]byte
)
defer conn.Close()
for _, uid := range mids {
args = args.Add(guardAnchorUIDKey(uid))
}
if cacheResult, err = redis.ByteSlices(conn.Do("MGET", args...)); err != nil {
if err == redis.ErrNil {
err = nil
} else {
PromError(_promGetErr)
log.Error(_errorRedisLogPrefix+"|conn.MGET(%v) error(%v)", args, err)
err = errors.Wrapf(err, "redis.StringMap(conn.Do(MGET,%v)", args)
}
return
}
dhhList = make([]*dahanghaiModel.DaHangHaiRedis2, 0)
dhhListSingle := &dahanghaiModel.DaHangHaiRedis2{}
if len(cacheResult) > 0 {
for k, v := range cacheResult {
if v == nil {
return nil, nil
}
if len(v) > 0 {
if err = json.Unmarshal([]byte(v), &dhhList); err != nil {
if err = json.Unmarshal([]byte(v), &dhhListSingle); err != nil {
log.Error("[dao.dahanghai.cache|GetDHHFromRedis] json.Unmarshal rawInfo error(%v), uid(%d), reply(%s)",
err, k, v)
return nil, nil
}
dhhList = append(dhhList, dhhListSingle)
}
}
}
}
PromInfo(_promGetSuccess)
return
}
func (d *GuardDao) delDHHFromRedis(ctx context.Context, key string) (err error) {
conn := d.redis.Get(ctx)
defer conn.Close()
_, err = conn.Do("DEL", key)
if err == gmc.ErrNotFound {
err = nil
} else {
log.Error(_errorRedisLogPrefix+"|Delete(%s) error(%v)", key, err)
PromError(_promDelErr)
}
PromInfo(_promDelSuccess)
conn.Close()
return
}
func (d *GuardDao) setDHHListCache(ctx context.Context, dhhList []dahanghaiModel.DaHangHaiRedis2, uid int64) (err error) {
expire := d.getExpire()
rand.Seed(time.Now().UnixNano())
expire = expire + rand.Int31n(60)
var (
argsMid = redis.Args{}
conn = d.redis.Get(ctx)
dhhJSON []byte
)
defer conn.Close()
key := dahanghaiUIDKey(uid)
if len(dhhList) == 0 {
argsMid = argsMid.Add(key).Add("")
} else {
dhhJSON, err = json.Marshal(dhhList)
if err != nil {
log.Error("[dao.dahanghai.cache|GetDHHFromRedis] json.Marshal rawInfo error(%v), uid(%d)", err, uid)
return
}
argsMid = argsMid.Add(key).Add(string(dhhJSON))
}
if err = conn.Send("SET", argsMid...); err != nil {
err = errors.Wrap(err, "conn.Send(SET) error")
return
}
rand.Seed(time.Now().UnixNano())
expire = expire + rand.Int31n(60)
if err = conn.Send("EXPIRE", key, expire); err != nil {
log.Error("setDHHListCache conn.Send(Expire, %s, %d) error(%v)", key, expire, err)
return
}
return
}
func (d *GuardDao) setAnchorGuardListCache(ctx context.Context, dhhList []dahanghaiModel.DaHangHaiRedis2, uid int64) (err error) {
expire := d.getExpire()
rand.Seed(time.Now().UnixNano())
expire = expire + rand.Int31n(60)
var (
argsMid = redis.Args{}
conn = d.redis.Get(ctx)
dhhJSON []byte
)
defer conn.Close()
key := guardAnchorUIDKey(uid)
if len(dhhList) == 0 {
argsMid = argsMid.Add(key).Add("")
} else {
dhhJSON, err = json.Marshal(dhhList)
if err != nil {
log.Error("[dao.dahanghai.cache|setAnchorGuardListCache] json.Marshal rawInfo error(%v), uid(%d)", err, uid)
return
}
argsMid = argsMid.Add(key).Add(string(dhhJSON))
}
if err = conn.Send("SET", argsMid...); err != nil {
err = errors.Wrap(err, "conn.Send(SET) error")
return
}
rand.Seed(time.Now().UnixNano())
expire = expire + rand.Int31n(60)
if err = conn.Send("EXPIRE", key, expire); err != nil {
log.Error("setAnchorGuardListCache conn.Send(Expire, %s, %d) error(%v)", key, expire, err)
return
}
return
}
func (d *GuardDao) getAnchorRecentTopGuardCache(ctx context.Context, uid int64) (resp map[int64]int64, err error) {
resp = make(map[int64]int64)
nowTime := time.Now().Unix()
cacheKey := recentGuardTopKey(uid)
var (
conn = d.redis.Get(ctx)
)
values, err := redis.Values(conn.Do("ZRANGEBYSCORE", cacheKey, nowTime, "INF", "WITHSCORES"))
if err != nil {
log.Error("getAnchorRecentTopGuardCache.conn.Do(ZRANGEBYSCORE %v) error(%v)", cacheKey, err)
return
}
if len(values) == 0 {
return
}
var aid, unix int64
for len(values) > 0 {
if values, err = redis.Scan(values, &aid, &unix); err != nil {
log.Error("getAnchorRecentTopGuardCache.redis.Scan(%v) error(%v)", values, err)
return
}
resp[aid] = unix
}
return
}

View File

@@ -0,0 +1,74 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_test",
"go_library",
)
go_test(
name = "go_default_test",
srcs = [
"dao_test.go",
"mc.cache_test.go",
"mc_test.go",
"mysql_test.go",
"redis_test.go",
],
embed = [":go_default_library"],
tags = ["automanaged"],
deps = [
"//app/service/live/xuser/conf:go_default_library",
"//app/service/live/xuser/model:go_default_library",
"//vendor/github.com/smartystreets/goconvey/convey:go_default_library",
],
)
go_library(
name = "go_default_library",
srcs = [
"dao.go",
"mc.cache.go",
"mc.go",
"mysql.go",
"redis.go",
],
importpath = "go-common/app/service/live/xuser/dao/roomAdmin",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/service/live/banned_service/api/liverpc/v1:go_default_library",
"//app/service/live/fans_medal/api/liverpc/v2:go_default_library",
"//app/service/live/room/api/liverpc/v1:go_default_library",
"//app/service/live/room/api/liverpc/v2:go_default_library",
"//app/service/live/xuser/api/grpc/v1:go_default_library",
"//app/service/live/xuser/conf:go_default_library",
"//app/service/live/xuser/dao:go_default_library",
"//app/service/live/xuser/model:go_default_library",
"//app/service/main/account/model:go_default_library",
"//app/service/main/account/rpc/client:go_default_library",
"//library/cache/memcache:go_default_library",
"//library/cache/redis:go_default_library",
"//library/database/orm:go_default_library",
"//library/database/sql:go_default_library",
"//library/ecode:go_default_library",
"//library/log:go_default_library",
"//library/net/http/blademaster:go_default_library",
"//library/stat/prom:go_default_library",
"//vendor/github.com/jinzhu/gorm:go_default_library",
],
)
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,933 @@
package roomAdmin
import (
"bytes"
"context"
"encoding/json"
"fmt"
"github.com/jinzhu/gorm"
banned "go-common/app/service/live/banned_service/api/liverpc/v1"
v12 "go-common/app/service/live/fans_medal/api/liverpc/v2"
"go-common/app/service/live/room/api/liverpc/v1"
"go-common/app/service/live/room/api/liverpc/v2"
v1pb "go-common/app/service/live/xuser/api/grpc/v1"
"go-common/app/service/live/xuser/conf"
"go-common/app/service/live/xuser/dao"
"go-common/app/service/live/xuser/model"
account "go-common/app/service/main/account/model"
accrpc "go-common/app/service/main/account/rpc/client"
"go-common/library/cache/memcache"
"go-common/library/cache/redis"
"go-common/library/database/orm"
xsql "go-common/library/database/sql"
"go-common/library/ecode"
"go-common/library/log"
bm "go-common/library/net/http/blademaster"
"io/ioutil"
"math"
"net/http"
"sort"
"strconv"
"time"
)
// Dao dao
type Dao struct {
c *conf.Config
mc *memcache.Pool
redis *redis.Pool
db *xsql.DB
orm *gorm.DB
RoomAdminExpire int32
// acc rpc
acc *accrpc.Service3
client *bm.Client
}
const (
userPrefix = "up_v1_%d"
roomPrefix = "rp_v1_%d"
mcExpire = 3600
maxAdminsNum = 100
)
// KeyUser return the mc key by user mid.
func KeyUser(uid int64) string {
return fmt.Sprintf(userPrefix, uid)
}
// KeyRoom return the mc key by anchor mid.
func KeyRoom(uid int64) string {
return fmt.Sprintf(roomPrefix, uid)
}
//go:generate $GOPATH/src/go-common/app/tool/cache/mc
type _mc interface {
// 获取主播的房管列表
// mc: -key=KeyRoom
CacheRoomAdminRoom(c context.Context, anchor int64) ([]*model.RoomAdmin, error)
// 获取用户的房管列表
// mc: -key=KeyUser
CacheRoomAdminUser(c context.Context, user int64) ([]*model.RoomAdmin, error)
// mc: -key=KeyRoom -expire=d.RoomAdminExpire -encode=json|gzip
AddCacheKeyAnchorRoom(c context.Context, anchor int64, value []*model.RoomAdmin) error
// mc: -key=KeyUser -expire=d.RoomAdminExpire -encode=gob
AddCacheRoomAdminUser(c context.Context, user int64, value []*model.RoomAdmin) error
// mc: -key=KeyRoom
DelCacheKeyAnchorRoom(c context.Context, anchor int64) error
// mc: -key=KeyUser
DelCacheRoomAdminUser(c context.Context, user int64) error
}
// New init mysql db
func New(c *conf.Config) (dao *Dao) {
dao = &Dao{
c: c,
mc: memcache.NewPool(c.Memcache),
redis: redis.NewPool(c.Redis),
db: xsql.NewMySQL(c.LiveAppMySQL),
orm: orm.NewMySQL(c.LiveAppORM),
RoomAdminExpire: mcExpire,
acc: accrpc.New3(c.AccountRPC),
client: bm.NewClient(c.BMClient),
}
return
}
// Close close the resource.
func (d *Dao) Close() {
d.mc.Close()
d.redis.Close()
d.orm.Close()
d.db.Close()
}
// Ping dao ping
func (d *Dao) Ping(c context.Context) error {
// TODO: if you need use mc,redis, please add
return d.db.Ping(c)
}
// HasAnyAdmin whether he has any admin in any room.
func (d *Dao) HasAnyAdmin(c context.Context, uid int64) (int64, error) {
noAdmin := int64(0)
hasAdmin := int64(1)
rst, err := d.GetAllByUid(c, uid)
if nil == rst {
return noAdmin, err
}
return hasAdmin, err
}
// GetByUidPage get admins by uid and page.
func (d *Dao) GetByUidPage(c context.Context, uid int64, page int64, pageSize int64) (resp *v1pb.RoomAdminGetByUidResp, err error) {
resp = &v1pb.RoomAdminGetByUidResp{}
resp.Page = &v1pb.RoomAdminGetByUidResp_Page{
Page: page,
PageSize: pageSize,
TotalPage: 1,
TotalCount: 0,
}
rst, err := d.GetAllByUid(c, uid)
//spew.Dump("GetAllByUid", rst, err)
if err != nil {
return
}
if rst == nil {
return
}
sort.Sort(sort.Reverse(model.RoomAdmins(rst)))
//spew.Dump(rst)
resp.Page.TotalCount = int64(len(rst))
resp.Page.PageSize = pageSize
resp.Page.TotalPage = int64(math.Ceil(float64(len(rst)) / float64(resp.Page.PageSize)))
begin := (page - 1) * pageSize
end := page * pageSize
if page*pageSize > int64(len(rst)) {
end = int64(len(rst))
}
roomUid, mids, err := d.getAnchorUidsFromAdmins(c, rst)
// 没获取到房间信息
if err != nil {
return resp, err
}
// 可能从room获取主播信息不全
if int64(len(mids)) < end {
end = int64(len(mids))
}
if begin > end {
begin = end
}
if err != nil {
return resp, err
}
args := &account.ArgMids{Mids: mids[begin:end]}
accData, err := d.acc.Infos3(c, args)
//spew.Dump("d.acc.Infos3", accData, err)
if err != nil {
log.Error("call account.Infos3(%v) error(%v)", args, err)
return resp, err
}
for _, v := range rst[begin:end] {
item := &v1pb.RoomAdminGetByUidResp_Data{
Uid: v.Uid,
Roomid: v.Roomid,
Ctime: v.Ctime.Time().Format("2006-01-02 15:04:05"),
}
if _, ok := roomUid[v.Roomid]; ok {
item.AnchorId = roomUid[v.Roomid]
if _, ok := accData[item.AnchorId]; ok {
item.AnchorCover = accData[item.AnchorId].Face
item.Uname = accData[item.AnchorId].Name
} else {
log.Error("没有这个人的用户信息 uid(%v) data(%v)", item.AnchorId, accData)
}
} else {
log.Error("没有这个人的房间信息 room (%v) data(%v)", v.Roomid, roomUid)
}
resp.Data = append(resp.Data, item)
}
//spew.Dump("resp.Data", resp.Data)
return
}
// GetAllByUid get by uid.
func (d *Dao) GetAllByUid(c context.Context, uid int64) ([]*model.RoomAdmin, error) {
rstMc, err := d.CacheRoomAdminUser(c, uid)
//spew.Dump("HasAnyAdmin1", rstMc, err)
//spew.Dump("lenMc", len(rstMc))
if err != nil {
return nil, err
}
// 空缓存标识
if rstMc != nil {
if rstMc[0].Id == -1 {
return nil, err
}
return rstMc, err
}
rstDb, err := d.GetByUserMysql(c, uid)
if err != nil {
return nil, err
}
if len(rstDb) == 0 {
d.AddCacheNoneUser(c, uid)
return nil, err
}
d.AddCacheRoomAdminUser(c, uid, rstDb)
return rstDb, err
}
// getAnchorUidsFromAdmins .
// 根据批量房管获取对应主播的房间号和UID
func (d *Dao) getAnchorUidsFromAdmins(c context.Context, admins []*model.RoomAdmin) (roomUid map[int64]int64, uids []int64, err error) {
var roomIds []int64
roomUid = make(map[int64]int64)
for _, a := range admins {
roomIds = append(roomIds, a.Roomid)
}
if len(roomIds) == 0 {
return
}
reply, err := dao.RoomAPI.V2Room.GetByIds(c, &v2.RoomGetByIdsReq{Ids: roomIds})
if err != nil {
log.Error("dao.RoomAPI.V2Room.GetByIds (%v) error(%v)", roomIds, err)
return roomUid, uids, err
}
if reply.GetCode() != 0 {
err = ecode.Int(int(reply.GetCode()))
log.Error("dao.RoomAPI.V2Room.GetByIds (%v) error code(%v)", roomIds, err)
return roomUid, uids, err
}
for aRoomId, r := range reply.Data {
roomUid[aRoomId] = r.Uid
uids = append(uids, r.Uid)
}
return
}
// Del delete a roomadmin
func (d *Dao) Del(c context.Context, uid int64, roomId int64) (err error) {
if err = d.DelAllCache(c, uid, roomId); err != nil {
log.Error("DelAllCache(%v) uid (%v) roomid (%v) error(%v)", uid, roomId, err)
return
}
admin, err := d.GetByRoomIdUidMysql(c, uid, roomId)
if len(admin) == 0 {
log.Error("GetByRoomIdUidMysql empty uid(%v) roomId (%v)error(%v)", uid, roomId, err)
return
}
//spew.Dump("GetByRoomIdUidMysql", admin)
if err = d.DelDbAdminMysql(c, admin[0].Id); err != nil {
log.Error("DelCacheRoomAdminUser uid(%v) roomId (%v) error(%v)", uid, roomId, err)
return err
}
return
}
func (d *Dao) getInfoByName(c context.Context, name string) (userInfo *account.Info, err error) {
userInfo = &account.Info{}
infosByName, err := d.acc.InfosByName3(c, &account.ArgNames{
Names: []string{name},
})
if err != nil {
log.Error("d.acc.InfosByName3(%v) error(%v)", name, err)
return userInfo, err
}
log.Info("d.acc.InfosByName3(%v) return (%v)", name, infosByName)
if len(infosByName) != 0 {
for _, info := range infosByName {
return info, err
}
}
return
}
// SearchForAdmin search user list by keyword.
func (d *Dao) SearchForAdmin(c context.Context, keyword string, anchorId int64) (resp []*v1pb.RoomAdminSearchForAdminResp_Data, err error) {
isUid := 0
matchUid, _ := strconv.ParseInt(keyword, 10, 64)
if matchUid != 0 && keyword != "0" {
isUid = 1
}
// get by name
infoMatchName, err := d.getInfoByName(c, keyword)
if err != nil {
return resp, err
}
if nil != infoMatchName && infoMatchName.Mid != 0 {
log.Info("SearchForAdmin infoMatchName keyword (%v) ret (%v)", keyword, infoMatchName)
isAdminName, _ := d.isAdminByUid(c, infoMatchName.Mid, anchorId)
medalData, errMedal := d.getMedalInfoByUids(c, []int64{infoMatchName.Mid})
if errMedal != nil {
return resp, errMedal
}
itemName := &v1pb.RoomAdminSearchForAdminResp_Data{
Uid: infoMatchName.Mid,
IsAdmin: isAdminName,
Uname: infoMatchName.Name,
Face: infoMatchName.Face,
}
if _, ok := medalData[infoMatchName.Mid]; ok {
itemName.MedalName = medalData[infoMatchName.Mid].MedalName
itemName.Level = medalData[infoMatchName.Mid].Level
} else {
log.Info("没有这个人的勋章信息 uid(%v) data(%v)", infoMatchName.Mid, medalData)
}
resp = append(resp, itemName)
}
//spew.Dump("searchForadmin2", resp)
// just name
if 0 == isUid {
return resp, nil
}
// get by uid
infoMatchUid, err := d.getInfoByUid(c, matchUid)
if err != nil {
return resp, err
}
if infoMatchUid == nil {
return resp, nil
}
isAdminUid, _ := d.isAdminByUid(c, matchUid, anchorId)
medalDataUid, err := d.getMedalInfoByUids(c, []int64{infoMatchUid.Mid})
if err != nil {
return resp, err
}
itemUid := &v1pb.RoomAdminSearchForAdminResp_Data{
Uid: infoMatchUid.Mid,
IsAdmin: isAdminUid,
Uname: infoMatchUid.Name,
Face: infoMatchUid.Face,
}
if _, ok := medalDataUid[infoMatchUid.Mid]; ok {
itemUid.MedalName = medalDataUid[infoMatchUid.Mid].MedalName
itemUid.Level = medalDataUid[infoMatchUid.Mid].Level
}
resp = append(resp, itemUid)
//spew.Dump("searchForadmin2", resp)
return
}
func (d *Dao) isAdminByUid(c context.Context, uid int64, anchorId int64) (rst int64, err error) {
roomId, err := d.getRoomIdByUid(c, anchorId)
if err != nil {
return rst, err
}
if 0 == roomId {
return rst, nil
}
return d.IsAdminByRoomId(c, uid, roomId)
}
func (d *Dao) getInfoByUid(c context.Context, uid int64) (info *account.Info, err error) {
info, err = d.acc.Info3(c, &account.ArgMid{
Mid: uid,
})
if err != nil {
log.Error("d.acc.Info3(%v) error(%v)", uid, err)
return info, err
}
log.Info("d.acc.Info3(%v) return (%v)", uid, info)
return
}
func (d *Dao) getRoomInfoByUid(c context.Context, uid int64) (roomInfo *v1.RoomGetStatusInfoByUidsResp_RoomInfo, err error) {
roomInfo = &v1.RoomGetStatusInfoByUidsResp_RoomInfo{}
reply, err := dao.RoomAPI.V1Room.GetStatusInfoByUids(c, &v1.RoomGetStatusInfoByUidsReq{
Uids: []int64{uid},
ShowHidden: 1,
})
if err != nil {
log.Error("dao.RoomAPI.V1Room.GetStatusInfoByUids (%v) error(%v)", uid, err)
return roomInfo, err
}
if reply.GetCode() != 0 {
err = ecode.Int(int(reply.GetCode()))
log.Error("dao.RoomAPI.V2Room.GetByIds (%v) error code(%v)", uid, err)
return roomInfo, err
}
if len(reply.Data) == 0 {
return
}
for aUid, aInfo := range reply.Data {
if aUid == uid {
return aInfo, nil
}
}
return
}
func (d *Dao) getMedalInfoByUids(c context.Context, uids []int64) (medalInfo map[int64]*v12.AnchorQueryLiveWearingResp_Medal, err error) {
medalInfo = make(map[int64]*v12.AnchorQueryLiveWearingResp_Medal)
reply, err := dao.FansMedalAPI.V2Anchor.QueryLiveWearing(c, &v12.AnchorQueryLiveWearingReq{
UidList: uids,
})
log.Info("call dao.FansMedalAPI.V2Anchor.QueryLiveWearing (%v) rst (%v)", uids, reply)
if err != nil {
log.Error("dao.FansMedalAPI.V2Anchor.QueryLiveWearing (%v) error(%v)", uids, err)
return medalInfo, err
}
if reply.GetCode() != 0 {
err = ecode.Int(int(reply.GetCode()))
log.Error("dao.RoomAPI.V2Room.GetByIds (%v) error code(%v)", uids, err)
return medalInfo, err
}
if len(reply.Data) == 0 {
return
}
return reply.Data, err
}
// IsAdminByRoomId ...
func (d *Dao) IsAdminByRoomId(c context.Context, uid int64, roomId int64) (rst int64, err error) {
rst = 0
admins, err := d.GetAllByRoomId(c, roomId)
if err != nil {
log.Error("GetAllByRoomId(%v) error(%v)", roomId, err)
return rst, err
}
if len(admins) == 0 {
return rst, nil
}
for _, v := range admins {
if v.Uid == uid {
rst = 1
return rst, nil
}
}
return
}
// GetByAnchorIdPage get by anchor id and page .
func (d *Dao) GetByAnchorIdPage(c context.Context, anchorId int64, page int64, pageSize int64) (resp *v1pb.RoomAdminGetByAnchorResp, err error) {
resp = &v1pb.RoomAdminGetByAnchorResp{}
resp.Page = &v1pb.RoomAdminGetByAnchorResp_Page{
Page: page,
PageSize: pageSize,
TotalPage: 1,
TotalCount: 0,
}
roomId, err := d.getRoomIdByUid(c, anchorId)
if err != nil {
return resp, err
}
if 0 == roomId {
return resp, nil
}
allAdmins, err := d.GetAllByRoomId(c, roomId)
//spew.Dump("GetAllByUid", allAdmins, err)
if err != nil {
return resp, err
}
if allAdmins == nil {
return resp, nil
}
sort.Sort(sort.Reverse(model.RoomAdmins(allAdmins)))
//spew.Dump(allAdmins)
resp.Page.TotalCount = int64(len(allAdmins))
resp.Page.PageSize = pageSize
resp.Page.TotalPage = int64(math.Ceil(float64(len(allAdmins)) / float64(resp.Page.PageSize)))
begin := (page - 1) * pageSize
end := page * pageSize
if page*pageSize > int64(len(allAdmins)) {
end = int64(len(allAdmins))
}
uids, _ := d.getUidsFromAdmins(c, allAdmins)
if int64(len(uids)) < end {
end = int64(len(uids))
}
if begin > end {
begin = end
}
//spew.Dump("getAnchorUidsFromAdmins", uids, err)
accArgs := &account.ArgMids{Mids: uids[begin:end]}
accData, err := d.acc.Infos3(c, accArgs)
if err != nil {
log.Error("d.acc.Infos3(%v) error(%v)", accArgs, err)
}
medalData, err := d.getMedalInfoByUids(c, uids)
if err != nil {
log.Error("d.getMedalInfoByUids(%v) error(%v)", uids, err)
return resp, err
}
if err != nil {
log.Error("call account.Infos3(%v) error(%v)", accArgs, err)
return resp, err
}
for _, v := range allAdmins[begin:end] {
item := &v1pb.RoomAdminGetByAnchorResp_Data{
Uid: v.Uid,
Ctime: v.Ctime.Time().Format("2006-01-02 15:04:05"),
Roomid: v.Roomid,
}
if _, ok := accData[v.Uid]; ok {
item.Uname = accData[v.Uid].Name
item.Face = accData[v.Uid].Face
} else {
log.Error("没有这个人的用户信息 uid(%v) data(%v)", v.Uid, accData)
}
if _, ok := medalData[v.Uid]; ok {
item.Level = medalData[v.Uid].Level
item.MedalName = medalData[v.Uid].MedalName
} else {
log.Info("没有这个人的勋章信息 uid(%v) data(%v)", v.Uid, medalData)
}
resp.Data = append(resp.Data, item)
}
//spew.Dump("resp.Data", resp.Data)
return
}
// GetAllByRoomId get by uid.
func (d *Dao) GetAllByRoomId(c context.Context, roomId int64) ([]*model.RoomAdmin, error) {
rstMc, err := d.CacheRoomAdminRoom(c, roomId)
//spew.Dump("HasAnyAdmin1", rstMc, err)
//spew.Dump("lenMc", len(rstMc))
if err != nil {
return nil, err
}
// 空缓存标识
if rstMc != nil {
if rstMc[0].Id == -1 {
return nil, err
}
return rstMc, err
}
rstDb, err := d.GetByRoomIdMysql(c, roomId)
if err != nil {
return nil, err
}
if len(rstDb) == 0 {
d.AddCacheNoneRoom(c, roomId)
return nil, err
}
d.AddCacheKeyAnchorRoom(c, roomId, rstDb)
return rstDb, err
}
// getUidsFromAdmins .
// 返回房管列表中的uid
func (d *Dao) getUidsFromAdmins(c context.Context, admins []*model.RoomAdmin) (uids []int64, err error) {
for _, a := range admins {
uids = append(uids, a.Uid)
}
return
}
// DismissAnchor del a admin
func (d *Dao) DismissAnchor(c context.Context, uid int64, anchorId int64) (resp *v1pb.RoomAdminDismissAdminResp, err error) {
resp = &v1pb.RoomAdminDismissAdminResp{}
roomId, err := d.getRoomIdByUid(c, anchorId)
if err != nil {
return resp, err
}
if 0 == roomId {
return resp, nil
}
isAdmin, err := d.IsAdminByRoomId(c, uid, roomId)
if err != nil {
log.Error("IsAdminByRoomId uid(%v) roomid (%v) error(%v)", uid, roomId, err)
return resp, err
}
if 0 == isAdmin {
err = ecode.Error(ecode.XUserAddRoomAdminNotAdminError, "该用户已经不是房管啦")
return
}
err = d.Del(c, uid, roomId)
if err != nil {
log.Error("getRoomInfoByUid uid (%v) roomid (%v) error(%v)", uid, roomId, err)
return
}
return
}
// DelAllCache delete cache .
func (d *Dao) DelAllCache(c context.Context, uid int64, roomId int64) (err error) {
if err = d.DelCacheKeyAnchorRoom(c, roomId); err != nil {
log.Error("DelCacheKeyAnchorRoom(%v) error(%v)", roomId, err)
return err
}
if err = d.DelCacheRoomAdminUser(c, uid); err != nil {
log.Error("DelCacheRoomAdminUser(%v) error(%v)", uid, err)
return err
}
return
}
// Add add a admin
func (d *Dao) Add(c context.Context, uid int64, anchorId int64) (resp *v1pb.RoomAdminAddResp, err error) {
resp = &v1pb.RoomAdminAddResp{}
roomId, err := d.getRoomIdByUid(c, anchorId)
if err != nil {
return resp, err
}
if 0 == roomId {
return resp, nil
}
allRoomAdmin, err := d.GetAllByRoomId(c, roomId)
//spew.Dump("Add", roomId, allRoomAdmin)
if err != nil {
return resp, err
}
if len(allRoomAdmin) >= maxAdminsNum {
err = ecode.Error(ecode.XUserAddRoomAdminOverLimitError, "最多设置100个房间管理员")
//err = &pb.Error{
// ErrCode: 2,
// ErrMessage: "最多设置100个房间管理员",
//}
return
}
isAdmin, err := d.IsAdminByRoomId(c, uid, roomId)
if err != nil {
log.Error("IsAdminByRoomId uid(%v) roomid (%v) error(%v)", uid, roomId, err)
return resp, err
}
if 1 == isAdmin {
err = ecode.Error(ecode.XUserAddRoomAdminIsAdminError, "该用户已经是你的房管啦")
//err = &pb.Error{
// ErrCode: 1,
// ErrMessage: "他已经是房管",
//}
return
}
banArg := &banned.SilentMngIsBlockUserReq{
Uid: uid,
Roomid: roomId,
Type: 1,
}
retBan, err := dao.BannedAPI.V1SilentMng.IsBlockUser(c, banArg)
if err != nil {
log.Error("call dao.BannedAPI.V1SilentMng.IsBlockUser(%v) error(%v)", banArg, err)
return
}
if retBan.Code != 0 || nil == retBan.Data {
log.Error("call dao.BannedAPI.V1SilentMng.IsBlockUser(%v) error return (%v)", banArg, retBan)
}
if retBan.Data.GetIsBlockUser() {
err = ecode.Error(ecode.XUserAddRoomAdminIsSilentError, "他已经被禁言,无法添加房管")
//err = &pb.Error{
// ErrCode: 3,
// ErrMessage: "他已经被禁言,无法添加房管",
//}
return
}
if err = d.DelAllCache(c, uid, roomId); err != nil {
log.Error("DelCacheKeyAnchorRoom(%v) error(%v)", roomId, err)
return resp, err
}
if err = d.AddAdminMysql(c, uid, roomId); err != nil {
log.Error("DelCacheKeyAnchorRoom(%v) error(%v)", roomId, err)
return
}
resp.Uid = uid
resp.Roomid = roomId
resp.Userinfo = &v1pb.RoomAdminAddResp_UI{}
resp.Userinfo.Uid = uid
info, _ := d.getInfoByUid(c, uid)
if info != nil {
resp.Userinfo.Uname = info.Name
}
d.adminChange(c, uid, roomId)
return
}
func (d *Dao) getRoomIdByUid(c context.Context, anchorId int64) (roomId int64, err error) {
roomInfo, err := d.getRoomInfoByUid(c, anchorId)
if err != nil {
log.Error("getRoomInfoByUid (%v) error(%v)", anchorId, err)
return roomId, err
}
if roomInfo == nil {
return roomId, nil
}
return roomInfo.RoomId, nil
}
// adminChange send broadcast
func (d *Dao) adminChange(c context.Context, uid int64, roomId int64) (err error) {
roomInfo, err := dao.RoomAPI.V2Room.GetByIds(c, &v2.RoomGetByIdsReq{
Ids: []int64{roomId},
NeedUinfo: 1,
})
if err != nil {
log.Error("dao.RoomAPI.V2Room.GetByIds(%v) error(%v)", roomId, err)
return
}
if roomInfo.Code != 0 || 0 == len(roomInfo.Data) {
log.Error("dao.RoomAPI.V1Room.GetInfoById(%v) error code (%v) data (%v)", roomId, roomInfo.Code, roomInfo.Data)
return
}
postJson := make(map[string]interface{})
postJson["cmd"] = "room_admin_entrance"
postJson["uid"] = uid
postJson["msg"] = "系统提示:你已被主播设为房管"
if err = d.sendBroadcastRoom(roomId, postJson); err != nil {
return err
}
admins, err := d.GetAllByRoomId(c, roomId)
if err != nil {
return err
}
var adminUids []int64
// adminUids := make([]int64, 100)
for _, v := range admins {
adminUids = append(adminUids, v.Uid)
}
postJson2 := make(map[string]interface{})
postJson2["cmd"] = "ROOM_ADMINS"
postJson2["uids"] = adminUids
if err = d.sendBroadcastRoom(roomId, postJson2); err != nil {
return err
}
return
}
// sendBroadcastRoom .
func (d *Dao) sendBroadcastRoom(roomid int64, postJson map[string]interface{}) (err error) {
log.Info("send reward broadcast begin:%d", roomid)
var endPoint = fmt.Sprintf("http://live-dm.bilibili.co/dm/1/push?cid=%d&ensure=1", roomid)
bytesData, err := json.Marshal(postJson)
if err != nil {
log.Error("json.Marshal(%v) error(%v)", postJson, err)
return
}
req, err := http.NewRequest("POST", endPoint, bytes.NewReader(bytesData))
req.Header.Add("Content-Type", "application/json")
if err != nil {
log.Error("http.NewRequest(%v) url(%v) error(%v)", postJson, endPoint, err)
return
}
client := http.Client{
Timeout: time.Second,
}
// use httpClient to send request
response, err := client.Do(req)
if err != nil {
log.Error("sending request to API endpoint(%v) error(%v)", req, err)
return
}
body, err := ioutil.ReadAll(response.Body)
if err != nil {
log.Error("parse resp body(%v) error(%v)", body, err)
}
log.Info("send reward broadcast end:%d", roomid)
return
}
// IsAdmin return whether a user is admin.
func (d *Dao) IsAdmin(c context.Context, uid int64, anchorId int64, roomId int64) (resp *v1pb.RoomAdminIsAdminResp, err error) {
resp = &v1pb.RoomAdminIsAdminResp{}
isAdmin, err := d.IsAdminByRoomId(c, uid, roomId)
if err != nil {
log.Error("IsAdminByRoomId uid(%v) roomid (%v) error(%v)", uid, roomId, err)
return resp, err
}
if 0 == isAdmin {
if uid != anchorId {
err = ecode.Error(120014, "Ta不是该主播的房管")
//err = ecode.Error{
// ErrCode: 120014, // 接口迁移, code 为老业务error code
// ErrMessage: "Ta不是该主播的房管",
//}
return
}
}
userInfo, err := d.getInfoByUid(c, uid)
if err != nil {
return resp, err
}
if userInfo != nil {
resp.Userinfo = &v1pb.RoomAdminIsAdminResp_UI{}
resp.Roomid = roomId
resp.Uid = uid
resp.Userinfo.Uid = uid
resp.Userinfo.Uname = userInfo.Name
return
}
return
}

View File

@@ -0,0 +1,34 @@
package roomAdmin
import (
"flag"
"go-common/app/service/live/xuser/conf"
"os"
"testing"
)
var (
d *Dao
)
func TestMain(m *testing.M) {
if os.Getenv("DEPLOY_ENV") != "" {
flag.Set("app_id", "live.xuser")
flag.Set("conf_token", "")
flag.Set("tree_id", "")
flag.Set("conf_version", "docker-1")
flag.Set("deploy_env", "uat")
flag.Set("conf_host", "config.bilibili.co")
flag.Set("conf_path", "/tmp")
flag.Set("region", "sh")
flag.Set("zone", "sh001")
} else {
flag.Set("conf", "../../cmd/test.toml")
}
flag.Parse()
if err := conf.Init(); err != nil {
panic(err)
}
d = New(conf.Conf)
os.Exit(m.Run())
}

View File

@@ -0,0 +1,156 @@
// Code generated by $GOPATH/src/go-common/app/tool/cache/mc. DO NOT EDIT.
/*
Package roomAdmin is a generated mc cache package.
It is generated from:
type _mc interface {
// 获取主播的房管列表
// mc: -key=KeyRoom
CacheRoomAdminRoom(c context.Context, anchor int64) ([]*model.RoomAdmin, error)
// 获取用户的房管列表
// mc: -key=KeyUser
CacheRoomAdminUser(c context.Context, user int64) ([]*model.RoomAdmin, error)
// mc: -key=KeyRoom -expire=d.RoomAdminExpire -encode=json|gzip
AddCacheKeyAnchorRoom(c context.Context, anchor int64, value []*model.RoomAdmin) error
// mc: -key=KeyUser -expire=d.RoomAdminExpire -encode=gob
AddCacheRoomAdminUser(c context.Context, user int64, value []*model.RoomAdmin) error
// mc: -key=KeyRoom
DelCacheKeyAnchorRoom(c context.Context, anchor int64) error
// mc: -key=KeyUser
DelCacheRoomAdminUser(c context.Context, user int64) error
}
*/
package roomAdmin
import (
"context"
"fmt"
"go-common/app/service/live/xuser/model"
"go-common/library/cache/memcache"
"go-common/library/log"
"go-common/library/stat/prom"
)
var _ _mc
// CacheRoomAdminRoom 获取主播的房管列表
func (d *Dao) CacheRoomAdminRoom(c context.Context, id int64) (res []*model.RoomAdmin, err error) {
conn := d.mc.Get(c)
defer conn.Close()
key := KeyRoom(id)
reply, err := conn.Get(key)
if err != nil {
if err == memcache.ErrNotFound {
err = nil
return
}
prom.BusinessErrCount.Incr("mc:CacheRoomAdminRoom")
log.Errorv(c, log.KV("CacheRoomAdminRoom", fmt.Sprintf("%+v", err)), log.KV("key", key))
return
}
res = []*model.RoomAdmin{}
err = conn.Scan(reply, &res)
if err != nil {
prom.BusinessErrCount.Incr("mc:CacheRoomAdminRoom")
log.Errorv(c, log.KV("CacheRoomAdminRoom", fmt.Sprintf("%+v", err)), log.KV("key", key))
return
}
return
}
// CacheRoomAdminUser 获取用户的房管列表
func (d *Dao) CacheRoomAdminUser(c context.Context, id int64) (res []*model.RoomAdmin, err error) {
conn := d.mc.Get(c)
defer conn.Close()
key := KeyUser(id)
reply, err := conn.Get(key)
if err != nil {
if err == memcache.ErrNotFound {
err = nil
return
}
prom.BusinessErrCount.Incr("mc:CacheRoomAdminUser")
log.Errorv(c, log.KV("CacheRoomAdminUser", fmt.Sprintf("%+v", err)), log.KV("key", key))
return
}
res = []*model.RoomAdmin{}
err = conn.Scan(reply, &res)
if err != nil {
prom.BusinessErrCount.Incr("mc:CacheRoomAdminUser")
log.Errorv(c, log.KV("CacheRoomAdminUser", fmt.Sprintf("%+v", err)), log.KV("key", key))
return
}
return
}
// AddCacheKeyAnchorRoom Set data to mc
func (d *Dao) AddCacheKeyAnchorRoom(c context.Context, id int64, val []*model.RoomAdmin) (err error) {
if len(val) == 0 {
return
}
conn := d.mc.Get(c)
defer conn.Close()
key := KeyRoom(id)
item := &memcache.Item{Key: key, Object: val, Expiration: d.RoomAdminExpire, Flags: memcache.FlagJSON | memcache.FlagGzip}
if err = conn.Set(item); err != nil {
prom.BusinessErrCount.Incr("mc:AddCacheKeyAnchorRoom")
log.Errorv(c, log.KV("AddCacheKeyAnchorRoom", fmt.Sprintf("%+v", err)), log.KV("key", key))
return
}
return
}
// AddCacheRoomAdminUser Set data to mc
func (d *Dao) AddCacheRoomAdminUser(c context.Context, id int64, val []*model.RoomAdmin) (err error) {
if len(val) == 0 {
return
}
conn := d.mc.Get(c)
defer conn.Close()
key := KeyUser(id)
item := &memcache.Item{Key: key, Object: val, Expiration: d.RoomAdminExpire, Flags: memcache.FlagGOB}
if err = conn.Set(item); err != nil {
prom.BusinessErrCount.Incr("mc:AddCacheRoomAdminUser")
log.Errorv(c, log.KV("AddCacheRoomAdminUser", fmt.Sprintf("%+v", err)), log.KV("key", key))
return
}
return
}
// DelCacheKeyAnchorRoom delete data from mc
func (d *Dao) DelCacheKeyAnchorRoom(c context.Context, id int64) (err error) {
conn := d.mc.Get(c)
defer conn.Close()
key := KeyRoom(id)
if err = conn.Delete(key); err != nil {
if err == memcache.ErrNotFound {
err = nil
return
}
prom.BusinessErrCount.Incr("mc:DelCacheKeyAnchorRoom")
log.Errorv(c, log.KV("DelCacheKeyAnchorRoom", fmt.Sprintf("%+v", err)), log.KV("key", key))
return
}
return
}
// DelCacheRoomAdminUser delete data from mc
func (d *Dao) DelCacheRoomAdminUser(c context.Context, id int64) (err error) {
conn := d.mc.Get(c)
defer conn.Close()
key := KeyUser(id)
if err = conn.Delete(key); err != nil {
if err == memcache.ErrNotFound {
err = nil
return
}
prom.BusinessErrCount.Incr("mc:DelCacheRoomAdminUser")
log.Errorv(c, log.KV("DelCacheRoomAdminUser", fmt.Sprintf("%+v", err)), log.KV("key", key))
return
}
return
}

View File

@@ -0,0 +1,103 @@
package roomAdmin
import (
"context"
"go-common/app/service/live/xuser/model"
"testing"
"github.com/smartystreets/goconvey/convey"
)
func TestRoomAdminCacheRoomAdminRoom(t *testing.T) {
convey.Convey("CacheRoomAdminRoom", t, func(ctx convey.C) {
var (
c = context.Background()
id = int64(0)
)
ctx.Convey("When everything gose positive", func(ctx convey.C) {
res, err := d.CacheRoomAdminRoom(c, id)
ctx.Convey("Then err should be nil.res should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(res, convey.ShouldNotBeNil)
})
})
})
}
func TestRoomAdminCacheRoomAdminUser(t *testing.T) {
convey.Convey("CacheRoomAdminUser", t, func(ctx convey.C) {
var (
c = context.Background()
id = int64(0)
)
ctx.Convey("When everything gose positive", func(ctx convey.C) {
res, err := d.CacheRoomAdminUser(c, id)
ctx.Convey("Then err should be nil.res should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(res, convey.ShouldNotBeNil)
})
})
})
}
func TestRoomAdminAddCacheKeyAnchorRoom(t *testing.T) {
convey.Convey("AddCacheKeyAnchorRoom", t, func(ctx convey.C) {
var (
c = context.Background()
id = int64(0)
val = []*model.RoomAdmin{}
)
ctx.Convey("When everything gose positive", func(ctx convey.C) {
err := d.AddCacheKeyAnchorRoom(c, id, val)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestRoomAdminAddCacheRoomAdminUser(t *testing.T) {
convey.Convey("AddCacheRoomAdminUser", t, func(ctx convey.C) {
var (
c = context.Background()
id = int64(0)
val = []*model.RoomAdmin{}
)
ctx.Convey("When everything gose positive", func(ctx convey.C) {
err := d.AddCacheRoomAdminUser(c, id, val)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestRoomAdminDelCacheKeyAnchorRoom(t *testing.T) {
convey.Convey("DelCacheKeyAnchorRoom", t, func(ctx convey.C) {
var (
c = context.Background()
id = int64(0)
)
ctx.Convey("When everything gose positive", func(ctx convey.C) {
err := d.DelCacheKeyAnchorRoom(c, id)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestRoomAdminDelCacheRoomAdminUser(t *testing.T) {
convey.Convey("DelCacheRoomAdminUser", t, func(ctx convey.C) {
var (
c = context.Background()
id = int64(0)
)
ctx.Convey("When everything gose positive", func(ctx convey.C) {
err := d.DelCacheRoomAdminUser(c, id)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}

View File

@@ -0,0 +1,105 @@
package roomAdmin
import (
"context"
"fmt"
"go-common/app/service/live/xuser/model"
"go-common/library/cache/memcache"
"go-common/library/log"
"go-common/library/stat/prom"
)
// AddCacheNoneUser write an flag in cache represents empty
func (d *Dao) AddCacheNoneUser(c context.Context, uid int64) (err error) {
conn := d.mc.Get(c)
defer conn.Close()
key := KeyUser(uid)
var roomAdmins []model.RoomAdmin
roomAdmins = append(roomAdmins, model.RoomAdmin{
Id: -1,
})
item := &memcache.Item{Key: key, Object: roomAdmins, Expiration: d.RoomAdminExpire, Flags: memcache.FlagJSON | memcache.FlagGzip}
if err = conn.Set(item); err != nil {
prom.BusinessErrCount.Incr("mc:AddCacheRoomAdminAnchor")
log.Errorv(c, log.KV("AddCacheRoomAdminAnchor", fmt.Sprintf("%+v", err)), log.KV("key", key))
return
}
return
}
// AddCacheNoneRoom write an flag in cache represents empty
func (d *Dao) AddCacheNoneRoom(c context.Context, uid int64) (err error) {
conn := d.mc.Get(c)
defer conn.Close()
key := KeyRoom(uid)
var roomAdmins []model.RoomAdmin
roomAdmins = append(roomAdmins, model.RoomAdmin{
Id: -1,
})
item := &memcache.Item{Key: key, Object: roomAdmins, Expiration: d.RoomAdminExpire, Flags: memcache.FlagJSON | memcache.FlagGzip}
if err = conn.Set(item); err != nil {
prom.BusinessErrCount.Incr("mc:AddCacheRoomAdminAnchor")
log.Errorv(c, log.KV("AddCacheRoomAdminAnchor", fmt.Sprintf("%+v", err)), log.KV("key", key))
return
}
return
}
//
//// GetByUserMC .
//func (d *Dao) GetByUserMC(c context.Context, uid int64) (rst []*model.RoomAdmin, hit int64, err error) {
// hit = 0
// key := d.getUserKey(uid)
// conn := d.mc.Get(c)
// defer conn.Close()
//
// reply, err := conn.Get(key)
// //spew.Dump("GetByUserMC1", reply, err)
//
// if err != nil {
// if err == memcache.ErrNotFound {
// err = nil
// return
// }
// log.Error("GetByUserMC get (%v) error(%v)", key, err)
// return
// }
//
// hit = 1
//
// if reply.Object == nil {
// return nil, hit, err
// }
//
// if err = conn.Scan(reply, rst); err != nil {
// log.Error("GetByUserMC Scan (%+v) error(%v)", reply, err)
// }
//
// //spew.Dump("GetByUserMC2", rst, err)
// return
//}
//
//// SetByUserMc .
//func (d *Dao) SetByUserMc(c context.Context, uid int64, rst []*model.RoomAdmin) (err error) {
// key := d.getUserKey(uid)
// conn := d.mc.Get(c)
// defer conn.Close()
//
// err = conn.Set(&memcache.Item{
// Key: key,
// Object: rst,
// Flags: memcache.FlagJSON,
// Expiration: mcExpire,
// })
//
// //spew.Dump("SetByUserMc", err)
// if err != nil {
// log.Error("SetByUserMc set(%v) value (%+v) error (%v)", key, rst, err)
// return
// }
// return
//}

View File

@@ -0,0 +1,38 @@
package roomAdmin
import (
"context"
"testing"
"github.com/smartystreets/goconvey/convey"
)
func TestRoomAdminAddCacheNoneUser(t *testing.T) {
convey.Convey("AddCacheNoneUser", t, func(ctx convey.C) {
var (
c = context.Background()
uid = int64(0)
)
ctx.Convey("When everything gose positive", func(ctx convey.C) {
err := d.AddCacheNoneUser(c, uid)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestRoomAdminAddCacheNoneRoom(t *testing.T) {
convey.Convey("AddCacheNoneRoom", t, func(ctx convey.C) {
var (
c = context.Background()
uid = int64(0)
)
ctx.Convey("When everything gose positive", func(ctx convey.C) {
err := d.AddCacheNoneRoom(c, uid)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}

View File

@@ -0,0 +1,81 @@
package roomAdmin
import (
"context"
"go-common/app/service/live/xuser/model"
"go-common/library/log"
)
// GetByUserMysql get admins by user .
func (dao *Dao) GetByUserMysql(c context.Context, uid int64) (admins []*model.RoomAdmin, err error) {
err = dao.orm.Model(&model.RoomAdmin{}).Find(&admins, "uid=?", uid).Order("ctime DESC").Error
if err != nil {
log.Error("GetByUserMysql (%v) error(%v)", uid, err)
return nil, err
}
return
}
// GetByRoomIdMysql get admins by roomId.
func (dao *Dao) GetByRoomIdMysql(c context.Context, roomId int64) (admins []*model.RoomAdmin, err error) {
err = dao.orm.Table("ap_room_admin").Model(&model.RoomAdmin{}).Find(&admins, "roomid=?", roomId).Order("ctime DESC").Error
if err != nil {
log.Error("GetByUserMysql (%v) error(%v)", roomId, err)
return nil, err
}
return
}
// DelDbAdminMysql delete a admin .
func (dao *Dao) DelDbAdminMysql(c context.Context, id int64) (err error) {
param := &model.RoomAdmin{
Id: id,
}
//spew.Dump("deldbAdmin", param);
err = dao.orm.Table("ap_room_admin").Model(&model.RoomAdmin{}).Delete(param).Error
if err != nil {
log.Error("DelDbAdminMysql (%v) error(%v)", id, err)
return err
}
return
}
// AddAdminMysql delete a admin .
func (dao *Dao) AddAdminMysql(c context.Context, uid int64, roomId int64) (err error) {
param := &model.RoomAdmin{
Uid: uid,
Roomid: roomId,
}
//spew.Dump("AddAdminMysql", param)
err = dao.orm.Model(&model.RoomAdmin{}).Save(param).Error
if err != nil {
log.Error("AddAdminMysql uid (%v) roomid (%v)error(%v)", uid, roomId, err)
return err
}
return
}
// GetByRoomIdUidMysql delete a admin .
func (dao *Dao) GetByRoomIdUidMysql(c context.Context, uid int64, roomId int64) (resp []*model.RoomAdmin, err error) {
//param := &model.RoomAdmin{
// Uid: uid,
// Roomid: roomId,
//}
err = dao.orm.Model(&model.RoomAdmin{}).Where("uid=? AND roomid =?", uid, roomId).Find(&resp).Error
//spew.Dump("GetByRoomIdUidMysql", resp)
if err != nil {
log.Error("AddAdminMysql uid (%v) roomid (%v)error(%v)", uid, roomId, err)
return resp, err
}
return
}

View File

@@ -0,0 +1,88 @@
package roomAdmin
import (
"context"
"testing"
"github.com/smartystreets/goconvey/convey"
)
func TestRoomAdminGetByUserMysql(t *testing.T) {
convey.Convey("GetByUserMysql", t, func(ctx convey.C) {
var (
c = context.Background()
uid = int64(0)
)
ctx.Convey("When everything gose positive", func(ctx convey.C) {
admins, err := d.GetByUserMysql(c, uid)
ctx.Convey("Then err should be nil.admins should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(admins, convey.ShouldNotBeNil)
})
})
})
}
func TestRoomAdminGetByRoomIdMysql(t *testing.T) {
convey.Convey("GetByRoomIdMysql", t, func(ctx convey.C) {
var (
c = context.Background()
roomId = int64(0)
)
ctx.Convey("When everything gose positive", func(ctx convey.C) {
admins, err := d.GetByRoomIdMysql(c, roomId)
ctx.Convey("Then err should be nil.admins should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(admins, convey.ShouldNotBeNil)
})
})
})
}
func TestRoomAdminDelDbAdmin(t *testing.T) {
convey.Convey("DelDbAdminMysql", t, func(ctx convey.C) {
var (
c = context.Background()
id = int64(0)
)
ctx.Convey("When everything gose positive", func(ctx convey.C) {
err := d.DelDbAdminMysql(c, id)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestRoomAdminAddAdminMysql(t *testing.T) {
convey.Convey("AddAdminMysql", t, func(ctx convey.C) {
var (
c = context.Background()
uid = int64(0)
roomId = int64(0)
)
ctx.Convey("When everything gose positive", func(ctx convey.C) {
err := d.AddAdminMysql(c, uid, roomId)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestRoomAdminGetByRoomIdUidDb(t *testing.T) {
convey.Convey("GetByRoomIdUidMysql", t, func(ctx convey.C) {
var (
c = context.Background()
uid = int64(0)
roomId = int64(0)
)
ctx.Convey("When everything gose positive", func(ctx convey.C) {
resp, err := d.GetByRoomIdUidMysql(c, uid, roomId)
ctx.Convey("Then err should be nil.resp should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(resp, convey.ShouldNotBeNil)
})
})
})
}

View File

@@ -0,0 +1 @@
package roomAdmin

View File

@@ -0,0 +1 @@
package roomAdmin

View File

@@ -0,0 +1,62 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_test",
"go_library",
)
go_test(
name = "go_default_test",
srcs = [
"cache_test.go",
"dao_test.go",
"mysql_test.go",
],
embed = [":go_default_library"],
tags = ["automanaged"],
deps = [
"//app/service/live/xuser/conf:go_default_library",
"//app/service/live/xuser/model:go_default_library",
"//library/database/sql:go_default_library",
"//library/log:go_default_library",
"//library/time:go_default_library",
"//vendor/github.com/smartystreets/goconvey/convey:go_default_library",
],
)
go_library(
name = "go_default_library",
srcs = [
"cache.go",
"dao.go",
"mysql.go",
],
importpath = "go-common/app/service/live/xuser/dao/vip",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/service/live/xuser/conf:go_default_library",
"//app/service/live/xuser/model:go_default_library",
"//library/cache/redis:go_default_library",
"//library/database/sql:go_default_library",
"//library/log:go_default_library",
"//library/net/metadata:go_default_library",
"//library/time:go_default_library",
"//vendor/github.com/pkg/errors:go_default_library",
],
)
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,168 @@
package vip
import (
"context"
"encoding/json"
"fmt"
"github.com/pkg/errors"
"go-common/app/service/live/xuser/model"
"go-common/library/cache/redis"
"go-common/library/log"
"go-common/library/net/metadata"
"time"
)
// redis cache
const (
_userInfoRedisKey = "us:infoo_v2:%d" // 用户缓存key prefix
_vipFieldName = "vip" // v3 vip attr field
_levelFieldName = "level" // v2 level attr field, Todo: remove level attr
_userExpired = 86400 // user cache expire time
)
type vipCache struct {
Vip interface{} `json:"vip"`
VipTime string `json:"vip_time"`
Svip interface{} `json:"svip"`
SvipTime string `json:"svip_time"`
}
// GetVipFromCache get user vip info from cache
func (d *Dao) GetVipFromCache(ctx context.Context, uid int64) (info *model.VipInfo, err error) {
conn := d.redis.Get(ctx)
defer conn.Close()
reply, err := redis.String(conn.Do("HGET", getUserCacheKey(uid), _vipFieldName))
if err != nil {
if err == redis.ErrNil {
// key or field not exists, return nil, nil, back to db
log.Info("[dao.vip.cache|GetVipFromCache] cache key or field not exists err(%v), uid(%d)", err, uid)
return nil, nil
}
log.Error("[dao.vip.cache|GetVipFromCache] hget error(%v), uid(%d)", err, uid)
return
}
if reply == "" {
return nil, nil
}
// ===== begin eat others' dog food =====
// 1.兼容缓存中vip/svip可能是int or string的问题
rawInfo := &vipCache{}
if err = json.Unmarshal([]byte(reply), rawInfo); err != nil {
log.Error("[dao.vip.cache|GetVipFromCache] json.Unmarshal rawInfo error(%v), uid(%d), reply(%s)",
err, uid, reply)
// parse cache json error, return nil, nil, back to db and restore cache
return nil, nil
}
if info, err = d.formatVipCache(rawInfo); err != nil {
log.Error("[dao.vip.cache|GetVipFromCache] format rawInfo error(%v), uid(%d), reply(%s)", err, uid, reply)
return nil, nil
}
// 2.注意!!! cache里的vip_time/svip_time不一定正确可能含有已经过期的time
currentTime := time.Now().Unix()
// vip time
if info.Vip, err = d.checkVipTime(info.VipTime, info.Vip, currentTime); err != nil {
log.Error("[dao.vip.cache|GetVipFromCache] check vip time error(%v), uid(%d), info(%v), reply(%s)",
err, uid, info, reply)
return nil, nil
}
if info.Svip, err = d.checkVipTime(info.SvipTime, info.Svip, currentTime); err != nil {
log.Error("[dao.vip.cache|GetVipFromCache] check svip time error(%v), uid(%d), info(%v), reply(%s)",
err, uid, info, reply)
return nil, nil
}
// ===== end =====
return
}
// formatVipCache 转换vip/svip的格式
func (d *Dao) formatVipCache(info *vipCache) (v *model.VipInfo, err error) {
v = &model.VipInfo{
VipTime: info.VipTime,
SvipTime: info.SvipTime,
}
if v.Vip, err = toInt(info.Vip); err != nil {
return
}
if v.Svip, err = toInt(info.Svip); err != nil {
return
}
// format info struct
v = d.initInfo(v)
return
}
// checkVipTime 检查缓存中vip_time/svip_time是否过期
func (d *Dao) checkVipTime(t string, f int, compare int64) (int, error) {
if t == model.TimeEmpty {
if f != 0 {
return 0, errors.New("empty time with not zero flag.")
}
} else {
vt, err := time.Parse(model.TimeNano, t)
if err != nil {
return 0, errors.New("time parse error.")
}
if vt.Unix() <= compare {
return 0, nil
}
}
return f, nil
}
// SetVipCache set vip to cache
func (d *Dao) SetVipCache(ctx context.Context, uid int64, info *model.VipInfo) (err error) {
var vipJson []byte
conn := d.redis.Get(ctx)
key := getUserCacheKey(uid)
defer conn.Close()
// format info struct
info = d.initInfo(info)
// format info json string
vipJson, err = json.Marshal(info)
if err != nil {
log.Error("[dao.vip.cache|SetVipCache] json.Marshal error(%v), uid(%d), info(%v)", err, uid, info)
// if marshal error, clear cache
goto CLEAR
}
_, err = conn.Do("HSET", key, _vipFieldName, string(vipJson))
if err != nil {
log.Error("[dao.vip.cache|SetVipCache] HSET error(%v), uid(%d), info(%v)", err, uid, info)
// if hset error, clear cache
goto CLEAR
}
_, err = conn.Do("EXPIRE", key, _userExpired)
if err != nil {
log.Error("[dao.vip.cache|SetVipCache] EXPIRE error(%v), uid(%d), info(%v)", err, uid, info)
// if set expire error, clear cache
goto CLEAR
}
return
CLEAR:
log.Error("[dao.vip.cache|SetVipCache] set error, aysnc clear, uid(%d), info(%v)", uid, info)
go d.ClearCache(metadata.WithContext(ctx), uid)
return
}
// ClearCache clear user's vip and level field cache
// Todo: remove level attr
func (d *Dao) ClearCache(ctx context.Context, uid int64) (err error) {
conn := d.redis.Get(ctx)
defer conn.Close()
key := getUserCacheKey(uid)
_, err = conn.Do("HDEL", key, _vipFieldName, _levelFieldName)
if err != nil {
err = errors.Wrapf(err, "conn.Do(HDEL, %s, %s, %s)", key, _vipFieldName, _levelFieldName)
log.Error("[dao.vip.cache|ClearCache] hdel uid(%d) vip and level attr err(%v)", uid, err)
}
return
}
func getUserCacheKey(uid int64) string {
return fmt.Sprintf(_userInfoRedisKey, uid)
}

View File

@@ -0,0 +1,169 @@
package vip
import (
"context"
"fmt"
. "github.com/smartystreets/goconvey/convey"
"go-common/app/service/live/xuser/model"
"go-common/library/log"
"testing"
"time"
)
func TestDao_GetVipFromCache(t *testing.T) {
initd()
Convey("test get vip cache", t, testWithTestUser(func(u *TestUser) {
var (
ctx = context.Background()
info *model.VipInfo
err error
conn = d.redis.Get(ctx)
key = getUserCacheKey(u.Uid)
)
log.Info("TestDao_GetVipFromCache uid(%d), key(%s)", u.Uid, key)
// delete key at begin
conn.Do("DEL", key)
// should get nil info and err
Convey("should get nil info and err", func() {
info, err = d.GetVipFromCache(ctx, u.Uid)
So(info, ShouldBeNil)
So(err, ShouldBeNil)
})
// set empty data
Convey("set empty data", func() {
conn.Do("HSET", key, _vipFieldName, "")
info, err = d.GetVipFromCache(ctx, u.Uid)
So(info, ShouldBeNil)
So(err, ShouldBeNil)
})
// set not a json
Convey("set not a json", func() {
conn.Do("HSET", key, _vipFieldName, "test")
info, err = d.GetVipFromCache(ctx, u.Uid)
So(info, ShouldBeNil)
So(err, ShouldBeNil)
})
// test vip/svip string format
Convey("test vip/svip string format", func() {
vipTime := time.Now().Add(time.Hour * 12).Format(model.TimeNano)
svipTime := time.Now().AddDate(0, -1, 0).Format(model.TimeNano)
conn.Do("HSET", key, _vipFieldName, fmt.Sprintf(`{"vip":"1","vip_time":"%s","svip":0,"svip_time":"%s"}`, vipTime, svipTime))
info, err = d.GetVipFromCache(ctx, u.Uid)
So(info.Vip, ShouldEqual, 1)
So(info.Svip, ShouldEqual, 0)
So(err, ShouldBeNil)
})
// set valid data
Convey("set valid data", func() {
vipTime := time.Now().Add(time.Hour * 12).Format(model.TimeNano)
conn.Do("HSET", key, _vipFieldName, fmt.Sprintf(`{"vip":1,"vip_time":"%s","svip":0,"svip_time":"0000-00-00 00:00:00"}`, vipTime))
info, err = d.GetVipFromCache(ctx, u.Uid)
So(info, ShouldNotBeNil)
So(info.Vip, ShouldEqual, 1)
So(info.VipTime, ShouldEqual, vipTime)
So(info.Svip, ShouldEqual, 0)
So(info.SvipTime, ShouldEqual, model.TimeEmpty)
So(err, ShouldBeNil)
})
// expired vip time
Convey("set valid but expired vip time", func() {
vip := 1
vipTime := time.Now().AddDate(0, 0, -1).Format(model.TimeNano)
svip := 1
svipTime := time.Now().AddDate(0, -1, -1).Format(model.TimeNano)
conn.Do("HSET", key, _vipFieldName, fmt.Sprintf(`{"vip":%d,"vip_time":"%s","svip":%d,"svip_time":"%s"}`, vip, vipTime, svip, svipTime))
info, err = d.GetVipFromCache(ctx, u.Uid)
log.Info("expired vip time, info(%+v)", info)
So(info, ShouldNotBeNil)
So(info.Vip, ShouldEqual, 0)
So(info.VipTime, ShouldEqual, vipTime)
So(info.Svip, ShouldEqual, 0)
So(info.SvipTime, ShouldEqual, svipTime)
So(err, ShouldBeNil)
})
}))
}
func TestDao_SetVipCache(t *testing.T) {
initd()
Convey("test set vip cache", t, testWithTestUser(func(u *TestUser) {
var (
ctx = context.Background()
info *model.VipInfo
err error
conn = d.redis.Get(ctx)
key = getUserCacheKey(u.Uid)
)
log.Info("TestDao_GetVipFromCache uid(%d), key(%s)", u.Uid, key)
// delete key at begin
conn.Do("DEL", key)
// nil info
Convey("nil info", func() {
err = d.SetVipCache(ctx, u.Uid, nil)
So(err, ShouldBeNil)
info, err = d.GetVipFromCache(ctx, u.Uid)
log.Info("TestDao_SetVipCache get info1(%v), err(%v)", info, err)
So(err, ShouldBeNil)
So(info.Vip, ShouldEqual, 0)
So(info.VipTime, ShouldEqual, model.TimeEmpty)
So(info.Svip, ShouldEqual, 0)
So(info.SvipTime, ShouldEqual, model.TimeEmpty)
})
// set valid info
Convey("set valid info", func() {
info = &model.VipInfo{
Vip: 1,
VipTime: time.Now().Add(time.Hour * 12).Format(model.TimeNano),
Svip: 1,
SvipTime: time.Now().Add(time.Hour * 6).Format(model.TimeNano),
}
err = d.SetVipCache(ctx, u.Uid, info)
So(err, ShouldBeNil)
info2, err := d.GetVipFromCache(ctx, u.Uid)
log.Info("TestDao_SetVipCache get info2(%v), err(%v)", info, err)
So(err, ShouldBeNil)
So(info2.Vip, ShouldEqual, info.Vip)
So(info2.VipTime, ShouldEqual, info.VipTime)
So(info2.Svip, ShouldEqual, info.Svip)
So(info2.SvipTime, ShouldEqual, info.SvipTime)
})
}))
}
func TestDao_ClearCache(t *testing.T) {
initd()
Convey("test clear cache", t, testWithTestUser(func(u *TestUser) {
var (
ctx = context.Background()
err error
conn = d.redis.Get(ctx)
key = getUserCacheKey(u.Uid)
)
log.Info("TestDao_ClearCache uid(%d), key(%s)", u.Uid, key)
// delete key at begin
conn.Do("DEL", key)
// del already deleted key
Convey("del already deleted key", func() {
err = d.ClearCache(ctx, u.Uid)
So(err, ShouldBeNil)
})
// set valid info
Convey("set valid info", func() {
err = d.SetVipCache(ctx, u.Uid, nil)
So(err, ShouldBeNil)
err = d.ClearCache(ctx, u.Uid)
So(err, ShouldBeNil)
})
}))
}

View File

@@ -0,0 +1,84 @@
package vip
import (
"context"
"fmt"
"go-common/app/service/live/xuser/conf"
"go-common/app/service/live/xuser/model"
"go-common/library/cache/redis"
xsql "go-common/library/database/sql"
"strconv"
)
// Dao vip dao
type Dao struct {
c *conf.Config
db *xsql.DB
redis *redis.Pool
}
// New new vip dao
func New(c *conf.Config) (dao *Dao) {
dao = &Dao{
c: c,
db: xsql.NewMySQL(c.LiveUserMysql),
redis: redis.NewPool(c.VipRedis),
}
return
}
// Close close the resource.
func (d *Dao) Close() {
d.db.Close()
d.redis.Close()
}
// initInfo init info struct
func (d *Dao) initInfo(info *model.VipInfo) *model.VipInfo {
if info == nil {
info = &model.VipInfo{Vip: 0, VipTime: model.TimeEmpty, Svip: 0, SvipTime: model.TimeEmpty}
} else {
if info.VipTime == "" {
info.VipTime = model.TimeEmpty
}
if info.SvipTime == "" {
info.SvipTime = model.TimeEmpty
}
}
return info
}
// Ping dao ping
func (d *Dao) Ping(ctx context.Context) error {
// TODO: add mc,redis... if you use
return nil
}
// toInt try trans interface input to int
func toInt(in interface{}) (int, error) {
switch in.(type) {
case int:
return in.(int), nil
case int32:
return int(in.(int32)), nil
case int64:
return int(in.(int64)), nil
case float32:
return int(in.(float32)), nil
case float64:
return int(in.(float64)), nil
case string:
i, err := strconv.Atoi(in.(string))
if err != nil {
return 0, err
}
return i, nil
case []byte:
i, err := strconv.Atoi(string(in.([]byte)))
if err != nil {
return 0, err
}
return i, nil
}
return 0, fmt.Errorf("invalid input(%v)", in)
}

View File

@@ -0,0 +1,70 @@
package vip
import (
"context"
"flag"
. "github.com/smartystreets/goconvey/convey"
"go-common/app/service/live/xuser/conf"
"math/rand"
"path/filepath"
"testing"
)
// vip dao and conf
var (
d *Dao
)
type TestUser struct {
Uid int64
}
// initd init vip dao
func initd() {
dir, _ := filepath.Abs("../../cmd/test.toml")
flag.Set("conf", dir)
flag.Set("deploy_env", "uat")
conf.Init()
d = New(conf.Conf)
}
func initTestUser() *TestUser {
return &TestUser{
Uid: int64(rand.Int31()),
}
}
func (t *TestUser) Reset() {
d.ClearCache(context.Background(), t.Uid)
d.deleteVip(context.Background(), t.Uid)
}
func testWithTestUser(f func(u *TestUser)) func() {
u := initTestUser()
return func() {
f(u)
u.Reset()
}
}
func TestToInt(t *testing.T) {
var (
err error
out int
)
Convey("test toInt", t, func() {
out, err = toInt(1)
So(out, ShouldEqual, 1)
So(err, ShouldBeNil)
})
Convey("test toInt", t, func() {
out, err = toInt("123")
So(out, ShouldEqual, 123)
So(err, ShouldBeNil)
})
Convey("test toInt", t, func() {
out, err = toInt("test")
So(out, ShouldEqual, 0)
So(err, ShouldNotBeNil)
})
}

View File

@@ -0,0 +1,204 @@
package vip
import (
"context"
"crypto/md5"
"database/sql"
"encoding/hex"
"fmt"
"strconv"
"time"
"go-common/app/service/live/xuser/model"
"go-common/library/log"
xtime "go-common/library/time"
"github.com/pkg/errors"
xsql "go-common/library/database/sql"
)
const (
_userVipRecordPrefix = "user_vip_record_%d"
_userVipRecordCount = 10
_userLevelPrefix = "user_"
)
var (
errUpdateVipTimeInvalid = errors.New("update vip but vip_time invalid")
)
var (
// get vip info from user_x table
_getVipInfo = "SELECT `vip`,`vip_time`,`svip`,`svip_time` FROM `%s` WHERE uid=?;"
// insert into user_vip_record_n
_insertUserVipRecord = "INSERT INTO `%s` (`uid`,`vip_type`,`vip_num`,`order_id`,`platform`,`source`) VALUES (?,?,?,?,?,?);"
// update user_vip_record_n before_time & status after update vip success
_updateUserVipRecordStatus = "UPDATE `%s` SET `before_vip`=?,`before_vip_time`=?,`before_svip`=?,`before_svip_time`=?,`status`=? WHERE `id`=?;"
// update user_x vip info
_updateAllVip = "UPDATE `%s` SET `vip`=?,`vip_time`=?,`svip`=?,`svip_time`=? WHERE uid=?;"
_updateVip = "UPDATE `%s` SET `vip`=?,`vip_time`=? WHERE uid=?;"
// insert vip
_insertVip = "INSERT INTO `%s` (`uid`,`vip`,`vip_time`,`svip`,`svip_time`) VALUES (?,?,?,?,?);"
// delete vip
_deleteVip = "DELETE FROM `%s` WHERE `uid`=? LIMIT 1;"
// insert ap_vip_record
_insertApVipRecord = "INSERT INTO `ap_vip_record`(`uid`,`type`,`vip_time`,`platform`) VALUES (?,?,?,?);"
)
// GetVipFromDB get vip info by uid
// return error maybe sql no row or just scan error, how to handle decided by upper business
func (d *Dao) GetVipFromDB(ctx context.Context, uid int64) (info *model.VipInfo, err error) {
var (
vipTime, sVipTime xtime.Time
currentTime = xtime.Time(time.Now().Unix())
)
row := d.db.QueryRow(ctx, fmt.Sprintf(_getVipInfo, getUserLevelTable(uid)), uid)
info = &model.VipInfo{}
if err = row.Scan(&info.Vip, &vipTime, &info.Svip, &sVipTime); err != nil {
log.Error("[dao.vip.mysql|GetVipFromDB] row scan error(%v), uid(%d)", err, uid)
// no rows in user_x table, async insert one, don't return error
if err == xsql.ErrNoRows {
go d.createVip(context.TODO(), uid, info)
err = nil
return
}
return
}
// format info vip time
if vipTime <= 0 {
info.VipTime = model.TimeEmpty
} else {
info.VipTime = vipTime.Time().Format(model.TimeNano)
}
if sVipTime <= 0 {
info.SvipTime = model.TimeEmpty
} else {
info.SvipTime = sVipTime.Time().Format(model.TimeNano)
}
// format vip & svip
// 注意!!! db里的数据不一定正确可能含有已经过期的time
if vipTime <= currentTime {
info.Vip = 0
}
if sVipTime <= currentTime {
info.Svip = 0
}
return
}
// AddVip update user_n vip fields, add vip/svip time
// weather add vip or svip, vipTime should not be empty
func (d *Dao) AddVip(ctx context.Context, uid int64, vipTime, sVipTime xtime.Time) (row int64, err error) {
var (
vt, st string
res sql.Result
updateType string
currentTime = xtime.Time(time.Now().Unix())
)
if vipTime <= currentTime {
return 0, errUpdateVipTimeInvalid
}
vt = vipTime.Time().Format(model.TimeNano)
if sVipTime > currentTime {
// update vip and svip
st = sVipTime.Time().Format(model.TimeNano)
updateType = "all"
res, err = d.db.Exec(ctx, fmt.Sprintf(_updateAllVip, getUserLevelTable(uid)), 1, vt, 1, st, uid)
} else {
// update vip only
updateType = "vip"
res, err = d.db.Exec(ctx, fmt.Sprintf(_updateVip, getUserLevelTable(uid)), 1, vt, uid)
}
if err != nil {
log.Error("[dao.vip.mysql|AddVip] update vip error(%v), type(%s), uid(%d), vip(%s), svip(%s)",
err, updateType, uid, vt, st)
return
}
row, _ = res.RowsAffected()
return
}
// createVip create user_n vip row, for internal usage only. Do not use in business!
func (d *Dao) createVip(ctx context.Context, uid int64, info *model.VipInfo) (err error) {
info = d.initInfo(info)
log.Info("[dao.vip.mysql|createVip] create user_n row, uid(%d), info(%v)", uid, info)
_, err = d.db.Exec(ctx, fmt.Sprintf(_insertVip, getUserLevelTable(uid)),
uid, info.Vip, info.VipTime, info.Svip, info.SvipTime)
if err != nil {
log.Error("[dao.vip.mysql|createVip] create error(%v), uid(%d), info(%v)", err, uid, info)
}
return
}
// deleteVip delete user_n vip row, for internal usage only. Do not use in business!
func (d *Dao) deleteVip(ctx context.Context, uid int64) (err error) {
log.Info("[dao.vip.mysql|deleteVip] delete user_n row, uid(%d)", uid)
_, err = d.db.Exec(ctx, fmt.Sprintf(_deleteVip, getUserLevelTable(uid)), uid)
if err != nil {
log.Error("[dao.vip.mysql|deleteVip] delete error(%v), uid(%d)", err, uid)
}
return
}
// CreateVipRecord create user vip record if not exists
// return error maybe unique key exists err or other db error, upper business should notice
// unique key is (uid,order_id)
func (d *Dao) CreateVipRecord(ctx context.Context, req *model.VipBuy) (recordID int64, err error) {
res, err := d.db.Exec(ctx, fmt.Sprintf(_insertUserVipRecord, getUserVipRecordTable(req.Uid)),
req.Uid, req.GoodID, req.GoodNum, req.OrderID, req.Platform, req.Source)
if err != nil {
log.Error("[dao.vip.mysql|CreateUserVipRecord] create user vip record error(%v), req(%v)", err, req)
return
}
if recordID, err = res.LastInsertId(); err != nil {
err = errors.WithStack(err)
log.Error("[dao.vip.mysql|CreateUserVipRecord] get last insert id error(%v), req(%v)", err, req)
}
return
}
// UpdateVipRecord update user vip record after buy success
func (d *Dao) UpdateVipRecord(ctx context.Context, recordID, uid int64, info *model.VipInfo) (err error) {
execSql := fmt.Sprintf(_updateUserVipRecordStatus, getUserVipRecordTable(uid))
_, err = d.db.Exec(ctx, execSql, info.Vip, info.VipTime, info.Svip, info.SvipTime, model.BuyStatusSuccess, recordID)
if err != nil {
log.Error("[dao.vip.mysql|UpdateVipRecordLater] update error(%v), record id(%d), uid(%d), info(%v)",
err, recordID, uid, info)
}
return
}
// CreateApVipRecord create ap_vip_record
func (d *Dao) CreateApVipRecord(ctx context.Context, record *model.VipRecord) (row int64, err error) {
res, err := d.db.Exec(ctx, _insertApVipRecord, record.Uid, record.VipType, record.AfterVipTime, record.Platform)
if err != nil {
log.Error("[dao.vip.mysql|CreateApVipRecord] insert ap_vip_record error(%v), record(%v)", err, record)
return
}
row, _ = res.RowsAffected()
return
}
// getUserLevelTable get user_x table by uid
func getUserLevelTable(uid int64) string {
uidStr := strconv.FormatInt(uid, 10)
md5Ctx := md5.New()
md5Ctx.Write([]byte(uidStr))
cipher := md5Ctx.Sum(nil)
return _userLevelPrefix + hex.EncodeToString(cipher)[0:1]
}
// getUserVipRecordTable get user_vip_record_x table by uid
func getUserVipRecordTable(uid int64) string {
return fmt.Sprintf(_userVipRecordPrefix, uid%_userVipRecordCount)
}

View File

@@ -0,0 +1,150 @@
package vip
import (
"context"
"fmt"
. "github.com/smartystreets/goconvey/convey"
"go-common/app/service/live/xuser/model"
"go-common/library/database/sql"
"go-common/library/log"
xtime "go-common/library/time"
"math/rand"
"testing"
"time"
)
func Test_getUserLevelTable(t *testing.T) {
initd()
Convey("test get user_x table name by uid", t, func() {
var uid = int64(123)
table := getUserLevelTable(uid)
So(table, ShouldEqual, "user_2")
})
}
func Test_getUserVipRecordTable(t *testing.T) {
initd()
Convey("test get user_vip_record_x table name by uid", t, func() {
var uid = rand.Int63()
log.Info("Test_getUserVipRecordTable uid(%d)", uid)
table := getUserVipRecordTable(uid)
t := fmt.Sprintf(_userVipRecordPrefix, uid%_userVipRecordCount)
So(table, ShouldEqual, t)
})
}
func TestDao_GetVipFromDB(t *testing.T) {
initd()
Convey("test get vip from db", t, testWithTestUser(func(u *TestUser) {
log.Info("TestDao_GetVipFromDB uid(%d), table(%s)", u.Uid, getUserLevelTable(u.Uid))
var (
ctx = context.Background()
err error
info *model.VipInfo
)
// delete random uid at begin
err = d.deleteVip(ctx, u.Uid)
So(err, ShouldBeNil)
// get nil result from db
Convey("get nil result from db", func() {
info, err = d.GetVipFromDB(ctx, u.Uid)
So(err, ShouldResemble, sql.ErrNoRows)
So(info, ShouldNotBeNil)
So(info.Vip, ShouldEqual, 0)
So(info.VipTime, ShouldEqual, "")
So(info.Svip, ShouldEqual, 0)
So(info.SvipTime, ShouldEqual, "")
})
// insert and then get
Convey("insert and then get", func() {
var info2 *model.VipInfo
info = &model.VipInfo{
Vip: 1,
VipTime: time.Now().Add(time.Hour * 12).Format(model.TimeNano),
Svip: 1,
SvipTime: time.Now().Add(time.Hour * 12).Format(model.TimeNano),
}
err = d.createVip(ctx, u.Uid, info)
So(err, ShouldBeNil)
info2, err = d.GetVipFromDB(ctx, u.Uid)
log.Info("TestDao_GetVipFromDB info2(%v)", info2)
So(err, ShouldBeNil)
So(info, ShouldResemble, info2)
})
// valid info but expired vip time
Convey("insert valid but expired vip time", func() {
info = &model.VipInfo{
Vip: 1,
VipTime: time.Now().AddDate(0, -1, 0).Format(model.TimeNano),
Svip: 1,
SvipTime: time.Now().AddDate(-1, 0, 0).Format(model.TimeNano),
}
err = d.createVip(ctx, u.Uid, info)
So(err, ShouldBeNil)
info2, err := d.GetVipFromDB(ctx, u.Uid)
log.Info("TestDao_GetVipFromDB info2(%v)", info2)
So(err, ShouldBeNil)
So(info2.Vip, ShouldEqual, 0)
So(info2.Svip, ShouldEqual, 0)
So(info2.VipTime, ShouldEqual, info.VipTime)
So(info2.SvipTime, ShouldEqual, info.SvipTime)
})
}))
}
func TestDao_AddVip(t *testing.T) {
initd()
Convey("test add vip", t, testWithTestUser(func(u *TestUser) {
log.Info("TestDao_GetVipFromDB uid(%d), table(%s)", u.Uid, getUserLevelTable(u.Uid))
var (
ctx = context.Background()
err error
info *model.VipInfo
row int64
)
// create one row at begin
info = &model.VipInfo{
Vip: 1,
VipTime: time.Now().Add(time.Hour * 12).Format(model.TimeNano),
Svip: 1,
SvipTime: time.Now().Add(time.Hour * 12).Format(model.TimeNano),
}
err = d.createVip(ctx, u.Uid, info)
So(err, ShouldBeNil)
// empty vip time, should return err
Convey("empty vip time, should return err", func() {
row, err = d.AddVip(ctx, u.Uid, 0, 0)
So(row, ShouldEqual, 0)
So(err, ShouldResemble, errUpdateVipTimeInvalid)
})
// add vip and svip time
Convey("add vip and svip time", func() {
// add one month vip
dt := xtime.Time(30 * 86400)
vtime, err := time.Parse(model.TimeNano, info.VipTime)
So(err, ShouldBeNil)
newvtime := xtime.Time(vtime.Unix()) + dt
log.Info("TestDao_AddVip info(%v), oldvt(%v), newvtime(%v), dt(%v)", info, vtime.Unix(), newvtime, dt)
row, err := d.AddVip(ctx, u.Uid, newvtime, 0)
So(row, ShouldEqual, 1)
So(err, ShouldBeNil)
info2, err := d.GetVipFromDB(ctx, u.Uid)
log.Info("TestDao_AddVip info2(%v)", info2)
So(err, ShouldBeNil)
So(info2.Vip, ShouldEqual, info.Vip)
So(info2.Svip, ShouldEqual, info.Svip)
So(info2.SvipTime, ShouldEqual, info.SvipTime)
So(info2.VipTime, ShouldEqual, newvtime.Time().Format(model.TimeNano))
})
}))
}

View File

@@ -0,0 +1,35 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = [
"anchorUpdate.go",
"dao.go",
],
importpath = "go-common/app/service/live/xuser/dao/xanchor",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/service/live/dao-anchor/api/grpc/v1:go_default_library",
"//app/service/live/xuser/conf:go_default_library",
],
)
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,15 @@
package xanchor
import (
"context"
XanchorV1 "go-common/app/service/live/dao-anchor/api/grpc/v1"
)
// UpdateAnchorInfo 更新主播经验db
func (d *Dao) UpdateAnchorInfo(ctx context.Context, params *XanchorV1.AnchorIncreReq) (err error) {
_, err = d.xuserGRPC.AnchorIncre(ctx, params)
if err != nil {
return
}
return
}

View File

@@ -0,0 +1,39 @@
package xanchor
import (
"context"
xanchor "go-common/app/service/live/dao-anchor/api/grpc/v1"
"go-common/app/service/live/xuser/conf"
)
// Dao dao
type Dao struct {
c *conf.Config
xuserGRPC *xanchor.Client
}
var _rsCli *xanchor.Client
// New init mysql db
func New(c *conf.Config) (dao *Dao) {
var err error
if _rsCli, err = xanchor.NewClient(c.XanchorClient); err != nil {
panic(err)
}
dao = &Dao{
c: c,
xuserGRPC: _rsCli,
}
return
}
// Close close the resource.
func (d *Dao) Close() {
}
// Ping dao ping
func (d *Dao) Ping(c context.Context) error {
// TODO: if you need use mc,redis, please add
// check
return nil
}

View File

@@ -0,0 +1,39 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = [
"model.go",
"roomAdmin.go",
],
importpath = "go-common/app/service/live/xuser/model",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/service/live/xuser/api/grpc:go_default_library",
"//library/time:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [
":package-srcs",
"//app/service/live/xuser/model/dhh:all-srcs",
"//app/service/live/xuser/model/exp:all-srcs",
],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,28 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = ["model.go"],
importpath = "go-common/app/service/live/xuser/model/dhh",
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,69 @@
package dhh
import "time"
// DHHDB 大航海信息DB层
type DHHDB struct {
ID int64
UID int64
TargetId int64
PrivilegeType int64
StartTime time.Time
ExpiredTime time.Time
Ctime time.Time
Utime time.Time
}
// DHHDBTime 大航海信息验DB层(时间转换)
type DHHDBTime struct {
ID int64
UID int64
TargetId int64
PrivilegeType int64
StartTime string
ExpiredTime string
Ctime string
Utime string
}
// ModelDHHList dhh list
type ModelDHHList struct {
Data []DHHDB
}
// ModelExpLog 行为日志上报结构
type ModelExpLog struct {
Mid int64
Uexp int64
Rexp int64
Ts int64
// 业务来源
ReqBizDesc string
Buvid string
// 具体描述
Content map[string]string
}
// DaHangHaiRedis 等级基础结构
type DaHangHaiRedis struct {
Id int64 `json:"id"`
Uid int64 `json:"uid"`
TargetId int64 `json:"target_id"`
PrivilegeType int64 `json:"privilege_type"`
StartTime string `json:"start_time"`
ExpiredTime string `json:"expired_time"`
Ctime string `json:"ctime"`
Utime string `json:"utime"`
}
// DaHangHaiRedis2 等级基础结构
type DaHangHaiRedis2 struct {
Id string `json:"id"`
Uid string `json:"uid"`
TargetId string `json:"target_id"`
PrivilegeType string `json:"privilege_type"`
StartTime string `json:"start_time"`
ExpiredTime string `json:"expired_time"`
Ctime string `json:"ctime"`
Utime string `json:"utime"`
}

View File

@@ -0,0 +1,28 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = ["model.go"],
importpath = "go-common/app/service/live/xuser/model/exp",
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,66 @@
package exp
// Exp 经验DB层
type Exp struct {
UID int64
Uexp int64
Rexp int64
CTime string
MTime string
}
// ModelExpList exp list
type ModelExpList struct {
Data []Exp
}
// ModelExpLog 行为日志上报结构
type ModelExpLog struct {
Mid int64
Uexp int64
Rexp int64
Ts int64
// 业务来源
ReqBizDesc string
Buvid string
// 具体描述
Content map[string]string
}
// LevelInfo 等级结构
type LevelInfo struct {
UID int64
UserLevel UserLevelInfo
AnchorLevel AnchorLevelInfo
CTime string
MTime string
}
// UserLevelInfo 等级基础结构
type UserLevelInfo struct {
Level int64 `json:"level"`
NextLevel int64 `json:"nextLevel"`
UserExpLeft int64 `json:"userExpLeft"`
UserExpRight int64 `json:"userExpRight"`
UserExp int64 `json:"userExp"`
UserExpNextLevel int64 `json:"userExpNextLevel"`
Color int64 `json:"color"`
UserExpNextLeft int64 `json:"userExpNextLeft"`
UserExpNextRight int64 `json:"userExpNextRight"`
IsLevelTop int64 `json:"isLevelTop"`
}
// AnchorLevelInfo 等级基础结构
type AnchorLevelInfo struct {
Level int64 `json:"level"`
NextLevel int64 `json:"nextLevel"`
UserExpLeft int64 `json:"userExpLeft"`
UserExpRight int64 `json:"userExpRight"`
UserExp int64 `json:"userExp"`
UserExpNextLevel int64 `json:"userExpNextLevel"`
Color int64 `json:"color"`
UserExpNextLeft int64 `json:"userExpNextLeft"`
UserExpNextRight int64 `json:"userExpNextRight"`
AnchorScore int64 `json:"anchorScore"`
IsLevelTop int64 `json:"isLevelTop"`
}

View File

@@ -0,0 +1,85 @@
package model
import (
"go-common/app/service/live/xuser/api/grpc"
"time"
)
// VipBuy buy vip request struct
type VipBuy struct {
Uid int64
OrderID string
GoodID int
GoodNum int
Platform grpc.Platform
Source string
}
// VipInfo vip info struct
type VipInfo struct {
Vip int `json:"vip"`
VipTime string `json:"vip_time"`
Svip int `json:"svip"`
SvipTime string `json:"svip_time"`
}
// VipRecord ap_vip_record log and notify message
type VipRecord struct {
Uid int64 `json:"uid"`
Opcode string `json:"opcode"`
BuyType int `json:"buy_type"`
BuyNum int `json:"buy_num"`
VipType int `json:"vip_type"`
BeforeVipTime string `json:"begin"`
AfterVipTime string `json:"end"`
Platform string
}
// GuardBuy buy guard request struct
type GuardBuy struct {
OrderId string
Uid int64
Ruid int64
GuardLevel int
Num int
Platform grpc.Platform
Source string
}
// GuardInfo guard info struct for ap_user_privilege
type GuardInfo struct {
Id int64
Uid int64
TargetId int64
PrivilegeType int
StartTime time.Time
ExpiredTime time.Time
}
// GuardEntryEffects entry effect message
type GuardEntryEffects struct {
Business int `json:"business"`
Data []GuardEntryEffect `json:"data"`
}
// GuardEntryEffect entry effect message
type GuardEntryEffect struct {
EffectId int `json:"effect_id"`
Uid int64 `json:"uid"`
TargetId int64 `json:"target_id"`
EndTime string `json:"end_time"`
}
// Vip constants
var (
Vip = 1 // 月费姥爷
Svip = 2 // 年费姥爷
BuyStatusSuccess = 1 // 购买成功
BuyStatusRetry = 2 // 需要重试
TimeNano = "2006-01-02 15:04:05"
TimeEmpty = "0000-00-00 00:00:00"
OpcodeAdd = "add"
)

View File

@@ -0,0 +1,34 @@
package model
import xtime "go-common/library/time"
// TableName is used to identify table name in gorm
func (ra *RoomAdmin) TableName() string {
return "ap_room_admin"
}
// RoomAdmin .
type RoomAdmin struct {
Id int64 `json:"id" gorm:"column:id"`
Uid int64 `json:"uid" gorm:"column:uid"`
Roomid int64 `json:"roomid" gorm:"column:roomid"`
Ctime xtime.Time `json:"ctime" gorm:"comumn:ctime"`
}
// RoomAdmins multi RoomAdmin .
type RoomAdmins []*RoomAdmin
// Len returns length of RoomAdmins.
func (ras RoomAdmins) Len() int {
return len(ras)
}
// Swap .
func (ras RoomAdmins) Swap(i, j int) {
ras[i], ras[j] = ras[j], ras[i]
}
// Less returns sorting rule.
func (ras RoomAdmins) Less(i, j int) bool {
return ras[i].Ctime < ras[j].Ctime
}

View File

@@ -0,0 +1,34 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = ["server.go"],
importpath = "go-common/app/service/live/xuser/server/grpc",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/service/live/xuser/api/grpc/v1:go_default_library",
"//app/service/live/xuser/dao:go_default_library",
"//app/service/live/xuser/service:go_default_library",
"//library/net/rpc/warden:go_default_library",
],
)
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,24 @@
package grpc
import (
v1pb "go-common/app/service/live/xuser/api/grpc/v1"
"go-common/app/service/live/xuser/dao"
"go-common/app/service/live/xuser/service"
"go-common/library/net/rpc/warden"
)
// New new grpc server
func New(svc *service.Service) (wsvr *warden.Server, err error) {
wsvr = warden.NewServer(nil)
dao.InitAPI()
v1pb.RegisterVipServer(wsvr.Server(), svc.VipV1Svc())
v1pb.RegisterGuardServer(wsvr.Server(), svc.GuardV1Svc())
v1pb.RegisterUserExpServer(wsvr.Server(), svc.ExpV1Svc())
v1pb.RegisterRoomAdminServer(wsvr.Server(), svc.RoomAdminV1Svc())
if wsvr, err = wsvr.Start(); err != nil {
return
}
return
}

View File

@@ -0,0 +1,35 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = ["http.go"],
importpath = "go-common/app/service/live/xuser/server/http",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/service/live/xuser/conf:go_default_library",
"//app/service/live/xuser/service:go_default_library",
"//library/log:go_default_library",
"//library/net/http/blademaster:go_default_library",
"//library/net/http/blademaster/middleware/verify:go_default_library",
],
)
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,58 @@
package http
import (
"go-common/app/service/live/xuser/conf"
"go-common/app/service/live/xuser/service"
"go-common/library/log"
bm "go-common/library/net/http/blademaster"
"go-common/library/net/http/blademaster/middleware/verify"
"net/http"
)
var (
vfy *verify.Verify
svc *service.Service
)
// Init init
func Init(c *conf.Config, s *service.Service) {
svc = s
vfy = verify.New(c.Verify)
engine := bm.DefaultServer(c.BM)
route(engine)
if err := engine.Start(); err != nil {
log.Error("bm Start error(%v)", err)
panic(err)
}
}
func route(e *bm.Engine) {
e.Ping(ping)
e.Register(register)
g := e.Group("/x/xuser")
{
g.GET("/test", test)
g.GET("/start", vfy.Verify, howToStart)
}
}
func ping(ctx *bm.Context) {
if err := svc.Ping(ctx); err != nil {
log.Error("ping error(%v)", err)
ctx.AbortWithStatus(http.StatusServiceUnavailable)
}
}
func register(c *bm.Context) {
c.JSON(map[string]interface{}{}, nil)
}
// test test http api
func test(c *bm.Context) {
c.JSON("test success", nil)
}
// example for http request handler
func howToStart(c *bm.Context) {
c.String(0, "Golang 大法好 !!!")
}

View File

@@ -0,0 +1,41 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = ["service.go"],
importpath = "go-common/app/service/live/xuser/service",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/service/live/xuser/conf:go_default_library",
"//app/service/live/xuser/service/exp/v1:go_default_library",
"//app/service/live/xuser/service/guard/v1:go_default_library",
"//app/service/live/xuser/service/roomAdmin/v1:go_default_library",
"//app/service/live/xuser/service/vip/v1:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [
":package-srcs",
"//app/service/live/xuser/service/exp/v1:all-srcs",
"//app/service/live/xuser/service/guard/v1:all-srcs",
"//app/service/live/xuser/service/roomAdmin/v1:all-srcs",
"//app/service/live/xuser/service/vip/v1:all-srcs",
],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,53 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = [
"audit.go",
"check_param.go",
"clear_cache.go",
"level_rule.go",
"log_debug.go",
"update_db.go",
"user_exp.go",
],
importpath = "go-common/app/service/live/xuser/service/exp/v1",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/service/live/dao-anchor/api/grpc/v1:go_default_library",
"//app/service/live/xuser/api/grpc/v1:go_default_library",
"//app/service/live/xuser/conf:go_default_library",
"//app/service/live/xuser/dao/exp:go_default_library",
"//app/service/live/xuser/dao/xanchor:go_default_library",
"//app/service/live/xuser/model/exp:go_default_library",
"//library/cache:go_default_library",
"//library/ecode:go_default_library",
"//library/log:go_default_library",
"//library/net/metadata:go_default_library",
"//library/queue/databus/report:go_default_library",
"//library/stat/prom:go_default_library",
"//library/sync/pipeline/fanout:go_default_library",
"//library/time:go_default_library",
"//vendor/github.com/pkg/errors:go_default_library",
],
)
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,101 @@
package v1
import (
"go-common/app/service/live/xuser/model/exp"
expm "go-common/app/service/live/xuser/model/exp"
"go-common/library/log"
"go-common/library/queue/databus/report"
gtime "go-common/library/time"
"strconv"
"time"
)
// consts
const (
_liveUserExpID = 104 // http://info.bilibili.co/pages/viewpage.action?pageId=8731603
)
func (s *UserExpService) addUserExpLog(uid int64, uexp int64, level map[int64]*expm.LevelInfo, logDesc string) (err error) {
if len(level) > 0 {
for _, v := range level {
logParams := &exp.ModelExpLog{
Mid: uid,
Uexp: uexp,
Rexp: 0,
Ts: time.Now().Unix(),
ReqBizDesc: logDesc,
Content: map[string]string{
"经验变更类型": "增加用户维度经验",
"变更数量": strconv.FormatInt(uexp, 10),
"当前用户维度总经验": strconv.FormatInt(v.UserLevel.UserExp, 10),
"当前主播维度总经验": strconv.FormatInt(v.AnchorLevel.UserExp, 10),
"当前用户维度等级": strconv.FormatInt(v.UserLevel.Level, 10),
"当前主播维度等级": strconv.FormatInt(v.AnchorLevel.Level, 10),
},
}
s.addLog(_liveUserExpID, "exp_change", logParams, "增加用户经验")
}
}
return
}
func (s *UserExpService) addAnchorExpLog(uid int64, uexp int64, level map[int64]*expm.LevelInfo, logDesc string) (err error) {
if len(level) > 0 {
for _, v := range level {
logParams := &exp.ModelExpLog{
Mid: uid,
Uexp: uexp,
Rexp: 0,
Ts: time.Now().Unix(),
ReqBizDesc: logDesc,
Content: map[string]string{
"经验变更类型": "增加主播维度经验",
"变更数量": strconv.FormatInt(uexp, 10),
"当前用户维度总经验": strconv.FormatInt(v.UserLevel.UserExp, 10),
"当前主播维度总经验": strconv.FormatInt(v.AnchorLevel.UserExp, 10),
"当前用户维度等级": strconv.FormatInt(v.UserLevel.Level, 10),
"当前主播维度等级": strconv.FormatInt(v.AnchorLevel.Level, 10),
},
}
s.addLog(_liveUserExpID, "exp_change", logParams, "增加主播经验")
}
}
return
}
func (s *UserExpService) addLog(business int, action string, expInfo *exp.ModelExpLog, desc string) (err error) {
t := gtime.Time(expInfo.Ts)
content := make(map[string]interface{}, len(expInfo.Content))
for k, v := range expInfo.Content {
content[k] = v
}
ui := &report.UserInfo{
Mid: expInfo.Mid,
Platform: desc,
Build: 0,
Buvid: expInfo.Buvid,
Business: business,
Type: 0,
Action: action,
Ctime: t.Time(),
IP: expInfo.ReqBizDesc,
// extra
Index: []interface{}{int64(expInfo.Mid), 0, "", "", ""},
Content: content,
}
report.User(ui)
log.Info("add log to report: userexplog: %+v userinfo: %+v,error(%v)", expInfo, ui)
return
}
// RecordTimeCostLog ...
// 记录时间
func (s *UserExpService) RecordTimeCostLog(nowTime int64, desc string) {
log.Info(desc+"|%d", nowTime)
}
// RecordTimeCost ...
// 记录时间
func (s *UserExpService) RecordTimeCost() (NowTime int64) {
return time.Now().UnixNano() / 1000000 // 用毫秒
}

View File

@@ -0,0 +1,61 @@
package v1
import (
"go-common/app/service/live/xuser/conf"
)
const (
_addExpReqBizTypeFromPHPUser = 419
// _addExpReqBizTypeCloseRoom = 420
// _addExpReqBizTypeUserOnline = 421
)
const (
_addUserExpType = 1
_addAnchorExpType = 2
)
var (
_addExpReqBizMap = map[int64]string{
// _addExpReqBizTypeCloseRoom: "主播关播后经验结算",
// _addExpReqBizTypeUserOnline: "用户在线观看直播经验结算",
_addExpReqBizTypeFromPHPUser: "用户经验结算",
}
)
var (
_addExpReqType = map[int64]string{
_addUserExpType: "增加用户经验",
_addAnchorExpType: "增加主播经验",
}
)
func (s *UserExpService) checkReqBiz(reqNum int64) (pass bool, rspBiz string) {
rspBiz, pass = _addExpReqBizMap[reqNum]
return
}
func (s *UserExpService) checkAddType(reqType int64) (pass bool, rspBiz string) {
rspBiz, pass = _addExpReqType[reqType]
return
}
func (s *UserExpService) checkAddNum(reqType int64) (pass bool, rspNum int64) {
// 当前业务场景不支持减经验,若之后需要支持请修改本method以及相关逻辑,注意修改proto文件,使用了validate gt标志
if reqType < 0 {
pass = false
rspNum = 0
}
pass = true
rspNum = reqType
return
}
func (s *UserExpService) getQueryStatus() (rspConf int) {
if t := conf.Conf.Switch; t != nil {
rspConf = t.QueryExp
} else {
rspConf = 0
}
return
}

View File

@@ -0,0 +1,35 @@
package v1
import (
"context"
v1pb "go-common/app/service/live/xuser/api/grpc/v1"
"go-common/library/net/metadata"
expModel "go-common/app/service/live/xuser/model/exp"
"go-common/library/log"
)
func (s *UserExpService) asyncCLearExpCache(ctx context.Context, req *v1pb.UserExpChunk) (err error) {
err = s.dao.DelExpFromMemCache(ctx, req.Uid)
if err != nil {
log.Error(_errorServiceLogPrefix+"|"+_ERROR_ASYNC_CACHE_FAIL3+"|clear UserExp cache(%d) error(%v)", req.Uid, err)
}
return
}
func (s *UserExpService) asyncSetExpCache(ctx context.Context, req map[int64]*expModel.LevelInfo) error {
c := metadata.WithContext(ctx)
f := func(c context.Context) {
if err := s.dao.SetExpListCache(c, req); err != nil {
log.Error(_errorServiceLogPrefix+"|"+_ERROR_ASYNC_CACHE_FAIL+"|asyncSetExpCache|error(%v),missedUIDs(%v)", err)
}
}
if runErr := s.addExpCache.Save(func() {
f(c)
}); runErr != nil {
log.Error(_errorServiceLogPrefix+"|"+_ERROR_ASYNC_CACHE_FAIL2+"|asyncSetExpCache|error(%v),run cache is full(%v)", runErr)
f(c)
}
return nil
}

View File

@@ -0,0 +1,280 @@
package v1
import (
expm "go-common/app/service/live/xuser/model/exp"
)
const (
_AnchorLevelMax = int64(40)
_UserLevelMax = int64(60)
_ColorLevel0_10 = int64(9868950)
_ColorLevel10_20 = int64(6406234)
_ColorLevel20_30 = int64(5805790)
_ColorLevel30_40 = int64(10512625)
_ColorLevel40_50 = int64(16746162)
_ColorLevel50_60 = int64(16752445)
_AnchorColorLevelDefault = int64(6406234)
_AnchorColorLevel10_20 = int64(5805790)
_AnchorColorLevel20_30 = int64(10512625)
_AnchorColorLevel30_40 = int64(16746162)
_AnchorColorLevel40_50 = int64(16752445)
)
type userLevelInfo struct {
// 当前等级
level int64
// 下一等级
nextLevel int64
// 当前等级对应的经验
userExpLeft int64
// 下一等级对应的经验
userExpRight int64
// 升级到下一等级对应的经验
userExpNextLevel int64
}
var (
_anchorLevelMap = []userLevelInfo{
1: {level: 1, nextLevel: 2, userExpLeft: 0, userExpRight: 5000, userExpNextLevel: 5000},
2: {level: 2, nextLevel: 3, userExpLeft: 5000, userExpRight: 20000, userExpNextLevel: 15000},
3: {level: 3, nextLevel: 4, userExpLeft: 20000, userExpRight: 47000, userExpNextLevel: 27000},
4: {level: 4, nextLevel: 5, userExpLeft: 47000, userExpRight: 92000, userExpNextLevel: 45000},
5: {level: 5, nextLevel: 6, userExpLeft: 92000, userExpRight: 210000, userExpNextLevel: 118000},
6: {level: 6, nextLevel: 7, userExpLeft: 210000, userExpRight: 406000, userExpNextLevel: 196000},
7: {level: 7, nextLevel: 8, userExpLeft: 406000, userExpRight: 716000, userExpNextLevel: 310000},
8: {level: 8, nextLevel: 9, userExpLeft: 716000, userExpRight: 1176000, userExpNextLevel: 460000},
9: {level: 9, nextLevel: 10, userExpLeft: 1176000, userExpRight: 1806000, userExpNextLevel: 630000},
10: {level: 10, nextLevel: 11, userExpLeft: 1806000, userExpRight: 2716000, userExpNextLevel: 910000},
11: {level: 11, nextLevel: 12, userExpLeft: 2716000, userExpRight: 3961000, userExpNextLevel: 1245000},
12: {level: 12, nextLevel: 13, userExpLeft: 3961000, userExpRight: 5641000, userExpNextLevel: 1680000},
13: {level: 13, nextLevel: 14, userExpLeft: 5641000, userExpRight: 7881000, userExpNextLevel: 2240000},
14: {level: 14, nextLevel: 15, userExpLeft: 7881000, userExpRight: 10981000, userExpNextLevel: 3100000},
15: {level: 15, nextLevel: 16, userExpLeft: 10981000, userExpRight: 15481000, userExpNextLevel: 4500000},
16: {level: 16, nextLevel: 17, userExpLeft: 15481000, userExpRight: 22681000, userExpNextLevel: 7200000},
17: {level: 17, nextLevel: 18, userExpLeft: 22681000, userExpRight: 31981000, userExpNextLevel: 9300000},
18: {level: 18, nextLevel: 19, userExpLeft: 31981000, userExpRight: 44281000, userExpNextLevel: 12300000},
19: {level: 19, nextLevel: 20, userExpLeft: 44281000, userExpRight: 60281000, userExpNextLevel: 16000000},
20: {level: 20, nextLevel: 21, userExpLeft: 60281000, userExpRight: 81681000, userExpNextLevel: 21400000},
21: {level: 21, nextLevel: 22, userExpLeft: 81681000, userExpRight: 113881000, userExpNextLevel: 32200000},
22: {level: 22, nextLevel: 23, userExpLeft: 113881000, userExpRight: 159481000, userExpNextLevel: 45600000},
23: {level: 23, nextLevel: 24, userExpLeft: 159481000, userExpRight: 221481000, userExpNextLevel: 62000000},
24: {level: 24, nextLevel: 25, userExpLeft: 221481000, userExpRight: 300481000, userExpNextLevel: 79000000},
25: {level: 25, nextLevel: 26, userExpLeft: 300481000, userExpRight: 398481000, userExpNextLevel: 98000000},
26: {level: 26, nextLevel: 27, userExpLeft: 398481000, userExpRight: 522981000, userExpNextLevel: 124500000},
27: {level: 27, nextLevel: 28, userExpLeft: 522981000, userExpRight: 690981000, userExpNextLevel: 168000000},
28: {level: 28, nextLevel: 29, userExpLeft: 690981000, userExpRight: 901381000, userExpNextLevel: 210400000},
29: {level: 29, nextLevel: 30, userExpLeft: 901381000, userExpRight: 1188381000, userExpNextLevel: 287000000},
30: {level: 30, nextLevel: 31, userExpLeft: 1188381000, userExpRight: 1561381000, userExpNextLevel: 373000000},
31: {level: 31, nextLevel: 32, userExpLeft: 1561381000, userExpRight: 2061381000, userExpNextLevel: 500000000},
32: {level: 32, nextLevel: 33, userExpLeft: 2061381000, userExpRight: 2731381000, userExpNextLevel: 670000000},
33: {level: 33, nextLevel: 34, userExpLeft: 2731381000, userExpRight: 3641381000, userExpNextLevel: 910000000},
34: {level: 34, nextLevel: 35, userExpLeft: 3641381000, userExpRight: 4781381000, userExpNextLevel: 1140000000},
35: {level: 35, nextLevel: 36, userExpLeft: 4781381000, userExpRight: 6201381000, userExpNextLevel: 1420000000},
36: {level: 36, nextLevel: 37, userExpLeft: 6201381000, userExpRight: 7951381000, userExpNextLevel: 1750000000},
37: {level: 37, nextLevel: 38, userExpLeft: 7951381000, userExpRight: 9951381000, userExpNextLevel: 2000000000},
38: {level: 38, nextLevel: 39, userExpLeft: 9951381000, userExpRight: 12201381000, userExpNextLevel: 2250000000},
39: {level: 39, nextLevel: 40, userExpLeft: 12201381000, userExpRight: 14701381000, userExpNextLevel: 2500000000},
40: {level: 40, nextLevel: 40, userExpLeft: 14701381000, userExpRight: 14701381000, userExpNextLevel: 0},
}
_userLevelMap = []userLevelInfo{
0: {level: 0, nextLevel: 1, userExpLeft: 0, userExpRight: 100000, userExpNextLevel: 100000},
1: {level: 1, nextLevel: 2, userExpLeft: 100000, userExpRight: 200000, userExpNextLevel: 100000},
2: {level: 2, nextLevel: 3, userExpLeft: 200000, userExpRight: 300000, userExpNextLevel: 100000},
3: {level: 3, nextLevel: 4, userExpLeft: 300000, userExpRight: 400000, userExpNextLevel: 100000},
4: {level: 4, nextLevel: 5, userExpLeft: 400000, userExpRight: 500000, userExpNextLevel: 100000},
5: {level: 5, nextLevel: 6, userExpLeft: 500000, userExpRight: 600000, userExpNextLevel: 100000},
6: {level: 6, nextLevel: 7, userExpLeft: 600000, userExpRight: 700000, userExpNextLevel: 100000},
7: {level: 7, nextLevel: 8, userExpLeft: 700000, userExpRight: 800000, userExpNextLevel: 100000},
8: {level: 8, nextLevel: 9, userExpLeft: 800000, userExpRight: 900000, userExpNextLevel: 100000},
9: {level: 9, nextLevel: 10, userExpLeft: 900000, userExpRight: 1000000, userExpNextLevel: 100000},
10: {level: 10, nextLevel: 11, userExpLeft: 1000000, userExpRight: 1800000, userExpNextLevel: 800000},
11: {level: 11, nextLevel: 12, userExpLeft: 1800000, userExpRight: 2600000, userExpNextLevel: 800000},
12: {level: 12, nextLevel: 13, userExpLeft: 2600000, userExpRight: 3400000, userExpNextLevel: 800000},
13: {level: 13, nextLevel: 14, userExpLeft: 3400000, userExpRight: 4200000, userExpNextLevel: 800000},
14: {level: 14, nextLevel: 15, userExpLeft: 4200000, userExpRight: 5000000, userExpNextLevel: 800000},
15: {level: 15, nextLevel: 16, userExpLeft: 5000000, userExpRight: 6000000, userExpNextLevel: 1000000},
16: {level: 16, nextLevel: 17, userExpLeft: 6000000, userExpRight: 7000000, userExpNextLevel: 1000000},
17: {level: 17, nextLevel: 18, userExpLeft: 7000000, userExpRight: 8000000, userExpNextLevel: 1000000},
18: {level: 18, nextLevel: 19, userExpLeft: 8000000, userExpRight: 9000000, userExpNextLevel: 1000000},
19: {level: 19, nextLevel: 20, userExpLeft: 9000000, userExpRight: 10000000, userExpNextLevel: 1000000},
20: {level: 20, nextLevel: 21, userExpLeft: 10000000, userExpRight: 18000000, userExpNextLevel: 8000000},
21: {level: 21, nextLevel: 22, userExpLeft: 18000000, userExpRight: 26000000, userExpNextLevel: 8000000},
22: {level: 22, nextLevel: 23, userExpLeft: 26000000, userExpRight: 34000000, userExpNextLevel: 8000000},
23: {level: 23, nextLevel: 24, userExpLeft: 34000000, userExpRight: 42000000, userExpNextLevel: 8000000},
24: {level: 24, nextLevel: 25, userExpLeft: 42000000, userExpRight: 50000000, userExpNextLevel: 8000000},
25: {level: 25, nextLevel: 26, userExpLeft: 50000000, userExpRight: 60000000, userExpNextLevel: 10000000},
26: {level: 26, nextLevel: 27, userExpLeft: 60000000, userExpRight: 70000000, userExpNextLevel: 10000000},
27: {level: 27, nextLevel: 28, userExpLeft: 70000000, userExpRight: 80000000, userExpNextLevel: 10000000},
28: {level: 28, nextLevel: 29, userExpLeft: 80000000, userExpRight: 90000000, userExpNextLevel: 10000000},
29: {level: 29, nextLevel: 30, userExpLeft: 90000000, userExpRight: 100000000, userExpNextLevel: 10000000},
30: {level: 30, nextLevel: 31, userExpLeft: 100000000, userExpRight: 110000000, userExpNextLevel: 10000000},
31: {level: 31, nextLevel: 32, userExpLeft: 110000000, userExpRight: 120000000, userExpNextLevel: 10000000},
32: {level: 32, nextLevel: 33, userExpLeft: 120000000, userExpRight: 130000000, userExpNextLevel: 10000000},
33: {level: 33, nextLevel: 34, userExpLeft: 130000000, userExpRight: 140000000, userExpNextLevel: 10000000},
34: {level: 34, nextLevel: 35, userExpLeft: 140000000, userExpRight: 150000000, userExpNextLevel: 10000000},
35: {level: 35, nextLevel: 36, userExpLeft: 150000000, userExpRight: 180000000, userExpNextLevel: 30000000},
36: {level: 36, nextLevel: 37, userExpLeft: 180000000, userExpRight: 210000000, userExpNextLevel: 30000000},
37: {level: 37, nextLevel: 38, userExpLeft: 210000000, userExpRight: 240000000, userExpNextLevel: 30000000},
38: {level: 38, nextLevel: 39, userExpLeft: 240000000, userExpRight: 270000000, userExpNextLevel: 30000000},
39: {level: 39, nextLevel: 40, userExpLeft: 270000000, userExpRight: 300000000, userExpNextLevel: 30000000},
40: {level: 40, nextLevel: 41, userExpLeft: 300000000, userExpRight: 340000000, userExpNextLevel: 40000000},
41: {level: 41, nextLevel: 42, userExpLeft: 340000000, userExpRight: 380000000, userExpNextLevel: 40000000},
42: {level: 42, nextLevel: 43, userExpLeft: 380000000, userExpRight: 420000000, userExpNextLevel: 40000000},
43: {level: 43, nextLevel: 44, userExpLeft: 420000000, userExpRight: 460000000, userExpNextLevel: 40000000},
44: {level: 44, nextLevel: 45, userExpLeft: 460000000, userExpRight: 500000000, userExpNextLevel: 40000000},
45: {level: 45, nextLevel: 46, userExpLeft: 500000000, userExpRight: 550000000, userExpNextLevel: 50000000},
46: {level: 46, nextLevel: 47, userExpLeft: 550000000, userExpRight: 600000000, userExpNextLevel: 50000000},
47: {level: 47, nextLevel: 48, userExpLeft: 600000000, userExpRight: 700000000, userExpNextLevel: 100000000},
48: {level: 48, nextLevel: 49, userExpLeft: 700000000, userExpRight: 800000000, userExpNextLevel: 100000000},
49: {level: 49, nextLevel: 50, userExpLeft: 800000000, userExpRight: 1000000000, userExpNextLevel: 200000000},
50: {level: 50, nextLevel: 51, userExpLeft: 1000000000, userExpRight: 1200000000, userExpNextLevel: 200000000},
51: {level: 51, nextLevel: 52, userExpLeft: 1200000000, userExpRight: 1400000000, userExpNextLevel: 200000000},
52: {level: 52, nextLevel: 53, userExpLeft: 1400000000, userExpRight: 1600000000, userExpNextLevel: 200000000},
53: {level: 53, nextLevel: 54, userExpLeft: 1600000000, userExpRight: 1800000000, userExpNextLevel: 200000000},
54: {level: 54, nextLevel: 55, userExpLeft: 1800000000, userExpRight: 2000000000, userExpNextLevel: 200000000},
55: {level: 55, nextLevel: 56, userExpLeft: 2000000000, userExpRight: 2200000000, userExpNextLevel: 200000000},
56: {level: 56, nextLevel: 57, userExpLeft: 2200000000, userExpRight: 2400000000, userExpNextLevel: 200000000},
57: {level: 57, nextLevel: 58, userExpLeft: 2400000000, userExpRight: 2600000000, userExpNextLevel: 200000000},
58: {level: 58, nextLevel: 59, userExpLeft: 2600000000, userExpRight: 2800000000, userExpNextLevel: 200000000},
59: {level: 59, nextLevel: 60, userExpLeft: 2800000000, userExpRight: 3000000000, userExpNextLevel: 200000000},
60: {level: 60, nextLevel: 60, userExpLeft: 3000000000, userExpRight: 3000000000, userExpNextLevel: 0},
}
)
// FormatLevel ...
// 等级转换
func (s *UserExpService) FormatLevel(exps []*expm.Exp) (level map[int64]*expm.LevelInfo) {
level = make(map[int64]*expm.LevelInfo)
if len(exps) <= 0 {
return
}
for _, v := range exps {
uid := v.UID
level[uid] = &expm.LevelInfo{}
level[uid].UID = uid
level[uid].UserLevel = s.getUserLevel(v)
level[uid].AnchorLevel = s.getAnchorLevel(v)
level[uid].CTime = v.CTime
level[uid].MTime = v.MTime
}
return
}
func (s *UserExpService) getUserLevel(originDBResult *expm.Exp) (userLevel expm.UserLevelInfo) {
userLevel = expm.UserLevelInfo{Level: -1}
for k, value := range _userLevelMap {
if originDBResult.Uexp < value.userExpRight {
userLevel.Level = value.level
userLevel.NextLevel = value.nextLevel
userLevel.UserExpLeft = value.userExpLeft
userLevel.UserExpRight = value.userExpRight
userLevel.UserExp = originDBResult.Uexp
userLevel.UserExpNextLevel = value.userExpNextLevel
nextLevel := k + 1
if nextLevel < len(_userLevelMap) {
userLevel.UserExpNextLeft = _userLevelMap[nextLevel].userExpLeft
userLevel.UserExpNextRight = _userLevelMap[nextLevel].userExpRight
userLevel.IsLevelTop = 0
}
break
}
}
if userLevel.Level == -1 {
userLevel.Level = _userLevelMap[_UserLevelMax].level
userLevel.NextLevel = _userLevelMap[_UserLevelMax].nextLevel
userLevel.UserExpLeft = _userLevelMap[_UserLevelMax].userExpLeft
userLevel.UserExpRight = _userLevelMap[_UserLevelMax].userExpRight
userLevel.UserExp = originDBResult.Uexp
userLevel.UserExpNextLevel = _userLevelMap[_UserLevelMax].userExpNextLevel
userLevel.UserExpNextLeft = _userLevelMap[_UserLevelMax].userExpLeft
userLevel.UserExpNextRight = _userLevelMap[_UserLevelMax].userExpRight
userLevel.IsLevelTop = 1
}
userLevel.Color = s.getUserLevelColor(userLevel.Level)
return
}
func (s *UserExpService) getAnchorLevel(originDBResult *expm.Exp) (anchorLevel expm.AnchorLevelInfo) {
anchorLevel = expm.AnchorLevelInfo{Level: -1}
for k, value := range _anchorLevelMap {
if originDBResult.Rexp < value.userExpRight {
anchorLevel.Level = value.level
anchorLevel.NextLevel = value.nextLevel
anchorLevel.UserExpLeft = value.userExpLeft
anchorLevel.UserExpRight = value.userExpRight
anchorLevel.UserExp = originDBResult.Rexp
anchorLevel.UserExpNextLevel = value.userExpNextLevel
if anchorLevel.UserExp == 0 {
anchorLevel.AnchorScore = 0
} else {
anchorLevel.AnchorScore = anchorLevel.UserExp / 100
}
nextLevel := k + 1
if nextLevel < len(_userLevelMap) {
anchorLevel.UserExpNextLeft = _userLevelMap[nextLevel].userExpLeft
anchorLevel.UserExpNextRight = _userLevelMap[nextLevel].userExpRight
anchorLevel.IsLevelTop = 0
}
break
}
}
if anchorLevel.Level == -1 {
anchorLevel.Level = _anchorLevelMap[_AnchorLevelMax].level
anchorLevel.NextLevel = _anchorLevelMap[_AnchorLevelMax].nextLevel
anchorLevel.UserExpLeft = _anchorLevelMap[_AnchorLevelMax].userExpLeft
anchorLevel.UserExpRight = _anchorLevelMap[_AnchorLevelMax].userExpRight
anchorLevel.UserExp = originDBResult.Rexp
anchorLevel.UserExpNextLevel = _anchorLevelMap[_AnchorLevelMax].userExpNextLevel
anchorLevel.UserExpNextLeft = _userLevelMap[_UserLevelMax].userExpLeft
anchorLevel.UserExpNextRight = _userLevelMap[_UserLevelMax].userExpRight
anchorLevel.IsLevelTop = 1
if anchorLevel.UserExp == 0 {
anchorLevel.AnchorScore = 0
} else {
anchorLevel.AnchorScore = anchorLevel.UserExp / 100
}
}
anchorLevel.Color = s.getAnchorLevelColor(anchorLevel.Level)
return
}
func (s *UserExpService) getUserLevelColor(level int64) (color int64) {
switch {
case level <= 10:
color = _ColorLevel0_10
case level <= 20:
color = _ColorLevel10_20
case level <= 30:
color = _ColorLevel20_30
case level <= 40:
color = _ColorLevel30_40
case level <= 50:
color = _ColorLevel40_50
default:
color = _ColorLevel50_60
}
return
}
func (s *UserExpService) getAnchorLevelColor(level int64) (color int64) {
switch {
case level <= 10:
color = _AnchorColorLevelDefault
case level <= 20:
color = _AnchorColorLevel10_20
case level <= 30:
color = _AnchorColorLevel20_30
case level <= 40:
color = _AnchorColorLevel30_40
case level <= 50:
color = _AnchorColorLevel40_50
default:
color = _AnchorColorLevel40_50
}
return
}

View File

@@ -0,0 +1,18 @@
package v1
const (
_ERROR_ADD_USEREXP_UPDATEDB_RETRYALL_FAIL = "1008004"
_ERROR_QUERY_AFTER_ADD_USEREXP_UPDATEDB_FAIL = "1008006"
_INFO_ADD_USEREXP_RETRY_UPDATEDB = "UI1"
_ERROR_GETEXP_DB_FAIL = "1008025"
_ERROR_ASYNC_CACHE_FAIL = "SE1"
_ERROR_ASYNC_CACHE_FAIL2 = "SE2"
_ERROR_ASYNC_CACHE_FAIL3 = "SE3"
_ERROR_QUERY_CACHE_FAIL = "1008024"
_ERROR_ADD_UEXP_PARA_NIL = "1008005"
_ERROR_ADD_UEXP_REQ_BIZ_FAIL = "1008001"
_ERROR_ADD_UEXP_TYPE_FAIL = "1008002"
_ERROR_ADD_UEXP_NUM_FAIL = "1008003"
_ERROR_ASYNADD_UEXP_NUM_FAIL = "1008010"
_ERROR_ASYNADD_REXP_NUM_FAIL = "1002150"
)

View File

@@ -0,0 +1,123 @@
package v1
import (
"context"
"go-common/library/ecode"
"go-common/library/net/metadata"
v1pb "go-common/app/service/live/xuser/api/grpc/v1"
expm "go-common/app/service/live/xuser/model/exp"
XanchorV1 "go-common/app/service/live/dao-anchor/api/grpc/v1"
"go-common/library/log"
)
func (s *UserExpService) asyncUpdateDBUexp(ctx context.Context, req *v1pb.UserExpChunk) error {
ctxNodeadline := metadata.WithContext(ctx)
f := func(c context.Context) {
reqStartTime := s.RecordTimeCost()
retryTime := 1
_, err := s.dao.AddUexp(c, req.Uid, req.Num)
if err != nil {
for ; retryTime <= _retryAddExpTimes; retryTime++ {
_, err = s.dao.AddUexp(c, req.Uid, req.Num)
if err != nil {
break
}
}
if retryTime >= _retryAddExpTimes {
reqUpdateDBFail := s.RecordTimeCost()
log.Error(_errorServiceLogPrefix+"|"+_ERROR_ADD_USEREXP_UPDATEDB_RETRYALL_FAIL+"|asyncUpdateDBUexp|DB error(%v),params(%v)|耗时:%dms", err, req, reqUpdateDBFail-reqStartTime)
err = ecode.XUserAddUserExpUpdateDBError
return
}
log.Info(_errorServiceLogPrefix+"|asyncUpdateDBUexp"+_INFO_ADD_USEREXP_RETRY_UPDATEDB+"|DB retry %d times success,params(%v)", retryTime, req)
}
s.asyncCLearExpCache(c, req)
QueryConfig := s.getQueryStatus()
if QueryConfig == 1 {
reqBeforeQueryDBTime := s.RecordTimeCost()
expResult, err := s.dao.Exp(c, req.Uid)
reqAfterQueryDBTime := s.RecordTimeCost()
if err != nil {
log.Error(_errorServiceLogPrefix+"|asyncUpdateDBUexp|"+_ERROR_QUERY_AFTER_ADD_USEREXP_UPDATEDB_FAIL+"|更新db后再查询error|Query error(%v),params(%v),resp(%v)|耗时:%dms", err, req, expResult, reqAfterQueryDBTime-reqBeforeQueryDBTime)
} else {
expResultList := make([]*expm.Exp, 0)
expResultList = append(expResultList, expResult)
item := s.FormatLevel(expResultList)
s.asyncSetExpCache(c, item)
logDesc, ok := _addExpReqBizMap[req.ReqBiz]
if !ok {
logDesc = "默认渠道"
}
s.addUserExpLog(req.Uid, req.Num, item, logDesc)
}
}
}
if runErr := s.dbUpdater.Do(ctxNodeadline, func(c context.Context) {
f(ctxNodeadline)
}); runErr != nil {
log.Error(_errorServiceLogPrefix+"|"+_ERROR_ASYNADD_UEXP_NUM_FAIL+"|asyncSetExpCache|error(%v),run cache is full(%v)", runErr)
f(ctxNodeadline)
}
return nil
}
func (s *UserExpService) asyncUpdateDBRexp(ctx context.Context, req *v1pb.UserExpChunk) error {
ctxNodeadline := metadata.WithContext(ctx)
f := func(c context.Context) {
reqStartTime := s.RecordTimeCost()
retryTime := 1
_, err := s.dao.AddRexp(c, req.Uid, req.Num)
if err != nil {
for ; retryTime <= _retryAddExpTimes; retryTime++ {
_, err = s.dao.AddRexp(c, req.Uid, req.Num)
if err != nil {
break
}
}
if retryTime >= _retryAddExpTimes {
reqUpdateDBFail := s.RecordTimeCost()
log.Error(_errorServiceLogPrefix+"|"+_ERROR_ADD_USEREXP_UPDATEDB_RETRYALL_FAIL+"|asyncUpdateDBRexp|DB error(%v),params(%v)|耗时:%dms", err, req, reqUpdateDBFail-reqStartTime)
err = ecode.XUserAddUserExpUpdateDBError
return
}
log.Info(_errorServiceLogPrefix+"|asyncUpdateDBRexp"+_INFO_ADD_USEREXP_RETRY_UPDATEDB+"|DB retry %d times success,params(%v)", retryTime, req)
}
params := &XanchorV1.AnchorIncreReq{ReqId: "AnchorIncreReq", Fields: []string{"exp"}, Uid: req.Uid, Exp: req.Num}
err = s.xuserDao.UpdateAnchorInfo(c, params)
if err != nil {
reqUpdateDaoDBFail := s.RecordTimeCost()
log.Error(_errorServiceLogPrefix+"|asyncUpdateDBRexp|"+_ERROR_ASYNADD_REXP_NUM_FAIL+"|主播经验双写error|Query error(%v),params(%v)|耗时:%dms", err, req, reqUpdateDaoDBFail-reqStartTime)
}
s.asyncCLearExpCache(c, req)
QueryConfig := s.getQueryStatus()
if QueryConfig == 1 {
reqBeforeQueryDBTime := s.RecordTimeCost()
expResult, err := s.dao.Exp(c, req.Uid)
reqAfterQueryDBTime := s.RecordTimeCost()
if err != nil {
log.Error(_errorServiceLogPrefix+"|asyncUpdateDBRexp|"+_ERROR_QUERY_AFTER_ADD_USEREXP_UPDATEDB_FAIL+"|更新db后再查询error|Query error(%v),params(%v),resp(%v)|耗时:%dms", err, req, expResult, reqAfterQueryDBTime-reqBeforeQueryDBTime)
} else {
expResultList := make([]*expm.Exp, 0)
expResultList = append(expResultList, expResult)
item := s.FormatLevel(expResultList)
s.asyncSetExpCache(c, item)
logDesc, ok := _addExpReqBizMap[req.ReqBiz]
if !ok {
logDesc = "默认渠道"
}
s.addAnchorExpLog(req.Uid, req.Num, item, logDesc)
}
}
}
if runErr := s.dbUpdater.Do(ctxNodeadline, func(c context.Context) {
f(ctxNodeadline)
}); runErr != nil {
log.Error(_errorServiceLogPrefix+"|"+_ERROR_ASYNADD_UEXP_NUM_FAIL+"|asyncSetExpCache|error(%v),run cache is full(%v)", runErr)
f(ctxNodeadline)
}
return nil
}

View File

@@ -0,0 +1,211 @@
package v1
import (
"context"
"github.com/pkg/errors"
v1pb "go-common/app/service/live/xuser/api/grpc/v1"
"go-common/app/service/live/xuser/conf"
"go-common/app/service/live/xuser/dao/exp"
XanchorDao "go-common/app/service/live/xuser/dao/xanchor"
expm "go-common/app/service/live/xuser/model/exp"
"go-common/library/cache"
"go-common/library/ecode"
"go-common/library/log"
"go-common/library/stat/prom"
"go-common/library/sync/pipeline/fanout"
)
// UserExpService struct
type UserExpService struct {
conf *conf.Config
// optionally add other properties here, such as dao
dao *exp.Dao
addExpCache *cache.Cache
dbUpdater *fanout.Fanout
xuserDao *XanchorDao.Dao
}
const (
_errorServiceLogPrefix = "xuser.exp.service"
_promCacheMissed = "xuser_exp_mc:用户经验cache miss"
_promCacheHitAll = "xuser_exp_mc:用户经验cache全部命中"
_retryAddExpTimes = 3
)
// NewUserExpService init
func NewUserExpService(c *conf.Config) (s *UserExpService) {
s = &UserExpService{
conf: c,
dao: exp.NewExpDao(c),
addExpCache: cache.New(1, 10240),
dbUpdater: fanout.New("jibamao", fanout.Worker(1), fanout.Buffer(10240)),
xuserDao: XanchorDao.New(c),
}
return s
}
// Close close exp service
func (s *UserExpService) Close() {
s.dao.Close()
}
func (s *UserExpService) adaptResultFromMemCache(expList map[int64]*expm.LevelInfo) (resp *v1pb.GetUserExpResp) {
resp = &v1pb.GetUserExpResp{}
resp.Data = make(map[int64]*v1pb.LevelInfo)
for _, v := range expList {
resp.Data[v.UID] = &v1pb.LevelInfo{}
resp.Data[v.UID] = s.adaptAPIModel(v)
}
return
}
func (s *UserExpService) adaptResultFromDBMemCache(expFromMc map[int64]*expm.LevelInfo, expFromDB map[int64]*expm.LevelInfo) (resp *v1pb.GetUserExpResp) {
resp = &v1pb.GetUserExpResp{}
resp.Data = make(map[int64]*v1pb.LevelInfo)
if len(expFromMc) <= 0 && len(expFromDB) <= 0 {
return
} else if len(expFromMc) <= 0 {
for _, v := range expFromDB {
resp.Data[v.UID] = &v1pb.LevelInfo{}
resp.Data[v.UID] = s.adaptAPIModel(v)
}
} else if len(expFromDB) <= 0 {
for _, v := range expFromMc {
resp.Data[v.UID] = &v1pb.LevelInfo{}
resp.Data[v.UID] = s.adaptAPIModel(v)
}
} else {
for _, v := range expFromMc {
resp.Data[v.UID] = &v1pb.LevelInfo{}
resp.Data[v.UID] = s.adaptAPIModel(v)
}
for _, v := range expFromDB {
resp.Data[v.UID] = &v1pb.LevelInfo{}
resp.Data[v.UID] = s.adaptAPIModel(v)
}
}
return
}
// 直播用户经验gRPC接口
// GetUserExp ...
// 获取用户经验与等级信息,支持批量
func (s *UserExpService) GetUserExp(ctx context.Context, req *v1pb.GetUserExpReq) (resp *v1pb.GetUserExpResp, err error) {
reqStartTime := s.RecordTimeCost()
// s.RecordTimeCostLog(reqStartTime, "G0|获取经验入口耗时")
resp = &v1pb.GetUserExpResp{}
resp.Data = make(map[int64]*v1pb.LevelInfo)
cacheHealth := true
expResultFromMc, missedUIDs, err := s.dao.GetExpFromMemCache(ctx, req.Uids)
// 回源db原则,仅在get成功且miss时回源db!!!
if err != nil {
reqAfterQueryMCTime := s.RecordTimeCost()
log.Error(_errorServiceLogPrefix+"|"+_ERROR_QUERY_CACHE_FAIL+"|GetUserExp|查询缓存失败,暂不回源db,接口返回err|%dms", err, reqAfterQueryMCTime-reqStartTime)
err = errors.WithMessage(ecode.XUserExpGetExpMcFail, "获取用户经验缓存失败")
cacheHealth = false
return
} else if len(missedUIDs) == 0 {
resp = s.adaptResultFromMemCache(expResultFromMc)
exp.PromCacheHit(_promCacheHitAll)
return
}
exp.PromCacheMiss(_promCacheMissed)
prom.CacheMiss.Incr("expList_mc")
resultDB, err := s.dao.MultiExp(ctx, missedUIDs)
if err != nil {
reqAfterQueryDBTime := s.RecordTimeCost()
log.Error(_errorServiceLogPrefix+"|"+_ERROR_GETEXP_DB_FAIL+"|GetUserExp|获取用户经验回源DB失败error|(%v),missedUIDs(%v)|耗时:%dms", err, missedUIDs, reqAfterQueryDBTime-reqStartTime)
err = errors.WithMessage(ecode.XUserExpGetExpDBFail, "获取用户经验回源DB失败")
return
}
expResultFromDB := s.FormatLevel(resultDB)
// 写入缓存
if cacheHealth {
s.asyncSetExpCache(ctx, expResultFromDB)
}
resp = s.adaptResultFromDBMemCache(expResultFromMc, expResultFromDB)
return
}
// AddUserExp ...
// 增加用户经验,支持批量
func (s *UserExpService) AddUserExp(ctx context.Context, req *v1pb.AddUserExpReq) (resp *v1pb.AddUserExpResp, err error) {
resp = &v1pb.AddUserExpResp{}
if req == nil {
err = ecode.XUserAddUserExpParamsEmptyError
log.Error(_errorServiceLogPrefix+"|"+_ERROR_ADD_UEXP_PARA_NIL+"|AddUserExp|s.dao.Exp|error(%v)|入参校验错误", err)
err = errors.WithMessage(ecode.ResourceParamErr, "添加用户经验入参异常")
return
}
userInfo := req.UserInfo
allow, _ := s.checkReqBiz(userInfo.ReqBiz)
if !allow {
log.Error(_errorServiceLogPrefix+"|"+_ERROR_ADD_UEXP_REQ_BIZ_FAIL+"|AddUserExp|error(%v),reqBiz(%d)is not register yet", err, userInfo.ReqBiz)
err = ecode.XUserAddUserExpReqBizNotAllow
return
}
typeAllow, _ := s.checkAddType(userInfo.Type)
if !typeAllow {
log.Error(_errorServiceLogPrefix+"|"+_ERROR_ADD_UEXP_TYPE_FAIL+"|AddUserExp|error(%v),addType(%d)is not allow yet", err, userInfo.Type)
err = ecode.XUserAddUserExpTypeNotAllow
return
}
numAllow, _ := s.checkAddNum(userInfo.Num)
if !numAllow {
log.Error(_errorServiceLogPrefix+"|"+_ERROR_ADD_UEXP_NUM_FAIL+"|AddUserExp|error(%v),addNum(%d)is not allow yet", err, userInfo.Num)
err = ecode.XUserAddUserExpNumNotAllow
}
s.doAddExp(ctx, userInfo)
return
}
func (s *UserExpService) doAddExp(ctx context.Context, req *v1pb.UserExpChunk) (err error) {
switch req.Type {
case _addUserExpType:
{
return s.asyncUpdateDBUexp(ctx, req)
}
case _addAnchorExpType:
{
return s.asyncUpdateDBRexp(ctx, req)
}
}
return
}
func (s *UserExpService) adaptAPIModel(dbModel *expm.LevelInfo) (apiModel *v1pb.LevelInfo) {
apiModel = &v1pb.LevelInfo{}
apiModel.UserLevel = &v1pb.UserLevelInfo{}
apiModel.AnchorLevel = &v1pb.AnchorLevelInfo{}
apiModel.Uid = dbModel.UID
apiModel.UserLevel.UserExpNextRight = dbModel.UserLevel.UserExpNextRight
apiModel.UserLevel.IsLevelTop = dbModel.UserLevel.IsLevelTop
apiModel.UserLevel.UserExpNextLeft = dbModel.UserLevel.UserExpNextLeft
apiModel.UserLevel.Level = dbModel.UserLevel.Level
apiModel.UserLevel.NextLevel = dbModel.UserLevel.NextLevel
apiModel.UserLevel.UserExpLeft = dbModel.UserLevel.UserExpLeft
apiModel.UserLevel.UserExpRight = dbModel.UserLevel.UserExpRight
apiModel.UserLevel.UserExp = dbModel.UserLevel.UserExp
apiModel.UserLevel.UserExpNextLevel = dbModel.UserLevel.UserExpNextLevel
apiModel.UserLevel.Color = dbModel.UserLevel.Color
apiModel.AnchorLevel.UserExpNextRight = dbModel.AnchorLevel.UserExpNextRight
apiModel.AnchorLevel.IsLevelTop = dbModel.AnchorLevel.IsLevelTop
apiModel.AnchorLevel.UserExpNextLeft = dbModel.AnchorLevel.UserExpNextLeft
apiModel.AnchorLevel.Level = dbModel.AnchorLevel.Level
apiModel.AnchorLevel.NextLevel = dbModel.AnchorLevel.NextLevel
apiModel.AnchorLevel.UserExpLeft = dbModel.AnchorLevel.UserExpLeft
apiModel.AnchorLevel.UserExpRight = dbModel.AnchorLevel.UserExpRight
apiModel.AnchorLevel.UserExp = dbModel.AnchorLevel.UserExp
apiModel.AnchorLevel.UserExpNextLevel = dbModel.AnchorLevel.UserExpNextLevel
apiModel.AnchorLevel.Color = dbModel.AnchorLevel.Color
apiModel.AnchorLevel.AnchorScore = dbModel.AnchorLevel.AnchorScore
return
}

View File

@@ -0,0 +1,51 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = [
"clear_cache.go",
"dahanghai.go",
"guard.go",
"log_debug.go",
],
importpath = "go-common/app/service/live/xuser/service/guard/v1",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/service/live/room/api/liverpc:go_default_library",
"//app/service/live/room/api/liverpc/v2:go_default_library",
"//app/service/live/xuser/api/grpc/v1:go_default_library",
"//app/service/live/xuser/conf:go_default_library",
"//app/service/live/xuser/dao/account:go_default_library",
"//app/service/live/xuser/dao/exp:go_default_library",
"//app/service/live/xuser/dao/guard:go_default_library",
"//app/service/live/xuser/model:go_default_library",
"//app/service/live/xuser/model/dhh:go_default_library",
"//library/cache:go_default_library",
"//library/ecode:go_default_library",
"//library/log:go_default_library",
"//library/net/http/blademaster:go_default_library",
"//library/net/metadata:go_default_library",
"//library/queue/databus:go_default_library",
"//vendor/github.com/pkg/errors:go_default_library",
],
)
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,114 @@
package v1
import (
"context"
dahanghaiModel "go-common/app/service/live/xuser/model/dhh"
"go-common/library/net/metadata"
"go-common/library/log"
)
func (s *GuardService) asyncCLearExpCache(ctx context.Context, uid int64) (err error) {
err = s.dao.DelDHHFromRedis(ctx, uid)
if err != nil {
log.Error(_errorServiceLogPrefix+"|clear UserExp cache(%d) error(%v)", uid, err)
}
return
}
func (s *GuardService) asyncSetDHHCache(ctx context.Context, req []*dahanghaiModel.DaHangHaiRedis2, uid int64) error {
c := metadata.WithContext(ctx)
dbreq := make([]dahanghaiModel.DaHangHaiRedis2, 0)
for _, v := range req {
dbreqItem := dahanghaiModel.DaHangHaiRedis2{}
dbreqItem.Id = v.Id
dbreqItem.Uid = v.Uid
dbreqItem.TargetId = v.TargetId
dbreqItem.PrivilegeType = v.PrivilegeType
dbreqItem.StartTime = v.StartTime
dbreqItem.ExpiredTime = v.ExpiredTime
dbreqItem.Ctime = v.Ctime
dbreqItem.Utime = v.Utime
dbreq = append(dbreq, dbreqItem)
}
f := func(c context.Context) {
if err := s.dao.SetDHHListCache(c, dbreq, uid); err != nil {
log.Error(_errorServiceLogPrefix+"|asyncSetDHHCache|error(%v),missedUIDs(%v)", err)
}
}
if runErr := s.async.Save(func() {
f(c)
}); runErr != nil {
log.Error(_errorServiceLogPrefix+"|asyncSetDHHCache|error(%v),run cache is full(%v)", runErr)
f(c)
}
return nil
}
func (s *GuardService) asyncSetDHHCacheBatch(ctx context.Context, req map[int64][]*dahanghaiModel.DaHangHaiRedis2) error {
c := metadata.WithContext(ctx)
dbreq := make(map[int64][]dahanghaiModel.DaHangHaiRedis2)
dbreqList := make([]dahanghaiModel.DaHangHaiRedis2, 0)
for k, v := range req {
for _, vv := range v {
dbreqItem := dahanghaiModel.DaHangHaiRedis2{}
dbreqItem.Id = vv.Id
dbreqItem.Uid = vv.Uid
dbreqItem.TargetId = vv.TargetId
dbreqItem.PrivilegeType = vv.PrivilegeType
dbreqItem.StartTime = vv.StartTime
dbreqItem.ExpiredTime = vv.ExpiredTime
dbreqItem.Ctime = vv.Ctime
dbreqItem.Utime = vv.Utime
dbreqList = append(dbreqList, dbreqItem)
}
if _, exist := dbreq[k]; !exist {
dbreq[k] = make([]dahanghaiModel.DaHangHaiRedis2, 0)
}
dbreq[k] = dbreqList
}
f := func(c context.Context) {
for k, v := range dbreq {
if err := s.dao.SetDHHListCache(c, v, k); err != nil {
log.Error(_errorServiceLogPrefix+"|asyncSetDHHCache|error(%v),missedUIDs(%v)", err)
}
}
}
if runErr := s.asyncMulti.Save(func() {
f(c)
}); runErr != nil {
log.Error(_errorServiceLogPrefix+"|asyncSetDHHCache|error(%v),run cache is full(%v)", runErr)
f(c)
}
return nil
}
func (s *GuardService) asyncSetAnchorGuardCache(ctx context.Context, req []*dahanghaiModel.DaHangHaiRedis2, uid int64) error {
c := metadata.WithContext(ctx)
dbreq := make([]dahanghaiModel.DaHangHaiRedis2, 0)
for _, v := range req {
dbreqItem := dahanghaiModel.DaHangHaiRedis2{}
dbreqItem.Id = v.Id
dbreqItem.Uid = v.Uid
dbreqItem.TargetId = v.TargetId
dbreqItem.PrivilegeType = v.PrivilegeType
dbreqItem.StartTime = v.StartTime
dbreqItem.ExpiredTime = v.ExpiredTime
dbreqItem.Ctime = v.Ctime
dbreqItem.Utime = v.Utime
dbreq = append(dbreq, dbreqItem)
}
f := func(c context.Context) {
if err := s.dao.SetAnchorGuardListCache(c, dbreq, uid); err != nil {
log.Error(_errorServiceLogPrefix+"|asyncSetAnchorGuardCache|error(%v),missedUIDs(%v)", err)
}
}
if runErr := s.async.Save(func() {
f(c)
}); runErr != nil {
log.Error(_errorServiceLogPrefix+"|asyncSetAnchorGuardCache|error(%v),run cache is full(%v)", runErr)
f(c)
}
return nil
}

View File

@@ -0,0 +1,888 @@
package v1
import (
"context"
"github.com/pkg/errors"
v1pb "go-common/app/service/live/xuser/api/grpc/v1"
"go-common/app/service/live/xuser/dao/exp"
"go-common/library/ecode"
"strconv"
"time"
confm "go-common/app/service/live/xuser/conf"
dhhm "go-common/app/service/live/xuser/model/dhh"
"go-common/library/log"
dahanghaiModel "go-common/app/service/live/xuser/model/dhh"
)
const (
_errorServiceLogPrefix = "xuser.dhh.service"
_promCacheMissed = "xuser_dhh_redis:用户守护cache miss"
_promAnchorCacheMissed = "xuser_dhh_redis:主播侧守护cache miss"
_promCacheHitAll = "xuser_dhh_redis:用户守护cache全部命中"
_promAnchorCacheHitAll = "xuser_dhh_redis:主播侧守护cache全部命中"
// _retryAddExpTimes = 3
_formatTime = "2006-01-02 15:04:05"
_default = 0
_zongdu = 1
_tidu = 2
_jianzhang = 3
)
// RParseInt 转换
func RParseInt(inputStr string, defaultValue int64) (output int64) {
if mid, err := strconv.ParseInt(inputStr, 10, 64); err == nil {
output = mid
} else {
output = defaultValue
}
return
}
func (s *GuardService) adaptResultFromRedis(uid int64, targetID int64, sortType int64, dhhList []*dahanghaiModel.DaHangHaiRedis2) (result map[int64]*v1pb.DaHangHaiInfo) {
resultP1 := make([]*v1pb.DaHangHaiInfo, 0)
resultP2 := make([]*v1pb.DaHangHaiInfo, 0)
resultP3 := make([]*v1pb.DaHangHaiInfo, 0)
for _, v := range dhhList {
TargetID := RParseInt(v.TargetId, 0)
PrivilegeType := RParseInt(v.PrivilegeType, 0)
if TargetID == targetID {
respItem := &v1pb.DaHangHaiInfo{}
respItem.Id = RParseInt(v.Id, 0)
respItem.Uid = RParseInt(v.Uid, 0)
respItem.TargetId = RParseInt(v.TargetId, 0)
respItem.PrivilegeType = RParseInt(v.PrivilegeType, 0)
respItem.StartTime = v.StartTime
respItem.ExpiredTime = v.ExpiredTime
respItem.Ctime = v.Ctime
respItem.Utime = v.Utime
switch PrivilegeType {
case _zongdu:
{
resultP1 = append(resultP1, respItem)
}
case _tidu:
{
resultP2 = append(resultP2, respItem)
}
case _jianzhang:
{
resultP3 = append(resultP3, respItem)
}
}
}
}
result = s.mergeResult(resultP1, resultP2, resultP3, sortType)
return
}
func (s *GuardService) filterUIDTopFromRedis(uid int64, dhhList []*dahanghaiModel.DaHangHaiRedis2) (result map[int64]*v1pb.DaHangHaiInfo) {
result = make(map[int64]*v1pb.DaHangHaiInfo)
for _, v := range dhhList {
PrivilegeType := RParseInt(v.PrivilegeType, 0)
if len(result) <= 0 {
respItem := &v1pb.DaHangHaiInfo{}
respItem.Id = RParseInt(v.Id, 0)
respItem.Uid = RParseInt(v.Uid, 0)
respItem.TargetId = RParseInt(v.TargetId, 0)
respItem.PrivilegeType = RParseInt(v.PrivilegeType, 0)
respItem.StartTime = v.StartTime
respItem.ExpiredTime = v.ExpiredTime
respItem.Ctime = v.Ctime
respItem.Utime = v.Utime
result[uid] = respItem
} else {
if result[uid].PrivilegeType > PrivilegeType {
respItem := &v1pb.DaHangHaiInfo{}
respItem.Id = RParseInt(v.Id, 0)
respItem.Uid = RParseInt(v.Uid, 0)
respItem.TargetId = RParseInt(v.TargetId, 0)
respItem.PrivilegeType = RParseInt(v.PrivilegeType, 0)
respItem.StartTime = v.StartTime
respItem.ExpiredTime = v.ExpiredTime
respItem.Ctime = v.Ctime
respItem.Utime = v.Utime
result[uid] = respItem
}
}
}
return
}
func (s *GuardService) getAnchorTopGuardCount(uid int64, dhhList []*dahanghaiModel.DaHangHaiRedis2) (result map[int64]*v1pb.DaHangHaiInfo) {
result = make(map[int64]*v1pb.DaHangHaiInfo)
for _, v := range dhhList {
PrivilegeType := RParseInt(v.PrivilegeType, 0)
UID := RParseInt(v.Uid, 0)
if _, exist := result[UID]; !exist {
respItem := &v1pb.DaHangHaiInfo{}
respItem.Id = RParseInt(v.Id, 0)
respItem.Uid = RParseInt(v.Uid, 0)
respItem.TargetId = RParseInt(v.TargetId, 0)
respItem.PrivilegeType = RParseInt(v.PrivilegeType, 0)
respItem.StartTime = v.StartTime
respItem.ExpiredTime = v.ExpiredTime
respItem.Ctime = v.Ctime
respItem.Utime = v.Utime
result[UID] = respItem
} else {
if result[UID].PrivilegeType > PrivilegeType {
respItem := &v1pb.DaHangHaiInfo{}
respItem.Id = RParseInt(v.Id, 0)
respItem.Uid = RParseInt(v.Uid, 0)
respItem.TargetId = RParseInt(v.TargetId, 0)
respItem.PrivilegeType = RParseInt(v.PrivilegeType, 0)
respItem.StartTime = v.StartTime
respItem.ExpiredTime = v.ExpiredTime
respItem.Ctime = v.Ctime
respItem.Utime = v.Utime
result[UID] = respItem
}
}
}
return
}
func (s *GuardService) getAnchorTopGuardCountFromDB(uid int64, dhhList []*dhhm.DHHDBTime) (result map[int64]*v1pb.DaHangHaiInfo) {
result = make(map[int64]*v1pb.DaHangHaiInfo)
for _, v := range dhhList {
PrivilegeType := v.PrivilegeType
UID := v.UID
if _, exist := result[UID]; !exist {
respItem := &v1pb.DaHangHaiInfo{}
respItem.Id = v.ID
respItem.Uid = v.UID
respItem.TargetId = v.TargetId
respItem.PrivilegeType = v.PrivilegeType
respItem.StartTime = v.StartTime
respItem.ExpiredTime = v.ExpiredTime
respItem.Ctime = v.Ctime
respItem.Utime = v.Utime
result[UID] = respItem
} else {
if result[UID].PrivilegeType > PrivilegeType {
respItem := &v1pb.DaHangHaiInfo{}
respItem.Id = v.ID
respItem.Uid = v.UID
respItem.TargetId = v.TargetId
respItem.PrivilegeType = v.PrivilegeType
respItem.StartTime = v.StartTime
respItem.ExpiredTime = v.ExpiredTime
respItem.Ctime = v.Ctime
respItem.Utime = v.Utime
result[UID] = respItem
}
}
}
return
}
func (s *GuardService) filterResultFromRedis(uid int64, targetMap map[int64]int64, dhhList []*dahanghaiModel.DaHangHaiRedis2) (result map[int64]*v1pb.DaHangHaiInfo) {
resultP1 := make([]*v1pb.DaHangHaiInfo, 0)
resultP2 := make([]*v1pb.DaHangHaiInfo, 0)
resultP3 := make([]*v1pb.DaHangHaiInfo, 0)
for _, v := range dhhList {
PrivilegeType := RParseInt(v.PrivilegeType, 0)
respItem := &v1pb.DaHangHaiInfo{}
respItem.Id = RParseInt(v.Id, 0)
respItem.Uid = RParseInt(v.Uid, 0)
respItem.TargetId = RParseInt(v.TargetId, 0)
respItem.PrivilegeType = RParseInt(v.PrivilegeType, 0)
respItem.StartTime = v.StartTime
respItem.ExpiredTime = v.ExpiredTime
respItem.Ctime = v.Ctime
respItem.Utime = v.Utime
switch PrivilegeType {
case _zongdu:
{
resultP1 = append(resultP1, respItem)
}
case _tidu:
{
resultP2 = append(resultP2, respItem)
}
case _jianzhang:
{
resultP3 = append(resultP3, respItem)
}
}
}
result = s.mergeResultWithTargetMap(resultP1, resultP2, resultP3, targetMap)
return
}
func (s *GuardService) filterResultFromDB(uid int64, targetMap map[int64]int64, dhhList []*dahanghaiModel.DHHDBTime) (result map[int64]*v1pb.DaHangHaiInfo) {
resultP1 := make([]*v1pb.DaHangHaiInfo, 0)
resultP2 := make([]*v1pb.DaHangHaiInfo, 0)
resultP3 := make([]*v1pb.DaHangHaiInfo, 0)
for _, v := range dhhList {
PrivilegeType := v.PrivilegeType
respItem := &v1pb.DaHangHaiInfo{}
respItem.Id = v.ID
respItem.Uid = v.UID
respItem.TargetId = v.TargetId
respItem.PrivilegeType = v.PrivilegeType
respItem.StartTime = v.StartTime
respItem.ExpiredTime = v.ExpiredTime
respItem.Ctime = v.Ctime
respItem.Utime = v.Utime
switch PrivilegeType {
case _zongdu:
{
resultP1 = append(resultP1, respItem)
}
case _tidu:
{
resultP2 = append(resultP2, respItem)
}
case _jianzhang:
{
resultP3 = append(resultP3, respItem)
}
}
}
result = s.mergeResultWithTargetMap(resultP1, resultP2, resultP3, targetMap)
return
}
func (s *GuardService) mergeResultForGetByUIDBatch(dbResult map[int64][]*dahanghaiModel.DaHangHaiRedis2, cacheResult []*dahanghaiModel.DaHangHaiRedis2) (result map[int64]*v1pb.DaHangHaiInfoList) {
result = make(map[int64]*v1pb.DaHangHaiInfoList)
for _, v := range cacheResult {
uid := RParseInt(v.Uid, 0)
if _, exist := result[uid]; !exist {
result[uid] = &v1pb.DaHangHaiInfoList{}
result[uid].List = make([]*v1pb.DaHangHaiInfo, 0)
}
item := &v1pb.DaHangHaiInfo{}
item.Id = RParseInt(v.Id, 0)
item.Uid = RParseInt(v.Uid, 0)
item.TargetId = RParseInt(v.TargetId, 0)
item.PrivilegeType = RParseInt(v.PrivilegeType, 0)
item.Ctime = v.Ctime
item.Utime = v.Utime
item.ExpiredTime = v.ExpiredTime
item.StartTime = v.StartTime
result[uid].List = append(result[uid].List, item)
}
for _, v := range dbResult {
for _, vv := range v {
uid := RParseInt(vv.Uid, 0)
if _, exist := result[uid]; !exist {
result[uid] = &v1pb.DaHangHaiInfoList{}
result[uid].List = make([]*v1pb.DaHangHaiInfo, 0)
}
item := &v1pb.DaHangHaiInfo{}
item.Id = RParseInt(vv.Id, 0)
item.Uid = RParseInt(vv.Uid, 0)
item.TargetId = RParseInt(vv.TargetId, 0)
item.PrivilegeType = RParseInt(vv.PrivilegeType, 0)
item.Ctime = vv.Ctime
item.Utime = vv.Utime
item.ExpiredTime = vv.ExpiredTime
item.StartTime = vv.StartTime
result[uid].List = append(result[uid].List, item)
}
}
return
}
func (s *GuardService) formatDaHangHaiCache(uid int64, dbData []*dahanghaiModel.DHHDBTime) (dhhList []*dahanghaiModel.DaHangHaiRedis2) {
dhhList = make([]*dahanghaiModel.DaHangHaiRedis2, 0)
if len(dbData) <= 0 {
return
}
for _, v := range dbData {
item := &dahanghaiModel.DaHangHaiRedis2{}
item.Id = strconv.Itoa(int(v.ID))
item.Uid = strconv.Itoa(int(v.UID))
item.TargetId = strconv.Itoa(int(v.TargetId))
item.PrivilegeType = strconv.Itoa(int(v.PrivilegeType))
item.StartTime = v.StartTime
item.ExpiredTime = v.ExpiredTime
item.Ctime = v.Ctime
item.Utime = v.Utime
dhhList = append(dhhList, item)
}
return
}
func (s *GuardService) formatDaHangHaiCacheBatch(dbData map[int64][]*dahanghaiModel.DHHDBTime) (dhhMapList map[int64][]*dahanghaiModel.DaHangHaiRedis2) {
dhhMapList = make(map[int64][]*dahanghaiModel.DaHangHaiRedis2)
if len(dbData) <= 0 {
return
}
for k, v := range dbData {
dhhList := make([]*dahanghaiModel.DaHangHaiRedis2, 0)
for _, vv := range v {
item := &dahanghaiModel.DaHangHaiRedis2{}
item.Id = strconv.Itoa(int(vv.ID))
item.Uid = strconv.Itoa(int(vv.UID))
item.TargetId = strconv.Itoa(int(vv.TargetId))
item.PrivilegeType = strconv.Itoa(int(vv.PrivilegeType))
item.StartTime = vv.StartTime
item.ExpiredTime = vv.ExpiredTime
item.Ctime = vv.Ctime
item.Utime = vv.Utime
dhhList = append(dhhList, item)
}
dhhMapList[k] = dhhList
}
return
}
func (s *GuardService) adaptResultFromDB(uid int64, targetID int64, sortType int64, dhhList []*dahanghaiModel.DHHDBTime) (result map[int64]*v1pb.DaHangHaiInfo) {
resultP1 := make([]*v1pb.DaHangHaiInfo, 0)
resultP2 := make([]*v1pb.DaHangHaiInfo, 0)
resultP3 := make([]*v1pb.DaHangHaiInfo, 0)
for _, v := range dhhList {
if v.TargetId == targetID {
respItem := &v1pb.DaHangHaiInfo{}
respItem.Id = v.ID
respItem.Uid = v.UID
respItem.TargetId = v.TargetId
respItem.PrivilegeType = v.PrivilegeType
respItem.StartTime = v.StartTime
respItem.ExpiredTime = v.ExpiredTime
respItem.Ctime = v.Ctime
respItem.Utime = v.Utime
switch v.PrivilegeType {
case _zongdu:
{
resultP1 = append(resultP1, respItem)
}
case _tidu:
{
resultP2 = append(resultP2, respItem)
}
case _jianzhang:
{
resultP3 = append(resultP3, respItem)
}
}
}
}
result = s.mergeResult(resultP1, resultP2, resultP3, sortType)
return
}
func (s *GuardService) filterTopFromDB(uid int64, dhhList []*dahanghaiModel.DHHDBTime) (result map[int64]*v1pb.DaHangHaiInfo) {
result = make(map[int64]*v1pb.DaHangHaiInfo)
for _, v := range dhhList {
PrivilegeType := v.PrivilegeType
if len(result) <= 0 {
respItem := &v1pb.DaHangHaiInfo{}
respItem.Id = v.ID
respItem.Uid = v.UID
respItem.TargetId = v.TargetId
respItem.PrivilegeType = v.PrivilegeType
respItem.StartTime = v.StartTime
respItem.ExpiredTime = v.ExpiredTime
respItem.Ctime = v.Ctime
respItem.Utime = v.Utime
result[uid] = respItem
} else {
if result[uid].PrivilegeType > PrivilegeType {
respItem := &v1pb.DaHangHaiInfo{}
respItem.Id = v.ID
respItem.Uid = v.UID
respItem.TargetId = v.TargetId
respItem.PrivilegeType = v.PrivilegeType
respItem.StartTime = v.StartTime
respItem.ExpiredTime = v.ExpiredTime
respItem.Ctime = v.Ctime
respItem.Utime = v.Utime
result[uid] = respItem
}
}
}
return
}
func (s *GuardService) mergeResult(resultZongdu []*v1pb.DaHangHaiInfo, resultTidu []*v1pb.DaHangHaiInfo, resultJianzhang []*v1pb.DaHangHaiInfo,
sortType int64) (result map[int64]*v1pb.DaHangHaiInfo) {
result = make(map[int64]*v1pb.DaHangHaiInfo)
switch sortType {
case _default:
{
if len(resultZongdu) > 0 {
for _, v := range resultZongdu {
result[v.TargetId] = v
return
}
}
if len(resultTidu) > 0 {
for _, v := range resultTidu {
result[v.TargetId] = v
return
}
}
if len(resultJianzhang) > 0 {
for _, v := range resultJianzhang {
result[v.TargetId] = v
return
}
}
}
case _zongdu:
{
for _, v := range resultZongdu {
result[v.TargetId] = v
return
}
}
case _tidu:
{
for _, v := range resultTidu {
result[v.TargetId] = v
return
}
}
case _jianzhang:
{
for _, v := range resultJianzhang {
result[v.TargetId] = v
return
}
}
}
return
}
func (s *GuardService) mergeResultWithTargetMap(resultZongdu []*v1pb.DaHangHaiInfo, resultTidu []*v1pb.DaHangHaiInfo, resultJianzhang []*v1pb.DaHangHaiInfo,
targetMap map[int64]int64) (result map[int64]*v1pb.DaHangHaiInfo) {
result = make(map[int64]*v1pb.DaHangHaiInfo)
resultArr := make([]*v1pb.DaHangHaiInfo, 0)
if len(resultZongdu) > 0 {
resultArr = append(resultArr, resultZongdu...)
}
if len(resultTidu) > 0 {
resultArr = append(resultArr, resultTidu...)
}
if len(resultJianzhang) > 0 {
resultArr = append(resultArr, resultJianzhang...)
}
for kmap, vmap := range targetMap {
switch vmap {
case _default:
{
for _, varr := range resultArr {
if varr.TargetId == kmap {
result[varr.TargetId] = varr
break
}
}
}
case _zongdu, _tidu, _jianzhang:
{
for _, varr := range resultArr {
if (varr.TargetId == kmap) && (varr.PrivilegeType == vmap) {
result[varr.TargetId] = varr
break
}
}
}
}
}
return
}
func (s *GuardService) findMissUIDs(hits map[int64]bool, req []int64) (resp []int64) {
resp = make([]int64, 0)
if len(req) <= 0 {
return
}
for _, v := range req {
if _, exist := hits[v]; !exist {
resp = append(resp, v)
}
}
return
}
// GetByUIDTargetID ...
// 单uid单targetid
func (s *GuardService) GetByUIDTargetID(ctx context.Context, req *v1pb.GetByUidTargetIdReq) (resp *v1pb.GetByUidTargetIdResp, err error) {
reqStartTime := confm.RecordTimeCost()
resp = &v1pb.GetByUidTargetIdResp{}
resp.Data = make(map[int64]*v1pb.DaHangHaiInfo)
cacheHealth := true
uids := make([]int64, 0)
uids = append(uids, req.Uid)
dhhResultFromRedis, err := s.dao.GetUIDAllGuardFromRedis(ctx, uids)
// 回源db原则,仅在get成功且miss时回源db!!!
if err != nil {
reqAfterQueryMCTime := confm.RecordTimeCost()
log.Error(_errorServiceLogPrefix+"|GetByUIDTargetID|查询缓存失败,暂不回源db,接口返回err|%dms", err, reqAfterQueryMCTime-reqStartTime)
cacheHealth = false
return
} else if dhhResultFromRedis != nil {
resp.Data = s.adaptResultFromRedis(req.Uid, req.TargetId, req.SortType, dhhResultFromRedis)
exp.PromCacheHit(_promCacheHitAll)
return
}
exp.PromCacheMiss(_promCacheMissed)
resultDB, err := s.dao.GetByUIDs(ctx, uids)
resultDBTime, _ := s.changeDBTime(ctx, resultDB)
if err != nil {
reqAfterQueryDBTime := confm.RecordTimeCost()
log.Error(_errorServiceLogPrefix+"|GetByUidTargetId|s.dao.Exp|从DB获取大航海信息error|(%v),missedUIDs(%v)|耗时:%dms", err, uids, reqAfterQueryDBTime-reqStartTime)
return
}
dhhResultFromDB := s.formatDaHangHaiCache(req.Uid, resultDBTime)
// 写入缓存
if cacheHealth {
s.asyncSetDHHCache(ctx, dhhResultFromDB, req.Uid)
}
resp.Data = s.adaptResultFromDB(req.Uid, req.TargetId, req.SortType, resultDBTime)
return
}
// ClearUIDCache 清除cache
func (s *GuardService) ClearUIDCache(ctx context.Context, req *v1pb.ClearUIDCacheReq) (resp *v1pb.ClearUIDCacheResp, err error) {
resp = &v1pb.ClearUIDCacheResp{}
if req.MagicKey != "jibamao" {
err = errors.WithMessage(ecode.ResourceParamErr, "禁止通行!")
}
s.asyncCLearExpCache(ctx, req.Uid)
return
}
// GetAnchorRecentTopGuard 获取最近开通总督的用户信息
func (s *GuardService) GetAnchorRecentTopGuard(ctx context.Context, req *v1pb.GetAnchorRecentTopGuardReq) (resp *v1pb.GetAnchorRecentTopGuardResp, err error) {
resp = &v1pb.GetAnchorRecentTopGuardResp{}
if req == nil || req.Uid <= 0 {
return
}
guardResultFromRedis, err := s.dao.GetAnchorRecentTopGuardCache(ctx, req.Uid)
if err != nil {
err = errors.WithMessage(ecode.XUserGuardFetchRecentTopListFail, "get GetAnchorRecentTopGuard cache failed")
return
}
uids := make(map[int64]int64)
nowTime := time.Now().Unix()
for k, v := range guardResultFromRedis {
if v > nowTime {
uids[k] = v
}
}
if err != nil {
return
}
retList := make([]*v1pb.GetAnchorRecentTopGuardList, 0)
for k, v := range uids {
item := &v1pb.GetAnchorRecentTopGuardList{}
item.Uid = k
item.IsOpen = 1
item.EndTime = v
retList = append(retList, item)
}
resp.List = retList
resp.Cnt = int64(len(retList))
return
}
// func (s *GuardService) getTopListGuardPrepareParams(req *v1pb.GetTopListGuardReq) (uid int64, page int64, pageSize int64) {
// uid = req.Uid
// if req.Page == 0 {
// page = 1
// } else {
// page = req.Page
// }
// if req.PageSize == 0 {
// pageSize = 10
// } else {
// pageSize = req.PageSize
// }
// return
// }
// GetTopListGuard ...
// 房间页守护排行榜
func (s *GuardService) GetTopListGuard(ctx context.Context, req *v1pb.GetTopListGuardReq) (resp *v1pb.GetTopListGuardResp, err error) {
resp = &v1pb.GetTopListGuardResp{}
// if req == nil {
// return
// }
// uid, page, pagesize := s.getTopListGuardPrepareParams(req)
return
}
// GetTopListGuardNum ...
// 房间页守护数量
func (s *GuardService) GetTopListGuardNum(ctx context.Context, req *v1pb.GetTopListGuardNumReq) (resp *v1pb.GetTopListGuardNumResp, err error) {
reqStartTime := confm.RecordTimeCost()
resp = &v1pb.GetTopListGuardNumResp{}
cacheHealth := true
uids := make([]int64, 0)
uids = append(uids, req.Uid)
dhhResultFromRedis, err := s.dao.GetAnchorAllGuardFromRedis(ctx, uids)
// 回源db原则,仅在get成功且miss时回源db!!!
if err != nil {
reqAfterQueryMCTime := confm.RecordTimeCost()
log.Error(_errorServiceLogPrefix+"|"+_XUserGetAnchorGuardNumCacheError+"|GetTopListGuardNum|查询缓存失败,暂不回源db,接口返回err|%dms", err, reqAfterQueryMCTime-reqStartTime)
cacheHealth = false
return
} else if dhhResultFromRedis != nil {
resp.TotalCount = int64(len(s.getAnchorTopGuardCount(req.Uid, dhhResultFromRedis)))
exp.PromCacheHit(_promAnchorCacheHitAll)
return
}
exp.PromCacheMiss(_promAnchorCacheMissed)
resultDB, err := s.dao.GetByAnchorUIDs(ctx, uids)
resultDBTime, _ := s.changeDBTime(ctx, resultDB)
if err != nil {
reqAfterQueryDBTime := confm.RecordTimeCost()
log.Error(_errorServiceLogPrefix+"|GetByUIDForGift|s.dao.dahanghai|从DB获取大航海信息error|(%v),missedUIDs(%v)|耗时:%dms", err, uids, reqAfterQueryDBTime-reqStartTime)
return
}
dhhResultFromDB := s.formatDaHangHaiCache(req.Uid, resultDBTime)
// 写入缓存
if cacheHealth {
s.asyncSetAnchorGuardCache(ctx, dhhResultFromDB, req.Uid)
}
resp.TotalCount = int64(len(s.getAnchorTopGuardCountFromDB(req.Uid, resultDBTime)))
return
}
// GetByTargetIdsBatch ...
// 单uid全量守护
func (s *GuardService) GetByTargetIdsBatch(ctx context.Context, req *v1pb.GetByTargetIdsReq) (resp *v1pb.GetByTargetIdsResp, err error) {
return
}
// GetByUIDForGift ...
// 单uid全量守护
func (s *GuardService) GetByUIDForGift(ctx context.Context, req *v1pb.GetByUidReq) (resp *v1pb.GetByUidResp, err error) {
reqStartTime := confm.RecordTimeCost()
resp = &v1pb.GetByUidResp{}
resp.Data = make(map[int64]*v1pb.DaHangHaiInfo)
cacheHealth := true
uids := make([]int64, 0)
uids = append(uids, req.Uid)
dhhResultFromRedis, err := s.dao.GetUIDAllGuardFromRedis(ctx, uids)
// 回源db原则,仅在get成功且miss时回源db!!!
if err != nil {
reqAfterQueryMCTime := confm.RecordTimeCost()
log.Error(_errorServiceLogPrefix+"|"+_XUserGetDaHangHaiForGiftCacheError+"|GetByUIDForGift|查询缓存失败,暂不回源db,接口返回err|%dms", err, reqAfterQueryMCTime-reqStartTime)
cacheHealth = false
return
} else if dhhResultFromRedis != nil {
resp.Data = s.filterUIDTopFromRedis(req.Uid, dhhResultFromRedis)
exp.PromCacheHit(_promCacheHitAll)
return
}
exp.PromCacheMiss(_promCacheMissed)
resultDB, err := s.dao.GetByUIDs(ctx, uids)
resultDBTime, _ := s.changeDBTime(ctx, resultDB)
if err != nil {
reqAfterQueryDBTime := confm.RecordTimeCost()
log.Error(_errorServiceLogPrefix+"|GetByUIDForGift|s.dao.dahanghai|从DB获取大航海信息error|(%v),missedUIDs(%v)|耗时:%dms", err, uids, reqAfterQueryDBTime-reqStartTime)
return
}
dhhResultFromDB := s.formatDaHangHaiCache(req.Uid, resultDBTime)
// 写入缓存
if cacheHealth {
s.asyncSetDHHCache(ctx, dhhResultFromDB, req.Uid)
}
resp.Data = s.filterTopFromDB(req.Uid, resultDBTime)
return
}
func (s *GuardService) transToMap(input []*dahanghaiModel.DaHangHaiRedis2) (resp map[int64]bool) {
resp = make(map[int64]bool)
if len(input) <= 0 {
return
}
for _, v := range input {
uid := RParseInt(v.Uid, 0)
resp[uid] = true
}
return
}
func (s *GuardService) adaptRetForGetByUIDBatch(dhhList []*dahanghaiModel.DaHangHaiRedis2) (result map[int64]*v1pb.DaHangHaiInfoList) {
result = make(map[int64]*v1pb.DaHangHaiInfoList)
if len(dhhList) <= 0 {
return
}
for _, v := range dhhList {
uid := RParseInt(v.Uid, 0)
if _, exist := result[uid]; !exist {
result[uid] = &v1pb.DaHangHaiInfoList{}
result[uid].List = make([]*v1pb.DaHangHaiInfo, 0)
}
item := &v1pb.DaHangHaiInfo{}
item.Id = RParseInt(v.Id, 0)
item.Uid = RParseInt(v.Uid, 0)
item.TargetId = RParseInt(v.TargetId, 0)
item.PrivilegeType = RParseInt(v.PrivilegeType, 0)
item.Ctime = v.Ctime
item.Utime = v.Utime
item.ExpiredTime = v.ExpiredTime
item.StartTime = v.StartTime
result[uid].List = append(result[uid].List, item)
}
return
}
// GetByUIDBatch ...
// 多uid全量守护
func (s *GuardService) GetByUIDBatch(ctx context.Context, req *v1pb.GetByUidBatchReq) (resp *v1pb.GetByUidBatchResp, err error) {
resp = &v1pb.GetByUidBatchResp{}
if req == nil {
return
}
reqStartTime := confm.RecordTimeCost()
resp.Data = make(map[int64]*v1pb.DaHangHaiInfoList)
cacheHealth := true
dhhResultFromRedis, err := s.dao.GetUIDAllGuardFromRedisBatch(ctx, req.Uids)
hitUIDs := s.transToMap(dhhResultFromRedis)
missUIDS := s.findMissUIDs(hitUIDs, req.Uids)
// 回源db原则,仅在get成功且miss时回源db!!!
if err != nil {
reqAfterQueryMCTime := confm.RecordTimeCost()
log.Error(_errorServiceLogPrefix+"|GetByUIDBatch|查询缓存失败,暂不回源db,接口返回err|%dms", err, reqAfterQueryMCTime-reqStartTime)
cacheHealth = false
return
} else if (dhhResultFromRedis != nil) && (len(missUIDS) <= 0) {
resp.Data = s.adaptRetForGetByUIDBatch(dhhResultFromRedis)
exp.PromCacheHit(_promCacheHitAll)
return
}
exp.PromCacheMiss(_promCacheMissed)
resultDB, err := s.dao.GetByUIDsWithMap(ctx, missUIDS)
resultDBTime, _ := s.changeDBTimeBatch(ctx, resultDB)
if err != nil {
reqAfterQueryDBTime := confm.RecordTimeCost()
log.Error(_errorServiceLogPrefix+"|GetByUIDTargetIds|从DB获取大航海信息error|(%v),missedUIDs(%v)|耗时:%dms", err, req.Uids, reqAfterQueryDBTime-reqStartTime)
return
}
dhhResultFromDB := s.formatDaHangHaiCacheBatch(resultDBTime)
// 写入缓存
if cacheHealth {
s.asyncSetDHHCacheBatch(ctx, dhhResultFromDB)
}
resp.Data = s.mergeResultForGetByUIDBatch(dhhResultFromDB, dhhResultFromRedis)
return
}
// GetByUIDTargetIds ...
// 单uid多targetids
func (s *GuardService) GetByUIDTargetIds(ctx context.Context, req *v1pb.GetByUidTargetIdsReq) (resp *v1pb.GetByUidTargetIdsResp, err error) {
reqStartTime := confm.RecordTimeCost()
resp = &v1pb.GetByUidTargetIdsResp{}
resp.Data = make(map[int64]*v1pb.DaHangHaiInfo)
cacheHealth := true
uids := make([]int64, 0)
uids = append(uids, req.Uid)
dhhResultFromRedis, err := s.dao.GetUIDAllGuardFromRedis(ctx, uids)
filterMap, _ := s.makeTargetFilterMap(ctx, req)
// 回源db原则,仅在get成功且miss时回源db!!!
if err != nil {
reqAfterQueryMCTime := confm.RecordTimeCost()
log.Error(_errorServiceLogPrefix+"|GetByUIDTargetIds|查询缓存失败,暂不回源db,接口返回err|%dms", err, reqAfterQueryMCTime-reqStartTime)
cacheHealth = false
return
} else if dhhResultFromRedis != nil {
resp.Data = s.filterResultFromRedis(req.Uid, filterMap, dhhResultFromRedis)
exp.PromCacheHit(_promCacheHitAll)
return
}
exp.PromCacheMiss(_promCacheMissed)
resultDB, err := s.dao.GetByUIDs(ctx, uids)
resultDBTime, _ := s.changeDBTime(ctx, resultDB)
if err != nil {
reqAfterQueryDBTime := confm.RecordTimeCost()
log.Error(_errorServiceLogPrefix+"|GetByUIDTargetIds|从DB获取大航海信息error|(%v),missedUIDs(%v)|耗时:%dms", err, uids, reqAfterQueryDBTime-reqStartTime)
return
}
dhhResultFromDB := s.formatDaHangHaiCache(req.Uid, resultDBTime)
// 写入缓存
if cacheHealth {
s.asyncSetDHHCache(ctx, dhhResultFromDB, req.Uid)
}
resp.Data = s.filterResultFromDB(req.Uid, filterMap, resultDBTime)
return
}
func (s *GuardService) changeDBTime(ctx context.Context, dhhs []*dhhm.DHHDB) (result []*dhhm.DHHDBTime, err error) {
result = make([]*dhhm.DHHDBTime, 0)
if len(dhhs) <= 0 {
return
}
for _, v := range dhhs {
item := &dhhm.DHHDBTime{}
item.ID = v.ID
item.UID = v.UID
item.TargetId = v.TargetId
item.PrivilegeType = v.PrivilegeType
item.StartTime = v.StartTime.Format(_formatTime)
item.ExpiredTime = v.ExpiredTime.Format(_formatTime)
item.Ctime = v.Ctime.Format(_formatTime)
item.Utime = v.Utime.Format(_formatTime)
result = append(result, item)
}
return
}
func (s *GuardService) changeDBTimeBatch(ctx context.Context, dhhs map[int64][]*dhhm.DHHDB) (result map[int64][]*dhhm.DHHDBTime, err error) {
result = make(map[int64][]*dhhm.DHHDBTime)
if len(dhhs) <= 0 {
return
}
for k, v := range dhhs {
resultList := make([]*dhhm.DHHDBTime, 0)
for _, vv := range v {
item := &dhhm.DHHDBTime{}
item.ID = vv.ID
item.UID = vv.UID
item.TargetId = vv.TargetId
item.PrivilegeType = vv.PrivilegeType
item.StartTime = vv.StartTime.Format(_formatTime)
item.ExpiredTime = vv.ExpiredTime.Format(_formatTime)
item.Ctime = vv.Ctime.Format(_formatTime)
item.Utime = vv.Utime.Format(_formatTime)
resultList = append(resultList, item)
}
result[k] = make([]*dhhm.DHHDBTime, 0)
result[k] = resultList
}
return
}
func (s *GuardService) makeTargetFilterMap(ctx context.Context, req *v1pb.GetByUidTargetIdsReq) (resp map[int64]int64, err error) {
resp = make(map[int64]int64)
if req == nil {
return
}
for _, v := range req.TargetIDs {
resp[v.TargetId] = v.SortType
}
return
}

View File

@@ -0,0 +1,280 @@
package v1
import (
"context"
"encoding/json"
"fmt"
"go-common/app/service/live/xuser/dao/account"
xhttp "net/http"
"strconv"
"strings"
roomApi "go-common/app/service/live/room/api/liverpc"
roomV2 "go-common/app/service/live/room/api/liverpc/v2"
v1pb "go-common/app/service/live/xuser/api/grpc/v1"
"go-common/app/service/live/xuser/conf"
"go-common/app/service/live/xuser/dao/guard"
"go-common/app/service/live/xuser/model"
// account "go-common/app/service/main/account/rpc/client"
"go-common/library/cache"
"go-common/library/log"
bm "go-common/library/net/http/blademaster"
"go-common/library/queue/databus"
)
const (
_entryEffectBusinessId = 1
)
// GuardService guard service
type GuardService struct {
c *conf.Config
dao *guard.GuardDao
cli *bm.Client
liveEntryEffectPub *databus.Databus
roomAPI *roomApi.Client
accRPC *account.Dao
async *cache.Cache
asyncMulti *cache.Cache
}
// New new guard service
func New(c *conf.Config) *GuardService {
return &GuardService{
c: c,
dao: guard.NewGuardDao(c),
cli: bm.NewClient(c.BMClient),
roomAPI: roomApi.New(c.LiveRPC["room"]),
accRPC: account.New(c),
liveEntryEffectPub: databus.New(c.LiveEntryEffectPub),
async: cache.New(1, 1024),
asyncMulti: cache.New(2, 10240),
}
}
// Buy buy guard
// grpc wrapper
func (s *GuardService) Buy(ctx context.Context, req *v1pb.GuardBuyReq) (reply *v1pb.GuardBuyReply, err error) {
var status int
reqDao := &model.GuardBuy{
OrderId: req.OrderId,
Uid: req.Uid,
Ruid: req.Ruid,
GuardLevel: req.GuardLevel,
Num: req.Num,
Platform: req.Platform,
Source: req.Source,
}
status, err = s.saveGuard(ctx, reqDao)
if err != nil {
log.Error("[service.v1.guard|Buy] buy guard error(%+v), params(%+v)", err, reqDao)
}
reply = &v1pb.GuardBuyReply{
Status: status,
}
return
}
func (s *GuardService) saveGuard(ctx context.Context, req *model.GuardBuy) (status int, err error) {
ok, err := s.dao.LockOrder(ctx, req.OrderId)
if !ok {
log.Info("[service.v1.guard|saveGuard] LockOrder success, req(%+v)", req)
return 1, nil
}
if err != nil {
log.Warn("[service.v1.guard|saveGuard] LockOrder failed(%+v), req(%+v)", err, req)
return 2, err
}
status = 1
info, err := s.dao.GetGuardByUIDRuid(ctx, req.Uid, req.Ruid)
if err != nil {
status = 2
return
}
s.dao.ClearCache(ctx, req.Uid, req.Ruid)
buyType := 1 // 1: 开通大航海广播, 2: 续费大航海广播
if len(info) == 0 {
// 无守护
err = s.dao.AddGuard(ctx, req)
if err != nil {
log.Warn("[service.v1.guard|saveGuard] AddGuard failed(%+v), req(%+v)", err, req)
status = 2
goto END
}
} else {
if info[0].PrivilegeType == req.GuardLevel {
buyType = 2
// 购买的等级没有当前佩戴的守护等级高
s.dao.UpdateGuard(ctx, req, ">=") // @CHECKED
} else if info[0].PrivilegeType < req.GuardLevel {
hasEqual := 0
// 越前面的等级越高
expiredTime := info[0].ExpiredTime
// 购买的新守护,只影响等级比(购买的守护等级)更低的守护的过期时间
for _, v := range info {
if v.PrivilegeType == req.GuardLevel {
buyType = 2
hasEqual = 1
}
// 在高级别的最晚有效期上处理
if v.PrivilegeType < req.GuardLevel && v.ExpiredTime.Sub(expiredTime) > 0 {
expiredTime = v.ExpiredTime
}
}
if hasEqual == 0 {
// 新购买, 过期时间在当前佩戴的大航海的过期时间基础上累加
s.dao.UpsertGuard(ctx, req, expiredTime.AddDate(0, 0, req.Num*30).Format("2006-01-02 15:04:05")) // @CHECKED insert or update
s.dao.UpdateGuard(ctx, req, ">") // @CHECKED
} else {
s.dao.UpdateGuard(ctx, req, ">=") // @CHECKED
}
} else if info[0].PrivilegeType > req.GuardLevel {
// 购买的等级比当前拥有的守护等级高
err = s.dao.AddGuard(ctx, req)
if err != nil {
log.Warn("[service.v1.guard|saveGuard] AddGuard failed(%+v), req(%+v)", err, req)
status = 2
goto END
}
// 更新低级别的过期时间
s.dao.UpdateGuard(ctx, req, ">") // @CHECKED
}
}
END:
if status == 1 {
// 发送房间广播
s.async.Save(func() {
s.sendRoomMsg(context.Background(), req.Uid, req.Ruid, req.GuardLevel, buyType)
})
} else {
s.dao.UnlockOrder(ctx, req.OrderId)
return
}
s.dao.ClearCache(ctx, req.Uid, req.Ruid)
// 投递进场特效给rewardcenter落库
s.async.Save(func() {
s.sendEntryEffect(context.Background(), req.Uid, req.Ruid)
})
return
}
func getEffectIdByLevel(level int) int {
switch level {
case 1:
return 1
case 2:
return 2
case 3:
return 4
}
return 0
}
func (s *GuardService) sendEntryEffect(ctx context.Context, uid int64, ruid int64) (err error) {
if !s.c.GuardCfg.OpenEntryEffectDatabus {
return
}
info, err := s.dao.GetGuardByUIDRuid(ctx, uid, ruid)
if err != nil {
log.Error("[service.guard.v1.guard|sendEntryEffect] GetGuardByUIDRuid error(%v), uid(%d), ruid(%d)", err, uid, ruid)
return
}
if len(info) == 0 {
log.Error("[service.guard.v1.guard|sendEntryEffect] GetGuardByUIDRuid error(%v), uid(%d), ruid(%d) length==0", err, uid, ruid)
return
}
var data model.GuardEntryEffects
data.Business = _entryEffectBusinessId
data.Data = make([]model.GuardEntryEffect, len(info))
for index := 0; index < len(info); index++ {
data.Data[index].EffectId = getEffectIdByLevel(info[index].PrivilegeType)
data.Data[index].Uid = info[index].Uid
data.Data[index].TargetId = info[index].TargetId
data.Data[index].EndTime = info[index].ExpiredTime.Format("2006-01-02 15:04:05")
}
if err = s.liveEntryEffectPub.Send(ctx, strconv.FormatInt(uid, 10), data); err != nil {
log.Error("[service.guard.v1.guard|sendEntryEffect] send error(%v), data(%v)", err, data)
}
return
}
func (s *GuardService) sendRoomMsg(ctx context.Context, uid int64, ruid int64, level int, buyType int) (err error) {
if !s.c.GuardCfg.EnableGuardBroadcast {
return
}
usrInfo, err := s.accRPC.GetUserInfo(ctx, uid)
if err != nil || usrInfo == nil {
log.Error("[service.v1.guard|sendRoomMsg] s.accRPC.Info3 error(%+v), params(%+v)", err, usrInfo)
return
}
var roomReq roomV2.RoomRoomIdByUidMultiReq
roomReq.Uids = make([]int64, 1)
roomReq.Uids[0] = ruid
roomRsp, err := s.roomAPI.V2Room.RoomIdByUidMulti(ctx, &roomReq)
if err != nil || roomRsp.Code != 0 {
log.Error("[service.v1.guard|sendRoomMsg] RoomIdByUidMulti error(%+v), params(%+v)", err, roomRsp)
return
}
roomIdStr, ok := roomRsp.Data[strconv.FormatInt(ruid, 10)]
if !ok {
log.Error("[service.v1.guard|sendRoomMsg] RoomIdByUidMulti error(%+v), params(%+v)", err, roomRsp)
return
}
var msg struct {
Cmd string `json:"cmd"`
Data struct {
OpType int `json:"op_type"`
Uid int64 `json:"uid"`
UserName string `json:"username"`
GuardLevel int `json:"guard_level"`
IsShow int `json:"is_show"`
} `json:"data"`
}
msg.Cmd = "USER_TOAST_MSG"
msg.Data.OpType = buyType
msg.Data.Uid = uid
msg.Data.UserName = usrInfo.Name
msg.Data.GuardLevel = level
msg.Data.IsShow = 0
bs, err := json.Marshal(msg)
if err != nil {
return
}
var resp struct {
Ret int `json:"ret"`
}
req, err := xhttp.NewRequest(xhttp.MethodPost, fmt.Sprintf("%s/dm/1/push?ensure=1&cid=%s", s.c.GuardCfg.DanmuHost, roomIdStr), strings.NewReader(string(bs)))
if err != nil {
log.Error("[service.v1.guard|sendRoomMsg] sendRoomMsg error(%+v), params(%+v)", err, msg)
return
}
err = s.cli.Do(ctx, req, &resp)
if resp.Ret != 1 {
log.Error("[service.v1.guard|sendRoomMsg] sendRoomMsg error(%+v), params(%+v), resp(%+v)", err, msg, resp)
}
return
}
// Close close guard service
func (s *GuardService) Close() {
s.dao.Close()
}

View File

@@ -0,0 +1,6 @@
package v1
const (
_XUserGetDaHangHaiForGiftCacheError = "1008030"
_XUserGetAnchorGuardNumCacheError = "1008031"
)

Some files were not shown because too many files have changed in this diff Show More