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/xlottery/api/grpc/v1:all-srcs",
"//app/service/live/xlottery/cmd:all-srcs",
"//app/service/live/xlottery/conf:all-srcs",
"//app/service/live/xlottery/dao:all-srcs",
"//app/service/live/xlottery/model:all-srcs",
"//app/service/live/xlottery/server/grpc:all-srcs",
"//app/service/live/xlottery/server/http:all-srcs",
"//app/service/live/xlottery/service:all-srcs",
],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,18 @@
# v1.0.0
1. 上线功能xxx
# v1.0.1
1. 修复bug
2. 修改error log err
3. 时间改成时间间隔
#v1.0.5
1. 修复 public beatid = 41002 的问题
2. 修改panic
3. 添加reviewer
4. 弹幕添加ip和platform
5. 改进log信息,修复roomid
6. 周星活动
7. lpl扭蛋币、首次赠送赠送扭蛋币
8. 新年祈福抽奖券,重试脚本

View File

@@ -0,0 +1,15 @@
# Owner
zhaohailin
longsifan
libaolin
hongshengjie
# Author
baojuntao
longsifan
# Reviewer
longsifan
libaolin
zhaohailin
hongshengjie

View File

@@ -0,0 +1,20 @@
# See the OWNERS docs at https://go.k8s.io/owners
approvers:
- baojuntao
- hongshengjie
- libaolin
- longsifan
- zhaohailin
labels:
- live
- service
- service/live/xlottery
options:
no_parent_owners: true
reviewers:
- baojuntao
- hongshengjie
- libaolin
- longsifan
- zhaohailin

View File

@@ -0,0 +1,12 @@
# lottery-service
# 项目简介
1. strom 服务 主要服务 直播 节奏风暴抽奖
# 编译环境
# 依赖包
# 编译执行

View File

@@ -0,0 +1,62 @@
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/xlottery/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/xlottery/api/grpc/v1",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//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,342 @@
// Code generated by protoc-gen-bm v0.1, DO NOT EDIT.
// source: 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.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 PathCapsuleGetDetail = "/live.xlottery.v1.Capsule/get_detail"
var PathCapsuleOpenCapsule = "/live.xlottery.v1.Capsule/open_capsule"
var PathCapsuleGetCoinList = "/live.xlottery.v1.Capsule/get_coin_list"
var PathCapsuleUpdateCoinConfig = "/live.xlottery.v1.Capsule/update_coin_config"
var PathCapsuleUpdateCoinStatus = "/live.xlottery.v1.Capsule/update_coin_status"
var PathCapsuleDeleteCoin = "/live.xlottery.v1.Capsule/delete_coin"
var PathCapsuleGetPoolList = "/live.xlottery.v1.Capsule/get_pool_list"
var PathCapsuleUpdatePool = "/live.xlottery.v1.Capsule/update_pool"
var PathCapsuleDeletePool = "/live.xlottery.v1.Capsule/delete_pool"
var PathCapsuleUpdatePoolStatus = "/live.xlottery.v1.Capsule/update_pool_status"
var PathCapsuleGetPoolPrize = "/live.xlottery.v1.Capsule/get_pool_prize"
var PathCapsuleGetPrizeType = "/live.xlottery.v1.Capsule/get_prize_type"
var PathCapsuleGetPrizeExpire = "/live.xlottery.v1.Capsule/get_prize_expire"
var PathCapsuleUpdatePoolPrize = "/live.xlottery.v1.Capsule/update_pool_prize"
var PathCapsuleDeletePoolPrize = "/live.xlottery.v1.Capsule/delete_pool_prize"
var PathCapsuleGetCapsuleInfo = "/live.xlottery.v1.Capsule/get_capsule_info"
var PathCapsuleOpenCapsuleByType = "/live.xlottery.v1.Capsule/open_capsule_by_type"
var PathCapsuleGetCouponList = "/live.xlottery.v1.Capsule/get_coupon_list"
var PathStormStart = "/live.xlottery.v1.Storm/Start"
var PathStormCanStart = "/live.xlottery.v1.Storm/CanStart"
var PathStormJoin = "/live.xlottery.v1.Storm/Join"
var PathStormCheck = "/live.xlottery.v1.Storm/Check"
// =================
// Capsule Interface
// =================
type CapsuleBMServer interface {
GetDetail(ctx context.Context, req *CapsuleGetDetailReq) (resp *CapsuleGetDetailResp, err error)
OpenCapsule(ctx context.Context, req *CapsuleOpenCapsuleReq) (resp *CapsuleOpenCapsuleResp, err error)
GetCoinList(ctx context.Context, req *GetCoinListReq) (resp *GetCoinListResp, err error)
UpdateCoinConfig(ctx context.Context, req *UpdateCoinConfigReq) (resp *UpdateCoinConfigResp, err error)
UpdateCoinStatus(ctx context.Context, req *UpdateCoinStatusReq) (resp *UpdateCoinStatusResp, err error)
DeleteCoin(ctx context.Context, req *DeleteCoinReq) (resp *DeleteCoinResp, err error)
GetPoolList(ctx context.Context, req *GetPoolListReq) (resp *GetPoolListResp, err error)
UpdatePool(ctx context.Context, req *UpdatePoolReq) (resp *UpdatePoolResp, err error)
DeletePool(ctx context.Context, req *DeletePoolReq) (resp *DeletePoolResp, err error)
UpdatePoolStatus(ctx context.Context, req *UpdatePoolStatusReq) (resp *UpdatePoolStatusResp, err error)
GetPoolPrize(ctx context.Context, req *GetPoolPrizeReq) (resp *GetPoolPrizeResp, err error)
GetPrizeType(ctx context.Context, req *GetPrizeTypeReq) (resp *GetPrizeTypeResp, err error)
GetPrizeExpire(ctx context.Context, req *GetPrizeExpireReq) (resp *GetPrizeExpireResp, err error)
UpdatePoolPrize(ctx context.Context, req *UpdatePoolPrizeReq) (resp *UpdatePoolPrizeResp, err error)
DeletePoolPrize(ctx context.Context, req *DeletePoolPrizeReq) (resp *DeletePoolPrizeResp, err error)
GetCapsuleInfo(ctx context.Context, req *CapsuleGetCapsuleInfoReq) (resp *CapsuleGetCapsuleInfoResp, err error)
OpenCapsuleByType(ctx context.Context, req *CapsuleOpenCapsuleByTypeReq) (resp *CapsuleOpenCapsuleByTypeResp, err error)
GetCouponList(ctx context.Context, req *CapsuleGetCouponListReq) (resp *CapsuleGetCouponListResp, err error)
}
var v1CapsuleSvc CapsuleBMServer
func capsuleGetDetail(c *bm.Context) {
p := new(CapsuleGetDetailReq)
if err := c.BindWith(p, binding.Default(c.Request.Method, c.Request.Header.Get("Content-Type"))); err != nil {
return
}
resp, err := v1CapsuleSvc.GetDetail(c, p)
c.JSON(resp, err)
}
func capsuleOpenCapsule(c *bm.Context) {
p := new(CapsuleOpenCapsuleReq)
if err := c.BindWith(p, binding.Default(c.Request.Method, c.Request.Header.Get("Content-Type"))); err != nil {
return
}
resp, err := v1CapsuleSvc.OpenCapsule(c, p)
c.JSON(resp, err)
}
func capsuleGetCoinList(c *bm.Context) {
p := new(GetCoinListReq)
if err := c.BindWith(p, binding.Default(c.Request.Method, c.Request.Header.Get("Content-Type"))); err != nil {
return
}
resp, err := v1CapsuleSvc.GetCoinList(c, p)
c.JSON(resp, err)
}
func capsuleUpdateCoinConfig(c *bm.Context) {
p := new(UpdateCoinConfigReq)
if err := c.BindWith(p, binding.Default(c.Request.Method, c.Request.Header.Get("Content-Type"))); err != nil {
return
}
resp, err := v1CapsuleSvc.UpdateCoinConfig(c, p)
c.JSON(resp, err)
}
func capsuleUpdateCoinStatus(c *bm.Context) {
p := new(UpdateCoinStatusReq)
if err := c.BindWith(p, binding.Default(c.Request.Method, c.Request.Header.Get("Content-Type"))); err != nil {
return
}
resp, err := v1CapsuleSvc.UpdateCoinStatus(c, p)
c.JSON(resp, err)
}
func capsuleDeleteCoin(c *bm.Context) {
p := new(DeleteCoinReq)
if err := c.BindWith(p, binding.Default(c.Request.Method, c.Request.Header.Get("Content-Type"))); err != nil {
return
}
resp, err := v1CapsuleSvc.DeleteCoin(c, p)
c.JSON(resp, err)
}
func capsuleGetPoolList(c *bm.Context) {
p := new(GetPoolListReq)
if err := c.BindWith(p, binding.Default(c.Request.Method, c.Request.Header.Get("Content-Type"))); err != nil {
return
}
resp, err := v1CapsuleSvc.GetPoolList(c, p)
c.JSON(resp, err)
}
func capsuleUpdatePool(c *bm.Context) {
p := new(UpdatePoolReq)
if err := c.BindWith(p, binding.Default(c.Request.Method, c.Request.Header.Get("Content-Type"))); err != nil {
return
}
resp, err := v1CapsuleSvc.UpdatePool(c, p)
c.JSON(resp, err)
}
func capsuleDeletePool(c *bm.Context) {
p := new(DeletePoolReq)
if err := c.BindWith(p, binding.Default(c.Request.Method, c.Request.Header.Get("Content-Type"))); err != nil {
return
}
resp, err := v1CapsuleSvc.DeletePool(c, p)
c.JSON(resp, err)
}
func capsuleUpdatePoolStatus(c *bm.Context) {
p := new(UpdatePoolStatusReq)
if err := c.BindWith(p, binding.Default(c.Request.Method, c.Request.Header.Get("Content-Type"))); err != nil {
return
}
resp, err := v1CapsuleSvc.UpdatePoolStatus(c, p)
c.JSON(resp, err)
}
func capsuleGetPoolPrize(c *bm.Context) {
p := new(GetPoolPrizeReq)
if err := c.BindWith(p, binding.Default(c.Request.Method, c.Request.Header.Get("Content-Type"))); err != nil {
return
}
resp, err := v1CapsuleSvc.GetPoolPrize(c, p)
c.JSON(resp, err)
}
func capsuleGetPrizeType(c *bm.Context) {
p := new(GetPrizeTypeReq)
if err := c.BindWith(p, binding.Default(c.Request.Method, c.Request.Header.Get("Content-Type"))); err != nil {
return
}
resp, err := v1CapsuleSvc.GetPrizeType(c, p)
c.JSON(resp, err)
}
func capsuleGetPrizeExpire(c *bm.Context) {
p := new(GetPrizeExpireReq)
if err := c.BindWith(p, binding.Default(c.Request.Method, c.Request.Header.Get("Content-Type"))); err != nil {
return
}
resp, err := v1CapsuleSvc.GetPrizeExpire(c, p)
c.JSON(resp, err)
}
func capsuleUpdatePoolPrize(c *bm.Context) {
p := new(UpdatePoolPrizeReq)
if err := c.BindWith(p, binding.Default(c.Request.Method, c.Request.Header.Get("Content-Type"))); err != nil {
return
}
resp, err := v1CapsuleSvc.UpdatePoolPrize(c, p)
c.JSON(resp, err)
}
func capsuleDeletePoolPrize(c *bm.Context) {
p := new(DeletePoolPrizeReq)
if err := c.BindWith(p, binding.Default(c.Request.Method, c.Request.Header.Get("Content-Type"))); err != nil {
return
}
resp, err := v1CapsuleSvc.DeletePoolPrize(c, p)
c.JSON(resp, err)
}
func capsuleGetCapsuleInfo(c *bm.Context) {
p := new(CapsuleGetCapsuleInfoReq)
if err := c.BindWith(p, binding.Default(c.Request.Method, c.Request.Header.Get("Content-Type"))); err != nil {
return
}
resp, err := v1CapsuleSvc.GetCapsuleInfo(c, p)
c.JSON(resp, err)
}
func capsuleOpenCapsuleByType(c *bm.Context) {
p := new(CapsuleOpenCapsuleByTypeReq)
if err := c.BindWith(p, binding.Default(c.Request.Method, c.Request.Header.Get("Content-Type"))); err != nil {
return
}
resp, err := v1CapsuleSvc.OpenCapsuleByType(c, p)
c.JSON(resp, err)
}
func capsuleGetCouponList(c *bm.Context) {
p := new(CapsuleGetCouponListReq)
if err := c.BindWith(p, binding.Default(c.Request.Method, c.Request.Header.Get("Content-Type"))); err != nil {
return
}
resp, err := v1CapsuleSvc.GetCouponList(c, p)
c.JSON(resp, err)
}
// RegisterCapsuleBMServer Register the blademaster route
func RegisterCapsuleBMServer(e *bm.Engine, server CapsuleBMServer) {
v1CapsuleSvc = server
e.GET("/live.xlottery.v1.Capsule/get_detail", capsuleGetDetail)
e.GET("/live.xlottery.v1.Capsule/open_capsule", capsuleOpenCapsule)
e.GET("/live.xlottery.v1.Capsule/get_coin_list", capsuleGetCoinList)
e.GET("/live.xlottery.v1.Capsule/update_coin_config", capsuleUpdateCoinConfig)
e.GET("/live.xlottery.v1.Capsule/update_coin_status", capsuleUpdateCoinStatus)
e.GET("/live.xlottery.v1.Capsule/delete_coin", capsuleDeleteCoin)
e.GET("/live.xlottery.v1.Capsule/get_pool_list", capsuleGetPoolList)
e.GET("/live.xlottery.v1.Capsule/update_pool", capsuleUpdatePool)
e.GET("/live.xlottery.v1.Capsule/delete_pool", capsuleDeletePool)
e.GET("/live.xlottery.v1.Capsule/update_pool_status", capsuleUpdatePoolStatus)
e.GET("/live.xlottery.v1.Capsule/get_pool_prize", capsuleGetPoolPrize)
e.GET("/live.xlottery.v1.Capsule/get_prize_type", capsuleGetPrizeType)
e.GET("/live.xlottery.v1.Capsule/get_prize_expire", capsuleGetPrizeExpire)
e.GET("/live.xlottery.v1.Capsule/update_pool_prize", capsuleUpdatePoolPrize)
e.GET("/live.xlottery.v1.Capsule/delete_pool_prize", capsuleDeletePoolPrize)
e.GET("/live.xlottery.v1.Capsule/get_capsule_info", capsuleGetCapsuleInfo)
e.GET("/live.xlottery.v1.Capsule/open_capsule_by_type", capsuleOpenCapsuleByType)
e.GET("/live.xlottery.v1.Capsule/get_coupon_list", capsuleGetCouponList)
}
// ===============
// Storm Interface
// ===============
// 节奏风暴 服务
type StormBMServer interface {
// 开启节奏风暴
Start(ctx context.Context, req *StartStormReq) (resp *StartStormResp, err error)
// 节奏风暴是否能开启
CanStart(ctx context.Context, req *StartStormReq) (resp *CanStartStormResp, err error)
// 加入节奏风暴
Join(ctx context.Context, req *JoinStormReq) (resp *JoinStormResp, err error)
// 检查是否加入节奏风暴
Check(ctx context.Context, req *CheckStormReq) (resp *CheckStormResp, err error)
}
var v1StormSvc StormBMServer
func stormStart(c *bm.Context) {
p := new(StartStormReq)
if err := c.BindWith(p, binding.Default(c.Request.Method, c.Request.Header.Get("Content-Type"))); err != nil {
return
}
resp, err := v1StormSvc.Start(c, p)
c.JSON(resp, err)
}
func stormCanStart(c *bm.Context) {
p := new(StartStormReq)
if err := c.BindWith(p, binding.Default(c.Request.Method, c.Request.Header.Get("Content-Type"))); err != nil {
return
}
resp, err := v1StormSvc.CanStart(c, p)
c.JSON(resp, err)
}
func stormJoin(c *bm.Context) {
p := new(JoinStormReq)
if err := c.BindWith(p, binding.Default(c.Request.Method, c.Request.Header.Get("Content-Type"))); err != nil {
return
}
resp, err := v1StormSvc.Join(c, p)
c.JSON(resp, err)
}
func stormCheck(c *bm.Context) {
p := new(CheckStormReq)
if err := c.BindWith(p, binding.Default(c.Request.Method, c.Request.Header.Get("Content-Type"))); err != nil {
return
}
resp, err := v1StormSvc.Check(c, p)
c.JSON(resp, err)
}
// RegisterStormBMServer Register the blademaster route
func RegisterStormBMServer(e *bm.Engine, server StormBMServer) {
v1StormSvc = server
e.GET("/live.xlottery.v1.Storm/Start", stormStart)
e.GET("/live.xlottery.v1.Storm/CanStart", stormCanStart)
e.GET("/live.xlottery.v1.Storm/Join", stormJoin)
e.GET("/live.xlottery.v1.Storm/Check", stormCheck)
}

View File

@@ -0,0 +1,836 @@
<!-- package=live.xlottery.v1 -->
- [/live.xlottery.v1.Capsule/get_detail](#live.xlottery.v1.Capsuleget_detail)
- [/live.xlottery.v1.Capsule/open_capsule](#live.xlottery.v1.Capsuleopen_capsule)
- [/live.xlottery.v1.Capsule/get_coin_list](#live.xlottery.v1.Capsuleget_coin_list)
- [/live.xlottery.v1.Capsule/update_coin_config](#live.xlottery.v1.Capsuleupdate_coin_config)
- [/live.xlottery.v1.Capsule/update_coin_status](#live.xlottery.v1.Capsuleupdate_coin_status)
- [/live.xlottery.v1.Capsule/delete_coin](#live.xlottery.v1.Capsuledelete_coin)
- [/live.xlottery.v1.Capsule/get_pool_list](#live.xlottery.v1.Capsuleget_pool_list)
- [/live.xlottery.v1.Capsule/update_pool](#live.xlottery.v1.Capsuleupdate_pool)
- [/live.xlottery.v1.Capsule/delete_pool](#live.xlottery.v1.Capsuledelete_pool)
- [/live.xlottery.v1.Capsule/update_pool_status](#live.xlottery.v1.Capsuleupdate_pool_status)
- [/live.xlottery.v1.Capsule/get_pool_prize](#live.xlottery.v1.Capsuleget_pool_prize)
- [/live.xlottery.v1.Capsule/get_prize_type](#live.xlottery.v1.Capsuleget_prize_type)
- [/live.xlottery.v1.Capsule/get_prize_expire](#live.xlottery.v1.Capsuleget_prize_expire)
- [/live.xlottery.v1.Capsule/update_pool_prize](#live.xlottery.v1.Capsuleupdate_pool_prize)
- [/live.xlottery.v1.Capsule/delete_pool_prize](#live.xlottery.v1.Capsuledelete_pool_prize)
- [/live.xlottery.v1.Capsule/get_capsule_info](#live.xlottery.v1.Capsuleget_capsule_info)
- [/live.xlottery.v1.Capsule/open_capsule_by_type](#live.xlottery.v1.Capsuleopen_capsule_by_type)
- [/live.xlottery.v1.Capsule/get_coupon_list](#live.xlottery.v1.Capsuleget_coupon_list)
## /live.xlottery.v1.Capsule/get_detail
### 无标题
#### 方法GET
#### 请求参数
|参数名|必选|类型|描述|
|:---|:---|:---|:---|
|uid|否|integer| 用户uid|
|from|否|string| 来源 h5 web room|
#### 响应
```javascript
{
"code": 0,
"message": "ok",
"data": {
// 普通扭蛋信息
"normal": {
"status": true,
// 扭蛋数量
"coin": 0,
// 变化值
"change": 0,
// 进度
"progress": {
// 当前进度
"now": 0,
// 最大进度
"max": 0
},
// 规则
"rule": "",
// 奖品列表
"gift": [
{
// 礼物名称
"name": "",
// 礼物图片
"image": "",
// 用法
"usage": {
// 用法描述
"text": "",
// 跳转链接
"url": ""
},
// web礼物图片
"web_image": "",
// mobile礼物图片
"mobile_image": ""
}
],
// 历史获奖列表
"list": [
{
// 数量
"num": 0,
// 礼物名称
"gift": "",
// 时间
"date": "",
// 用户名
"name": ""
}
]
},
// 梦幻扭蛋信息若梦幻扭蛋status=false则无coin、change、process、gift、list字段
"colorful": {
"status": true,
// 扭蛋数量
"coin": 0,
// 变化值
"change": 0,
// 进度
"progress": {
// 当前进度
"now": 0,
// 最大进度
"max": 0
},
// 规则
"rule": "",
// 奖品列表
"gift": [
{
// 礼物名称
"name": "",
// 礼物图片
"image": "",
// 用法
"usage": {
// 用法描述
"text": "",
// 跳转链接
"url": ""
},
// web礼物图片
"web_image": "",
// mobile礼物图片
"mobile_image": ""
}
],
// 历史获奖列表
"list": [
{
// 数量
"num": 0,
// 礼物名称
"gift": "",
// 时间
"date": "",
// 用户名
"name": ""
}
]
}
}
}
```
## /live.xlottery.v1.Capsule/open_capsule
### 无标题
#### 方法GET
#### 请求参数
|参数名|必选|类型|描述|
|:---|:---|:---|:---|
|uid|否|integer| 用户uid|
|type|否|string| 扭蛋类型|
|count|否|integer| 扭的个数|
|platform|否|string||
#### 响应
```javascript
{
"code": 0,
"message": "ok",
"data": {
// 扭蛋币扣除状态
"status": true,
// 奖品文案
"text": [
""
],
// 是否包含实物奖品
"isEntity": true,
// 用户扭蛋币拥有状态
"info": {
// 普通扭蛋币
"normal": {
// 拥有的币
"coin": 0,
// 变化值
"change": 0,
// 进度
"progress": {
// 当前进度
"now": 0,
// 最大进度
"max": 0
}
},
// 梦幻扭蛋币
"colorful": {
// 拥有的币
"coin": 0,
// 变化值
"change": 0,
// 进度
"progress": {
// 当前进度
"now": 0,
// 最大进度
"max": 0
}
}
},
// 头衔? 恒为空字符串, 忽略之
"showTitle": "",
// 奖品列表
"awards": [
{
// 奖品名字
"name": "",
// 奖品数量
"num": 0,
// 奖品 X 数量
"text": "",
// 奖品图片
"img": "",
// 奖品用法说明
"usage": {
// 用法描述
"text": "",
// 跳转链接
"url": ""
},
// web礼物图片
"web_image": "",
// mobile礼物图片
"mobile_image": ""
}
]
}
}
```
## /live.xlottery.v1.Capsule/get_coin_list
### 无标题
#### 方法GET
#### 请求参数
|参数名|必选|类型|描述|
|:---|:---|:---|:---|
|page|是|integer||
|page_size|是|integer||
#### 响应
```javascript
{
"code": 0,
"message": "ok",
"data": {
"total": 0,
"total_page": 0,
"list": [
{
"id": 0,
"title": "",
"change_num": 0,
"start_time": 0,
"end_time": 0,
"status": 0,
"gift_type": 0,
"gift_config": "",
"area_ids": [
{
"parent_id": 0,
"is_all": 0,
"list": [
0
]
}
]
}
]
}
}
```
## /live.xlottery.v1.Capsule/update_coin_config
### 无标题
#### 方法GET
#### 请求参数
```javascript
{
"id": 0,
"title": "",
"change_num": 0,
"start_time": 0,
"end_time": 0,
"status": 0,
"gift_type": 0,
"gift_ids": [
0
],
"area_ids": [
{
"parent_id": 0,
"is_all": 0,
"list": [
0
]
}
]
}
```
#### 响应
```javascript
{
"code": 0,
"message": "ok",
"data": {
"status": true
}
}
```
## /live.xlottery.v1.Capsule/update_coin_status
### 无标题
#### 方法GET
#### 请求参数
|参数名|必选|类型|描述|
|:---|:---|:---|:---|
|id|是|integer||
|status|否|integer||
#### 响应
```javascript
{
"code": 0,
"message": "ok",
"data": {
"status": true
}
}
```
## /live.xlottery.v1.Capsule/delete_coin
### 无标题
#### 方法GET
#### 请求参数
|参数名|必选|类型|描述|
|:---|:---|:---|:---|
|id|是|integer||
#### 响应
```javascript
{
"code": 0,
"message": "ok",
"data": {
"status": true
}
}
```
## /live.xlottery.v1.Capsule/get_pool_list
### 无标题
#### 方法GET
#### 请求参数
|参数名|必选|类型|描述|
|:---|:---|:---|:---|
|page|是|integer||
|page_size|是|integer||
#### 响应
```javascript
{
"code": 0,
"message": "ok",
"data": {
"total": 0,
"total_page": 0,
"list": [
{
"id": 0,
"coin_id": 0,
"title": "",
"coin_title": "",
"start_time": 0,
"end_time": 0,
"status": 0,
"rule": ""
}
]
}
}
```
## /live.xlottery.v1.Capsule/update_pool
### 无标题
#### 方法GET
#### 请求参数
|参数名|必选|类型|描述|
|:---|:---|:---|:---|
|id|否|integer||
|coin_id|是|integer||
|title|是|string||
|start_time|是|integer||
|end_time|是|integer||
|rule|是|string||
#### 响应
```javascript
{
"code": 0,
"message": "ok",
"data": {
"status": true
}
}
```
## /live.xlottery.v1.Capsule/delete_pool
### 无标题
#### 方法GET
#### 请求参数
|参数名|必选|类型|描述|
|:---|:---|:---|:---|
|id|是|integer||
#### 响应
```javascript
{
"code": 0,
"message": "ok",
"data": {
"status": true
}
}
```
## /live.xlottery.v1.Capsule/update_pool_status
### 无标题
#### 方法GET
#### 请求参数
|参数名|必选|类型|描述|
|:---|:---|:---|:---|
|id|是|integer||
|status|否|integer||
#### 响应
```javascript
{
"code": 0,
"message": "ok",
"data": {
"status": true
}
}
```
## /live.xlottery.v1.Capsule/get_pool_prize
### 无标题
#### 方法GET
#### 请求参数
|参数名|必选|类型|描述|
|:---|:---|:---|:---|
|pool_id|是|integer||
#### 响应
```javascript
{
"code": 0,
"message": "ok",
"data": {
"list": [
{
"id": 0,
"pool_id": 0,
"type": 0,
"num": 0,
"object_id": 0,
"web_url": "",
"mobile_url": "",
"description": "",
"jump_url": "",
"pro_type": 0,
"chance": "",
"loop": 0,
"limit": 0,
"name": "",
"weight": 0,
"white_uids": [
0
],
"expire": 0
}
]
}
}
```
## /live.xlottery.v1.Capsule/get_prize_type
### 无标题
#### 方法GET
#### 请求参数
#### 响应
```javascript
{
"code": 0,
"message": "ok",
"data": {
"list": [
{
"type": 0,
"name": ""
}
]
}
}
```
## /live.xlottery.v1.Capsule/get_prize_expire
### 无标题
#### 方法GET
#### 请求参数
#### 响应
```javascript
{
"code": 0,
"message": "ok",
"data": {
"list": [
{
"expire": 0,
"name": ""
}
]
}
}
```
## /live.xlottery.v1.Capsule/update_pool_prize
### 无标题
#### 方法GET
#### 请求参数
|参数名|必选|类型|描述|
|:---|:---|:---|:---|
|id|否|integer||
|pool_id|否|integer||
|type|是|integer||
|num|是|integer||
|object_id|否|integer||
|expire|否|integer||
|web_url|是|string||
|mobile_url|是|string||
|description|是|string||
|jump_url|否|string||
|pro_type|是|integer||
|chance|否|integer||
|loop|否|integer||
|limit|否|integer||
|weight|否|integer||
|white_uids|否|多个integer||
#### 响应
```javascript
{
"code": 0,
"message": "ok",
"data": {
"status": true,
"prize_id": 0
}
}
```
## /live.xlottery.v1.Capsule/delete_pool_prize
### 无标题
#### 方法GET
#### 请求参数
|参数名|必选|类型|描述|
|:---|:---|:---|:---|
|id|是|integer||
#### 响应
```javascript
{
"code": 0,
"message": "ok",
"data": {
"status": true
}
}
```
## /live.xlottery.v1.Capsule/get_capsule_info
### 无标题
#### 方法GET
#### 请求参数
|参数名|必选|类型|描述|
|:---|:---|:---|:---|
|uid|否|integer| 用户uid|
|type|否|string| 类型|
|from|否|string| 来源 h5 web room|
#### 响应
```javascript
{
"code": 0,
"message": "ok",
"data": {
// 扭蛋数量
"coin": 0,
// 规则
"rule": "",
// 奖品列表,包含数量
"gift_list": [
{
// 礼物id
"id": 0,
// 礼物名称
"name": "",
// 礼物数量
"num": 0,
// 权重
"weight": 0,
// 礼物图片
"web_url": "",
// 礼物图片
"mobile_url": "",
// 用法
"usage": {
// 用法描述
"text": "",
// 跳转链接
"url": ""
},
// 奖品类型 2 头衔
"type": 0,
// 过期时间
"expire": ""
}
],
// 奖品列表,不包含数量,同一类别只有一条
"gift_filter": [
{
// 礼物id
"id": 0,
// 礼物名称
"name": "",
// 礼物图片
"web_url": "",
// 礼物图片
"mobile_url": "",
// 用法
"usage": {
// 用法描述
"text": "",
// 跳转链接
"url": ""
}
}
]
}
}
```
## /live.xlottery.v1.Capsule/open_capsule_by_type
### 无标题
#### 方法GET
#### 请求参数
|参数名|必选|类型|描述|
|:---|:---|:---|:---|
|uid|否|integer| 用户uid|
|type|否|string| 扭蛋类型week周星|
|count|否|integer| 扭的个数 1 10 100|
|platform|否|string| 平台|
#### 响应
```javascript
{
"code": 0,
"message": "ok",
"data": {
// 扭蛋币扣除状态
"status": true,
// 是否包含实物奖品
"isEntity": true,
// 用户扭蛋币拥有状态
"info": {
// 拥有的币
"coin": 0
},
// 奖品列表
"awards": [
{
// 奖品id
"id": 0,
// 奖品名字
"name": "",
// 奖品数量
"num": 0,
// 奖品 X 数量
"text": "",
// 礼物图片
"web_url": "",
// 礼物图片
"mobile_url": "",
// 奖品用法说明
"usage": {
// 用法描述
"text": "",
// 跳转链接
"url": ""
},
// 奖品权重
"weight": 0,
// 奖品类型 2 头衔
"type": 0,
// 过期时间
"expire": ""
}
],
// 奖品列表
"text": [
""
]
}
}
```
## /live.xlottery.v1.Capsule/get_coupon_list
### 无标题
#### 方法GET
#### 请求参数
|参数名|必选|类型|描述|
|:---|:---|:---|:---|
|uid|否|integer||
#### 响应
```javascript
{
"code": 0,
"message": "ok",
"data": {
"list": [
{
"uid": 0,
// 中奖时间
"award_time": "",
// 奖品名称
"award_name": "",
// 券码
"award_code": "",
// 0 重试 1 成功
"status": 0,
// 上次重试时间
"retry_time": ""
}
]
}
}
```

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,647 @@
syntax = "proto3";
package live.xlottery.v1;
option go_package = "v1";
import "github.com/gogo/protobuf/gogoproto/gogo.proto";
service Capsule {
rpc get_detail(CapsuleGetDetailReq) returns (CapsuleGetDetailResp);
rpc open_capsule(CapsuleOpenCapsuleReq) returns (CapsuleOpenCapsuleResp);
rpc get_coin_list(GetCoinListReq) returns (GetCoinListResp);
rpc update_coin_config(UpdateCoinConfigReq) returns (UpdateCoinConfigResp);
rpc update_coin_status(UpdateCoinStatusReq) returns (UpdateCoinStatusResp);
rpc delete_coin(DeleteCoinReq) returns (DeleteCoinResp);
rpc get_pool_list(GetPoolListReq) returns (GetPoolListResp);
rpc update_pool(UpdatePoolReq) returns (UpdatePoolResp);
rpc delete_pool(DeletePoolReq) returns (DeletePoolResp);
rpc update_pool_status(UpdatePoolStatusReq) returns (UpdatePoolStatusResp);
rpc get_pool_prize(GetPoolPrizeReq) returns (GetPoolPrizeResp);
rpc get_prize_type (GetPrizeTypeReq) returns (GetPrizeTypeResp);
rpc get_prize_expire (GetPrizeExpireReq) returns (GetPrizeExpireResp);
rpc update_pool_prize(UpdatePoolPrizeReq) returns (UpdatePoolPrizeResp);
rpc delete_pool_prize(DeletePoolPrizeReq) returns (DeletePoolPrizeResp);
rpc get_capsule_info (CapsuleGetCapsuleInfoReq) returns (CapsuleGetCapsuleInfoResp);
rpc open_capsule_by_type (CapsuleOpenCapsuleByTypeReq) returns (CapsuleOpenCapsuleByTypeResp);
rpc get_coupon_list (CapsuleGetCouponListReq) returns (CapsuleGetCouponListResp);
}
message CapsuleGetDetailReq {
// 用户uid
int64 uid = 1 [(gogoproto.jsontag) = "uid"];
// 来源 h5 web room
string from = 2 [(gogoproto.jsontag) = "from"];
}
message Usage {
// 用法描述
string text = 1 [(gogoproto.jsontag) = "text"];
// 跳转链接
string url = 2 [(gogoproto.jsontag) = "url"];
}
message Progress {
// 当前进度
int64 now = 1 [(gogoproto.jsontag) = "now"];
// 最大进度
int64 max = 2 [(gogoproto.jsontag) = "max"];
}
message CapsuleGetDetailResp {
// 普通扭蛋信息
CapsuleInfo normal = 1 [(gogoproto.jsontag) = "normal"];
// 梦幻扭蛋信息若梦幻扭蛋status=false则无coin、change、process、gift、list字段
CapsuleInfo colorful = 2 [(gogoproto.jsontag) = "colorful"];
message Gift {
// 礼物名称
string name = 2 [(gogoproto.jsontag) = "name"];
// 礼物图片
string image = 3 [(gogoproto.jsontag) = "image"];
// 用法
Usage usage = 4 [(gogoproto.jsontag) = "usage"];
// web礼物图片
string web_image = 5 [(gogoproto.jsontag) = "web_image"];
// mobile礼物图片
string mobile_image = 6 [(gogoproto.jsontag) = "mobile_image"];
}
message List {
// 数量
int64 num = 1 [(gogoproto.jsontag) = "num"];
// 礼物名称
string gift = 2 [(gogoproto.jsontag) = "gift"];
// 时间
string date = 3 [(gogoproto.jsontag) = "date"];
// 用户名
string name = 4 [(gogoproto.jsontag) = "name"];
}
message CapsuleInfo {
//
bool status = 1 [(gogoproto.jsontag) = "status"];
// 扭蛋数量
int64 coin = 2 [(gogoproto.jsontag) = "coin"];
// 变化值
int64 change = 3 [(gogoproto.jsontag) = "change"];
// 进度
Progress progress = 4 [(gogoproto.jsontag) = "progress"];
// 规则
string rule = 5 [(gogoproto.jsontag) = "rule"];
// 奖品列表
repeated Gift gift = 6 [(gogoproto.jsontag) = "gift"];
// 历史获奖列表
repeated List list = 7 [(gogoproto.jsontag) = "list"];
}
}
message CapsuleOpenCapsuleReq {
// 用户uid
int64 uid = 1 [(gogoproto.jsontag) = "uid"];
// 扭蛋类型
string type = 2 [(gogoproto.jsontag) = "type"];
// 扭的个数
int64 count = 3 [(gogoproto.jsontag) = "count"];
string platform = 4 [(gogoproto.jsontag) = "platform"];
}
message CapsuleOpenCapsuleResp {
// 扭蛋币扣除状态
bool status = 1 [(gogoproto.jsontag) = "status"];
// 奖品文案
repeated string text = 2 [(gogoproto.jsontag) = "text"];
// 是否包含实物奖品
bool isEntity = 3 [(gogoproto.jsontag) = "isEntity"];
// 用户扭蛋币拥有状态
Info info = 4 [(gogoproto.jsontag) = "info"];
// 头衔? 恒为空字符串, 忽略之
string showTitle = 5 [(gogoproto.jsontag) = "showTitle"];
// 奖品列表
repeated Award awards = 6 [(gogoproto.jsontag) = "awards"];
message CapsuleInfo {
// 拥有的币
int64 coin = 1 [(gogoproto.jsontag) = "coin"];
// 变化值
int64 change = 2 [(gogoproto.jsontag) = "change"];
// 进度
Progress progress = 3 [(gogoproto.jsontag) = "progress"];
}
message Info {
// 普通扭蛋币
CapsuleInfo normal = 1 [(gogoproto.jsontag) = "normal"];
// 梦幻扭蛋币
CapsuleInfo colorful = 2 [(gogoproto.jsontag) = "colorful"];
}
message Award {
// 奖品名字
string name = 2 [(gogoproto.jsontag) = "name"];
// 奖品数量
int64 num = 3 [(gogoproto.jsontag) = "num"];
// 奖品 X 数量
string text = 4 [(gogoproto.jsontag) = "text"];
// 奖品图片
string img = 5 [(gogoproto.jsontag) = "img"];
// 奖品用法说明
Usage usage = 6 [(gogoproto.jsontag) = "usage"];
// web礼物图片
string web_image = 7 [(gogoproto.jsontag) = "web_image"];
// mobile礼物图片
string mobile_image = 8 [(gogoproto.jsontag) = "mobile_image"];
}
}
message CapsuleGetCapsuleInfoReq {
// 用户uid
int64 uid = 1 [(gogoproto.jsontag) = "uid"];
// 类型
string type = 2 [(gogoproto.jsontag) = "type"];
// 来源 h5 web room
string from = 3 [(gogoproto.jsontag) = "from"];
}
message CapsuleGetCapsuleInfoResp {
message GiftList {
// 礼物id
int64 id = 1 [(gogoproto.jsontag) = "id"];
// 礼物名称
string name = 2 [(gogoproto.jsontag) = "name"];
// 礼物数量
int64 num = 3 [(gogoproto.jsontag) = "num"];
// 权重
int64 weight = 4 [(gogoproto.jsontag) = "weight"];
// 礼物图片
string web_url = 5 [(gogoproto.jsontag) = "web_url"];
// 礼物图片
string mobile_url = 6 [(gogoproto.jsontag) = "mobile_url"];
// 用法
Usage usage = 7 [(gogoproto.jsontag) = "usage"];
// 奖品类型 2 头衔
int64 type = 8 [(gogoproto.jsontag) = "type"];
// 过期时间
string expire = 9 [(gogoproto.jsontag) = "expire"];
}
message GiftFilter {
// 礼物id
int64 id = 1 [(gogoproto.jsontag) = "id"];
// 礼物名称
string name = 2 [(gogoproto.jsontag) = "name"];
// 礼物图片
string web_url = 3 [(gogoproto.jsontag) = "web_url"];
// 礼物图片
string mobile_url = 4 [(gogoproto.jsontag) = "mobile_url"];
// 用法
Usage usage = 5 [(gogoproto.jsontag) = "usage"];
}
// 扭蛋数量
int64 coin = 1 [(gogoproto.jsontag) = "coin"];
// 规则
string rule = 2 [(gogoproto.jsontag) = "rule"];
// 奖品列表,包含数量
repeated GiftList gift_list = 3 [(gogoproto.jsontag) = "gift_list"];
// 奖品列表,不包含数量,同一类别只有一条
repeated GiftFilter gift_filter = 4 [(gogoproto.jsontag) = "gift_filter"];
}
message CapsuleOpenCapsuleByTypeReq {
// 用户uid
int64 uid = 1 [(gogoproto.jsontag) = "uid"];
// 扭蛋类型week周星
string type = 2 [(gogoproto.jsontag) = "type"];
// 扭的个数 1 10 100
int64 count = 3 [(gogoproto.jsontag) = "count"];
// 平台
string platform = 4 [(gogoproto.jsontag) = "platform"];
}
message CapsuleOpenCapsuleByTypeResp {
message CapsuleInfo {
// 拥有的币
int64 coin = 1 [(gogoproto.jsontag) = "coin"];
}
message Award {
// 奖品id
int64 id = 1 [(gogoproto.jsontag) = "id"];
// 奖品名字
string name = 2 [(gogoproto.jsontag) = "name"];
// 奖品数量
int64 num = 3 [(gogoproto.jsontag) = "num"];
// 奖品 X 数量
string text = 4 [(gogoproto.jsontag) = "text"];
// 礼物图片
string web_url = 5 [(gogoproto.jsontag) = "web_url"];
// 礼物图片
string mobile_url = 6 [(gogoproto.jsontag) = "mobile_url"];
// 奖品用法说明
Usage usage = 7 [(gogoproto.jsontag) = "usage"];
// 奖品权重
int64 weight = 8 [(gogoproto.jsontag) = "weight"];
// 奖品类型 2 头衔
int64 type = 9 [(gogoproto.jsontag) = "type"];
// 过期时间
string expire = 10 [(gogoproto.jsontag) = "expire"];
}
// 扭蛋币扣除状态
bool status = 1 [(gogoproto.jsontag) = "status"];
// 是否包含实物奖品
bool isEntity = 2 [(gogoproto.jsontag) = "isEntity"];
// 用户扭蛋币拥有状态
CapsuleInfo info = 3 [(gogoproto.jsontag) = "info"];
// 奖品列表
repeated Award awards = 4 [(gogoproto.jsontag) = "awards"];
// 奖品列表
repeated string text = 5 [(gogoproto.jsontag) = "text"];
}
message GetCoinListReq{
int64 page = 1 [(gogoproto.jsontag) = "page", (gogoproto.moretags) = 'form:"page" validate:"required"']; //页码从1开始
int64 page_size = 2 [(gogoproto.jsontag) = "page_size", (gogoproto.moretags) = 'form:"page_size" validate:"required"'];//页面的大小
}
message GetCoinListResp{
int64 total = 1 [(gogoproto.jsontag) = "total"]; //
int64 total_page = 2 [(gogoproto.jsontag) = "total_page"];
repeated List list = 3 [(gogoproto.jsontag) = "list"];
message List{
int64 id = 1 [(gogoproto.jsontag) = "id"];//扭蛋ID
string title = 2 [(gogoproto.jsontag) = "title"];//名称 普通扭蛋, 梦幻扭蛋
int64 change_num = 3 [(gogoproto.jsontag) = "change_num"];//转化数量
int64 start_time = 4 [(gogoproto.jsontag) = "start_time"];//开始时间
int64 end_time = 5 [(gogoproto.jsontag) = "end_time"];//结束时间
int64 status = 6 [(gogoproto.jsontag) = "status"]; //状态 0为下线1为上线
int64 gift_type = 7 [(gogoproto.jsontag) = "gift_type"];//获得方式 1为所有瓜子道具2为所有金瓜子道具3为指定道具ID
string gift_config = 8 [(gogoproto.jsontag) = "gift_config"];// 道具的ID
repeated AreaIds area_ids = 9 [(gogoproto.jsontag) = "area_ids"];//活动分区
message AreaIds{
int64 parent_id = 1 [(gogoproto.jsontag) = "parent_id"];// 父分区ID
int64 is_all = 2 [(gogoproto.jsontag) = "is_all"];// 是否全选
repeated int64 list = 3 [(gogoproto.jsontag) = "list"];// 子分区ID
}
}
}
message UpdateCoinConfigReq{
int64 id = 1 [(gogoproto.jsontag) = "id",(gogoproto.moretags) = 'form:"id"'];//扭蛋ID
string title = 2 [(gogoproto.jsontag) = "title", (gogoproto.moretags) = 'form:"title" validate:"required"'];//名称 普通扭蛋, 梦幻扭蛋
int64 change_num = 3 [(gogoproto.jsontag) = "change_num", (gogoproto.moretags) = 'form:"change_num" validate:"required"'];//转化数量
int64 start_time = 4 [(gogoproto.jsontag) = "start_time", (gogoproto.moretags) = 'form:"start_time" validate:"required"'];//开始时间
int64 end_time = 5 [(gogoproto.jsontag) = "end_time", (gogoproto.moretags) = 'form:"end_time" validate:"required"'];//结束时间
int64 status = 6 [(gogoproto.jsontag) = "status", (gogoproto.moretags) = 'form:"status" validate:"required"']; //状态 0为下线1为上线
int64 gift_type = 7 [(gogoproto.jsontag) = "gift_type", (gogoproto.moretags) = 'form:"gift_type" validate:"required"'];//获得方式 1为所有瓜子道具2为所有金瓜子道具3为指定道具ID
repeated int64 gift_ids = 8 [(gogoproto.jsontag) = "gift_ids", (gogoproto.moretags) = 'form:"gift_ids"'];// 道具的ID
repeated AreaIds area_ids = 9 [(gogoproto.jsontag) = "area_ids", (gogoproto.moretags) = 'form:"area_ids" validate:"required"'];//活动分区
message AreaIds{
int64 parent_id = 1 [(gogoproto.jsontag) = "parent_id", (gogoproto.moretags) = 'form:"parent_id" validate:"required"'];// 父分区ID
int64 is_all = 2 [(gogoproto.jsontag) = "is_all", (gogoproto.moretags) = 'form:"is_all"'];// 是否全选
repeated int64 list = 3 [(gogoproto.jsontag) = "list", (gogoproto.moretags) = 'form:"list" validate:"required"'];// 子分区ID
}
}
message UpdateCoinConfigResp{
bool status = 1 [(gogoproto.jsontag) = "status"];
}
message UpdateCoinStatusReq{
int64 id = 1 [(gogoproto.jsontag) = "id", (gogoproto.moretags) = 'form:"id" validate:"required"']; //扭蛋币id
int64 status = 2 [(gogoproto.jsontag) = "status", (gogoproto.moretags) = 'form:"status"'];//状态 0为下线1为上线
}
message UpdateCoinStatusResp{
bool status = 1 [(gogoproto.jsontag) = "status"];
}
message DeleteCoinReq{
int64 id = 1 [(gogoproto.jsontag) = "id", (gogoproto.moretags) = 'form:"id" validate:"required"'];//扭蛋币id
}
message DeleteCoinResp{
bool status = 1 [(gogoproto.jsontag) = "status"];
}
message GetPoolListReq{
int64 page = 1 [(gogoproto.jsontag) = "page", (gogoproto.moretags) = 'form:"page" validate:"required"'];//页码
int64 page_size = 2 [(gogoproto.jsontag) = "page_size", (gogoproto.moretags) = 'form:"page_size" validate:"required"'];//页面的大小
}
message GetPoolListResp{
int64 total = 1 [(gogoproto.jsontag) = "total"];
int64 total_page = 2 [(gogoproto.jsontag) = "total_page"];
repeated List list = 3 [(gogoproto.jsontag) = "list"];
message List{
int64 id = 1 [(gogoproto.jsontag) = "id"];// 奖池id
int64 coin_id = 2 [(gogoproto.jsontag) = "coin_id"];// 扭蛋名称
string title = 3 [(gogoproto.jsontag) = "title"];//奖池名称
string coin_title = 8 [(gogoproto.jsontag) = "coin_title"];//扭蛋币名称
int64 start_time = 4 [(gogoproto.jsontag) = "start_time"];//开始时间
int64 end_time = 5 [(gogoproto.jsontag) = "end_time"];//结束时间
int64 status = 6 [(gogoproto.jsontag) = "status"];//状态 0为下线1为上线
string rule = 7 [(gogoproto.jsontag) = "rule"];//描述
}
}
message UpdatePoolReq{
int64 id = 1 [(gogoproto.jsontag) = "id", (gogoproto.moretags) = 'form:"id"'];// 奖池id
int64 coin_id = 2 [(gogoproto.jsontag) = "coin_id", (gogoproto.moretags) = 'form:"coin_id" validate:"required"'];// 扭蛋名称
string title = 3 [(gogoproto.jsontag) = "title", (gogoproto.moretags) = 'form:"title" validate:"required"'];//奖池名称
int64 start_time = 4 [(gogoproto.jsontag) = "start_time", (gogoproto.moretags) = 'form:"start_time" validate:"required"'];//开始时间
int64 end_time = 5 [(gogoproto.jsontag) = "end_time", (gogoproto.moretags) = 'form:"end_time" validate:"required"'];//结束时间
string rule = 6 [(gogoproto.jsontag) = "rule", (gogoproto.moretags) = 'form:"rule" validate:"required"'];//描述
}
message UpdatePoolResp{
bool status = 1 [(gogoproto.jsontag) = "status"];
}
message DeletePoolReq{
int64 id = 1 [(gogoproto.jsontag) = "id", (gogoproto.moretags) = 'form:"id" validate:"required"'];//奖池id
}
message DeletePoolResp{
bool status = 1 [(gogoproto.jsontag) = "status"];
}
message UpdatePoolStatusReq{
int64 id = 1 [(gogoproto.jsontag) = "id", (gogoproto.moretags) = 'form:"id" validate:"required"'];//奖池id
int64 status = 2 [(gogoproto.jsontag) = "status", (gogoproto.moretags) = 'form:"status"'];//状态 0为未上线1为上线
}
message UpdatePoolStatusResp{
bool status = 1 [(gogoproto.jsontag) = "status"];
}
message GetPoolPrizeReq{
int64 pool_id = 1 [(gogoproto.jsontag) = "pool_id", (gogoproto.moretags) = 'form:"pool_id" validate:"required"'];//奖池id
}
message GetPoolPrizeResp{
repeated List list = 1 [(gogoproto.jsontag) = "list"];
message List{
int64 id = 1 [(gogoproto.jsontag) = "id"]; //奖励id
int64 pool_id = 2 [(gogoproto.jsontag) = "pool_id"]; //奖池id
int64 type = 3 [(gogoproto.jsontag) = "type"]; //奖品类型 1为道具2为头衔3为经验原石4为经验曜石5为贤者之石6为小号小电视7为舰长守护8为提督守护9为总督守护
int64 num = 4 [(gogoproto.jsontag) = "num"]; //数量
int64 object_id = 5 [(gogoproto.jsontag) = "object_id"]; //奖品真实id
string web_url = 6 [(gogoproto.jsontag) = "web_url"]; //web端图片
string mobile_url = 7 [(gogoproto.jsontag) = "mobile_url"]; //移动端图片
string description = 8 [(gogoproto.jsontag) = "description"]; //奖励描述
string jump_url = 9 [(gogoproto.jsontag) = "jump_url"]; //跳转地址
int64 pro_type = 10 [(gogoproto.jsontag) = "pro_type"]; //概率类型 1为普通2为固定每天3为固定每周
string chance = 11 [(gogoproto.jsontag) = "chance"]; //概率3位小数''为另一种概率模式
int64 loop = 12 [(gogoproto.jsontag) = "loop"]; //循环的数量 0为另一种概率模式
int64 limit = 13 [(gogoproto.jsontag) = "limit"]; //限制数量 0为另一种概率模式
string name = 14 [(gogoproto.jsontag) = "name"]; // 奖励名称
int64 weight = 15 [(gogoproto.jsontag) = "weight"];
repeated int64 white_uids = 16 [(gogoproto.jsontag) = "white_uids"];
int64 expire = 17 [(gogoproto.jsontag) = "expire"];
}
}
message UpdatePoolPrizeReq{
int64 id = 1 [(gogoproto.jsontag) = "id", (gogoproto.moretags) = 'form:"id"'];//奖励id
int64 pool_id = 2 [(gogoproto.jsontag) = "pool_id", (gogoproto.moretags) = 'form:"pool_id"'];//奖池id
int64 type = 3 [(gogoproto.jsontag) = "type", (gogoproto.moretags) = 'form:"type" validate:"required"'];//奖品类型 1为道具2为头衔3为经验原石4为经验曜石5为贤者之石6为小号小电视7为舰长守护8为提督守护9为总督守护
int64 num = 4 [(gogoproto.jsontag) = "num", (gogoproto.moretags) = 'form:"num" validate:"required"'];//数量
int64 object_id = 5 [(gogoproto.jsontag) = "object_id", (gogoproto.moretags) = 'form:"object_id"'];//奖品真实id
int64 expire = 6 [(gogoproto.jsontag) = "expire", (gogoproto.moretags) = 'form:"expire"'];//过期时间
string web_url = 7 [(gogoproto.jsontag) = "web_url", (gogoproto.moretags) = 'form:"web_url" validate:"required"'];//web端图片
string mobile_url = 8 [(gogoproto.jsontag) = "mobile_url", (gogoproto.moretags) = 'form:"mobile_url" validate:"required"'];//移动端图片
string description = 9 [(gogoproto.jsontag) = "description", (gogoproto.moretags) = 'form:"description" validate:"required"'];//奖励描述
string jump_url = 10 [(gogoproto.jsontag) = "jump_url", (gogoproto.moretags) = 'form:"jump_url"'];//跳转地址
int64 pro_type = 11 [(gogoproto.jsontag) = "pro_type", (gogoproto.moretags) = 'form:"pro_type" validate:"required"'];//概率类型 1为普通2为固定每天3为固定每周
int64 chance = 12 [(gogoproto.jsontag) = "chance", (gogoproto.moretags) = 'form:"chance"'];//概率3位小数''为另一种概率模式
int64 loop = 13 [(gogoproto.jsontag) = "loop", (gogoproto.moretags) = 'form:"loop"'];//循环的数量 0为另一种概率模式
int64 limit = 14 [(gogoproto.jsontag) = "limit", (gogoproto.moretags) = 'form:"limit"'];//限制数量 0为另一种概率模式
int64 weight = 15 [(gogoproto.jsontag) = "weight"];
repeated int64 white_uids = 16 [(gogoproto.jsontag) = "white_uids"];
}
message UpdatePoolPrizeResp{
bool status = 1 [(gogoproto.jsontag) = "status"];
int64 prize_id = 2 [(gogoproto.jsontag) = "prize_id"];
}
message DeletePoolPrizeReq{
int64 id = 1 [(gogoproto.jsontag) = "id", (gogoproto.moretags) = 'form:"id" validate:"required"'];//奖励id
}
message DeletePoolPrizeResp{
bool status = 1 [(gogoproto.jsontag) = "status"];
}
message GetPrizeTypeReq {
}
message GetPrizeTypeResp {
repeated List list = 3 [(gogoproto.jsontag) = "list"];
message List {
//
int64 type = 1 [(gogoproto.jsontag) = "type"];
//
string name = 2 [(gogoproto.jsontag) = "name"];
}
}
message GetPrizeExpireReq {
}
message GetPrizeExpireResp {
repeated List list = 3 [(gogoproto.jsontag) = "list"];
message List {
//
int64 expire = 1 [(gogoproto.jsontag) = "expire"];
//
string name = 2 [(gogoproto.jsontag) = "name"];
}
}
// 节奏风暴 服务
service Storm {
// 开启节奏风暴
rpc Start(StartStormReq)returns(StartStormResp);
//节奏风暴是否能开启
rpc CanStart(StartStormReq)returns(CanStartStormResp);
//加入节奏风暴
rpc Join (JoinStormReq)returns(JoinStormResp);
//检查是否加入节奏风暴
rpc Check (CheckStormReq)returns(CheckStormResp);
}
message StartStormReq {
// 用户id
int64 uid = 1;
// 主播id
int64 ruid = 2;
//房间号
int64 roomid = 3;
//是否开启敏感词过滤
bool useShield = 4;
//道具数量
int64 num = 5;
//节奏内容id
int64 beatid = 6;
//
int64 skipExternalCheck= 7;
}
message StartStormResp {
// 错误码
int32 code = 1;
// 错误信息
string msg = 2;
StartData start = 3;
}
message StartData {
//倒计时,秒
int32 time = 1;
//抽奖标识
int64 id = 2;
}
message CanStartStormResp {
// 错误码
int32 code = 1;
// 错误信息
string msg = 2;
//是否能开启节奏风暴
bool ret_status = 3;
}
message JoinStormReq {
//抽奖id
int64 id = 1 [(gogoproto.moretags) = 'form:"id"'];
//房间id
int64 roomid = 2 [(gogoproto.moretags) = 'form:"roomid" '];
//弹幕颜色
string color = 3 [(gogoproto.moretags) = 'form:"color"'];
//userid
int64 mid = 4 [(gogoproto.moretags) = 'form:"mid"'];
//平台 webiosandroid
string platform = 5 [(gogoproto.moretags) = 'form:"platform"'];
//验证码标识
string captcha_token = 6 [(gogoproto.moretags) = 'form:"captcha_token"'];
//验证码明文
string captcha_phrase = 7 [(gogoproto.moretags) = 'form:"captcha_phrase"'];
}
message JoinStormResp {
// 错误码
int32 code = 1;
// 错误信息
string msg = 2;
// 加入成功返回数据
JoinData join = 3;
}
message JoinData {
//礼物id
int64 gift_id = 1 [(gogoproto.jsontag) = "gift_id"];
//标题
string title = 2 [(gogoproto.jsontag) = "title"];
//礼物web内容
string content = 3 [(gogoproto.jsontag) = "content"];
//礼物移动端内容
string mobile_content = 4 [(gogoproto.jsontag) = "mobile_content"];
//礼物图片
string gift_img = 5 [(gogoproto.jsontag) = "gift_img"];
//礼物数量
int64 gift_num = 6 [(gogoproto.jsontag) = "gift_num"];
//礼物名字
string gift_name = 7 [(gogoproto.jsontag) = "gift_name"];
}
message CheckStormReq {
//房间号
int64 roomid = 1 [(gogoproto.moretags) = 'form:"roomid" validate:"required"'];
//用户id
int64 uid = 2 [(gogoproto.moretags) = 'form:"uid" '];
}
message CheckStormResp {
// 错误码
int32 code = 1;
// 错误信息
string msg = 2;
CheckData check = 3;
}
message CheckData {
//用户id
int64 id = 1 [(gogoproto.jsontag) = "id"];
//房间号
int64 roomid = 2 [(gogoproto.jsontag) = "roomid"];
//数量
int64 num = 3 [(gogoproto.jsontag) = "num"];
//发送数量
string send_num = 4 [(gogoproto.jsontag) = "send_num"];
//结束时间戳
int64 time = 5 [(gogoproto.jsontag) = "time"];
//内容
string content = 6 [(gogoproto.jsontag) = "content"];
//是否已经加入
int32 hasJoin = 7 [(gogoproto.jsontag) = "hasJoin"]; //是否加入
//图片链接
string storm_gif = 8 [(gogoproto.jsontag) = "storm_gif"];
}
message CapsuleGetCouponListReq {
//
int64 uid = 1 [(gogoproto.jsontag) = "uid"];
}
message CapsuleGetCouponListResp {
message List {
//
int64 uid = 1 [(gogoproto.jsontag) = "uid"];
// 中奖时间
string award_time = 2 [(gogoproto.jsontag) = "award_time"];
// 奖品名称
string award_name = 3 [(gogoproto.jsontag) = "award_name"];
// 券码
string award_code = 4 [(gogoproto.jsontag) = "award_code"];
// 0 重试 1 成功
int64 status = 5 [(gogoproto.jsontag) = "status"];
// 上次重试时间
string retry_time = 6 [(gogoproto.jsontag) = "retry_time"];
}
repeated List list = 1 [(gogoproto.jsontag) = "list"];
}

View File

@@ -0,0 +1,175 @@
<!-- package=live.xlottery.v1 -->
- [/live.xlottery.v1.Storm/Start](#live.xlottery.v1.StormStart) 开启节奏风暴
- [/live.xlottery.v1.Storm/CanStart](#live.xlottery.v1.StormCanStart) 节奏风暴是否能开启
- [/live.xlottery.v1.Storm/Join](#live.xlottery.v1.StormJoin) 加入节奏风暴
- [/live.xlottery.v1.Storm/Check](#live.xlottery.v1.StormCheck) 检查是否加入节奏风暴
## /live.xlottery.v1.Storm/Start
### 开启节奏风暴
#### 方法GET
#### 请求参数
|参数名|必选|类型|描述|
|:---|:---|:---|:---|
|uid|否|integer| 用户id|
|ruid|否|integer| 主播id|
|roomid|否|integer|房间号|
|useShield|否|bool|是否开启敏感词过滤|
|num|否|integer|道具数量|
|beatid|否|integer|节奏内容id|
|skipExternalCheck|否|integer||
#### 响应
```javascript
{
"code": 0,
"message": "ok",
"data": {
// 错误码
"code": 0,
// 错误信息
"msg": "",
"start": {
// 倒计时,秒
"time": 0,
// 抽奖标识
"id": 0
}
}
}
```
## /live.xlottery.v1.Storm/CanStart
###节奏风暴是否能开启
#### 方法GET
#### 请求参数
|参数名|必选|类型|描述|
|:---|:---|:---|:---|
|uid|否|integer| 用户id|
|ruid|否|integer| 主播id|
|roomid|否|integer|房间号|
|useShield|否|bool|是否开启敏感词过滤|
|num|否|integer|道具数量|
|beatid|否|integer|节奏内容id|
|skipExternalCheck|否|integer||
#### 响应
```javascript
{
"code": 0,
"message": "ok",
"data": {
// 错误码
"code": 0,
// 错误信息
"msg": "",
// 是否能开启节奏风暴
"ret_status": true
}
}
```
## /live.xlottery.v1.Storm/Join
###加入节奏风暴
#### 方法GET
#### 请求参数
|参数名|必选|类型|描述|
|:---|:---|:---|:---|
|id|否|integer|抽奖id|
|roomid|否|integer|房间id|
|color|否|string|弹幕颜色 |
|mid|否|integer|userid |
|platform|否|string|平台 webiosandroid|
|captcha_token|否|string|验证码标识|
|captcha_phrase|否|string|验证码明文 |
#### 响应
```javascript
{
"code": 0,
"message": "ok",
"data": {
// 错误码
"code": 0,
// 错误信息
"msg": "",
// 加入成功返回数据
"join": {
// 礼物id
"gift_id": 0,
// 标题
"title": "",
// 礼物web内容
"content": "",
// 礼物移动端内容
"mobile_content": "",
// 礼物图片
"gift_img": "",
// 礼物数量
"gift_num": 0,
// 礼物名字
"gift_name": ""
}
}
}
```
## /live.xlottery.v1.Storm/Check
###检查是否加入节奏风暴
#### 方法GET
#### 请求参数
|参数名|必选|类型|描述|
|:---|:---|:---|:---|
|roomid|是|integer|房间号|
|uid|否|integer|用户id|
#### 响应
```javascript
{
"code": 0,
"message": "ok",
"data": {
// 错误码
"code": 0,
// 错误信息
"msg": "",
"check": {
// 用户id
"id": 0,
// 房间号
"roomid": 0,
// 数量
"num": 0,
// 发送数量
"send_num": "",
// 结束时间戳
"time": 0,
// 内容
"content": "",
// 是否已经加入
"hasJoin": 0,
// 图片链接
"storm_gif": ""
}
}
}
```

View File

@@ -0,0 +1,31 @@
package v1
import (
"context"
"go-common/library/net/rpc/warden"
"google.golang.org/grpc"
)
// AppID unique app id for service discovery
const AppID = "live.xlottery"
// Client grpc client for interface
type Client struct {
CapsuleClient
StormClient
}
// NewClient new lottery 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.CapsuleClient = NewCapsuleClient(conn)
cli.StormClient = NewStormClient(conn)
return cli, nil
}

View File

@@ -0,0 +1,4 @@
#! /bin/sh
# 在环境变量中设置好$GOPATH即可食用
cd $GOPATH/src/go-common/app/service/live/xlottery/api/grpc/v1
$GOPATH/src/go-common/app/tool/warden/protoc.sh

View File

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

View File

@@ -0,0 +1,299 @@
##
`GET http://api.live.bilibili.com/xlive/lottery/v1/capsule/get_coin_list`
### 请求参数
|参数名|必选|类型|描述|
|:---|:---|:---|:---|
|page|否|integer||
|pageSize|否|integer||
```json
{
"code": 0,
"message": "ok",
"data": {
"total": 0,
"totalPage": 0,
"list": [
{
"id": 0,
"title": "",
"changeNum": 0,
"startTime": "",
"endTime": "",
"status": 0,
"giftType": 0,
"giftConfig": "",
"areaIds": [
{
"parentId": 0,
"isAll": 0,
"sonIds": [
0
]
}
]
}
]
}
}
```
##
`GET http://api.live.bilibili.com/xlive/lottery/v1/capsule/update_coin_config`
### 请求参数
|参数名|必选|类型|描述|
|:---|:---|:---|:---|
|id|否|integer||
|title|否|string||
|changeNum|否|integer||
|startTime|否|string||
|endTime|否|string||
|status|否|integer||
|giftType|否|integer||
|giftConfig|否|string||
|areaIds|否|多个unknown||
```json
{
"code": 0,
"message": "ok",
"data": {
"retStatus": true
}
}
```
##
`GET http://api.live.bilibili.com/xlive/lottery/v1/capsule/update_coin_status`
### 请求参数
|参数名|必选|类型|描述|
|:---|:---|:---|:---|
|id|否|integer||
|status|否|integer||
```json
{
"code": 0,
"message": "ok",
"data": {
"retStatus": true
}
}
```
##
`GET http://api.live.bilibili.com/xlive/lottery/v1/capsule/delete_coin`
### 请求参数
|参数名|必选|类型|描述|
|:---|:---|:---|:---|
|id|否|integer||
```json
{
"code": 0,
"message": "ok",
"data": {
"retStatus": true
}
}
```
##
`GET http://api.live.bilibili.com/xlive/lottery/v1/capsule/get_pool_config`
### 请求参数
|参数名|必选|类型|描述|
|:---|:---|:---|:---|
|page|否|integer||
|pageSize|否|integer||
```json
{
"code": 0,
"message": "ok",
"data": {
"total": 0,
"totalPage": 0,
"list": [
{
"id": 0,
"coinTitle": 0,
"title": "",
"start_time": "",
"endTime": "",
"status": 0,
"rule": ""
}
]
}
}
```
##
`GET http://api.live.bilibili.com/xlive/lottery/v1/capsule/update_pool_config`
### 请求参数
|参数名|必选|类型|描述|
|:---|:---|:---|:---|
|id|否|integer||
|coinTitle|否|integer||
|title|否|string||
|startTime|否|string||
|endTime|否|string||
|rule|否|string||
```json
{
"code": 0,
"message": "ok",
"data": {
"retStatus": true
}
}
```
##
`GET http://api.live.bilibili.com/xlive/lottery/v1/capsule/delete_pool`
### 请求参数
|参数名|必选|类型|描述|
|:---|:---|:---|:---|
|id|否|integer||
```json
{
"code": 0,
"message": "ok",
"data": {
"retStatus": true
}
}
```
##
`GET http://api.live.bilibili.com/xlive/lottery/v1/capsule/update_pool_status`
### 请求参数
|参数名|必选|类型|描述|
|:---|:---|:---|:---|
|id|否|integer||
|status|否|integer||
```json
{
"code": 0,
"message": "ok",
"data": {
"retStatus": true
}
}
```
##
`GET http://api.live.bilibili.com/xlive/lottery/v1/capsule/get_pool_detail`
### 请求参数
|参数名|必选|类型|描述|
|:---|:---|:---|:---|
|poolId|否|integer||
```json
{
"code": 0,
"message": "ok",
"data": {
"list": [
{
"ID": 0,
"poolID": 0,
"type": 0,
"num": 0,
"objectID": 0,
"webUrl": "",
"mobileUrl": "",
"description": "",
"jumpUrl": "",
"proType": 0,
"chance": "",
"loop": 0,
"limit": 0
}
]
}
}
```
##
`GET http://api.live.bilibili.com/xlive/lottery/v1/capsule/update_pool_detail`
### 请求参数
|参数名|必选|类型|描述|
|:---|:---|:---|:---|
|id|否|integer||
|poolId|否|integer||
|type|否|integer||
|num|否|integer||
|objectId|否|integer||
|webUrl|否|string||
|mobileUrl|否|string||
|description|否|string||
|jumpUrl|否|string||
|proType|否|integer||
|chance|否|string||
|loop|否|integer||
|limit|否|integer||
```json
{
"code": 0,
"message": "ok",
"data": {
"retStatus": true
}
}
```
##
`GET http://api.live.bilibili.com/xlive/lottery/v1/capsule/delete_pool_detail`
### 请求参数
|参数名|必选|类型|描述|
|:---|:---|:---|:---|
|id|否|integer||
```json
{
"code": 0,
"message": "ok",
"data": {
"retStatus": true
}
}
```

View File

@@ -0,0 +1,48 @@
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/xlottery/cmd",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/service/live/xlottery/conf:go_default_library",
"//app/service/live/xlottery/server/grpc:go_default_library",
"//app/service/live/xlottery/server/http:go_default_library",
"//app/service/live/xlottery/service:go_default_library",
"//library/ecode/tip:go_default_library",
"//library/log:go_default_library",
"//library/net/trace:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [
":package-srcs",
"//app/service/live/xlottery/cmd/client:all-srcs",
],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,39 @@
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/xlottery/cmd/client",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/service/live/xlottery/api/grpc/v1: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,41 @@
package main
import (
"context"
"fmt"
"go-common/app/service/live/xlottery/api/grpc/v1"
"go-common/library/net/rpc/warden"
)
//测试grpc
func main() {
// w := warden.NewClient(nil)
// conn, err := w.Dial(context.Background(), "localhost:9000")
// if err != nil {
// panic(err)
// }
// client := v1.NewCapsuleClient(conn)
// resp, err := client.UpdatePoolConfig(context.TODO(), &v1.UpdatePoolConfigReq{Id: 0, CoinTitle: 2, Title: "普通扭蛋币奖池2", StartTime: 1541751430, EndTime: 1541901430, Rule: "普通扭蛋币奖池描述2"})
// fmt.Println(resp)
stormTest()
}
func stormTest() {
w := warden.NewClient(nil)
conn, err := w.Dial(context.Background(), "172.22.32.242:9000")
if err != nil {
panic(err)
}
client := v1.NewStormClient(conn)
resp, err := client.Start(context.TODO(), &v1.StartStormReq{Roomid: 460820, Uid: 88888929, Ruid: 6810576, Num: 1, Beatid: 1, UseShield: true})
//resp, err := client.CanStart(context.TODO(), &v1.StartStormReq{Roomid: 460820, Uid: 88888929, Ruid: 6810576, Beatid: 1, UseShield: false})
//resp, err := client.Join(context.TODO(), &v1.JoinStormReq{Id: 14194626115, Roomid: 460820, Mid: 88888929, Platform: "ios"})
//resp, err := client.Check(context.TODO(), &v1.CheckStormReq{Roomid: 460820, Uid: 88888929})
//fmt.Println(resp, err)
fmt.Println(resp, err)
// client1 := v1.NewCapsuleClient(conn)
// resp2s, err := client1.DeleteCoin(context.TODO(), &v1.DeleteCoinReq{})
// fmt.Println(resp2s, err)
}

View File

@@ -0,0 +1,51 @@
package main
import (
"flag"
"os"
"os/signal"
"syscall"
"time"
"context"
"go-common/app/service/live/xlottery/conf"
"go-common/app/service/live/xlottery/server/grpc"
"go-common/app/service/live/xlottery/server/http"
"go-common/app/service/live/xlottery/service"
ecode "go-common/library/ecode/tip"
"go-common/library/log"
"go-common/library/net/trace"
)
func main() {
flag.Parse()
if err := conf.Init(); err != nil {
panic(err)
}
log.Init(conf.Conf.Log)
defer log.Close()
log.Info("lottery-service start")
trace.Init(conf.Conf.Tracer)
defer trace.Close()
ecode.Init(conf.Conf.Ecode)
svc := service.New(conf.Conf)
gsvr := grpc.Init(conf.Conf)
http.Init(conf.Conf, svc)
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:
gsvr.Shutdown(context.Background())
svc.Close()
log.Info("lottery-service exit")
time.Sleep(time.Second)
return
case syscall.SIGHUP:
default:
return
}
}
}

View File

@@ -0,0 +1,31 @@
[database]
[database.lottery]
addr = "172.16.38.117:3312"
dsn = "live:oWni@ElNs0P0C(dphdj*F1y4@tcp(172.16.38.117:3312)/live-app?timeout=5s&readTimeout=30s&writeTimeout=5s&parseTime=true&loc=Local&charset=utf8,utf8mb4"
active = 5
idle = 5
queryTimeout = "3s"
execTimeout = "3s"
tranTimeout = "3s"
[db.wallet.breaker]
window = "3s"
sleep = "100ms"
bucket = 10
ratio = 0.5
request = 100
[redis]
[redis.lottery]
name = "lottery-service"
proto = "tcp"
addr = "172.18.33.127:6379"
idle = 10
active = 10
dialTimeout = "1s"
readTimeout = "1s"
writeTimeout = "1s"
idleTimeout = "10s"
expire = "1m"

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 = ["conf.go"],
importpath = "go-common/app/service/live/xlottery/conf",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//library/cache/redis:go_default_library",
"//library/conf: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/liverpc: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,121 @@
package conf
import (
"errors"
"flag"
"go-common/library/queue/databus"
"go-common/library/net/rpc/liverpc"
"go-common/library/cache/redis"
"go-common/library/conf"
"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/trace"
"github.com/BurntSushi/toml"
)
var (
confPath string
client *conf.Client
// Conf config
Conf = &Config{}
)
// Config .
type Config struct {
Log *log.Config
BM *bm.ServerConfig
Verify *verify.Config
Tracer *trace.Config
Ecode *ecode.Config
Redis *Redis
Database *Database
LiveRpc map[string]*liverpc.ClientConfig
UserReport *databus.Config
RpcTimeout map[string]int64
HTTPClient *bm.ClientConfig
CouponConf *CouponConfig
}
// CouponConfig .
type CouponConfig struct {
Url string
Coupon map[string]string
}
// Database database
type Database struct {
Lottery *sql.Config
}
//Redis Redis config
type Redis struct {
Lottery *redis.Config
}
func init() {
flag.StringVar(&confPath, "conf", "", "default config path")
}
// 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
}
// GetTimeout implementation
// 获取超时
func GetTimeout(k string, def int64) (timeout int64) {
if t, ok := Conf.RpcTimeout[k]; ok {
timeout = t
} else {
timeout = def
}
return
}

View File

@@ -0,0 +1,76 @@
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",
"storm_test.go",
],
embed = [":go_default_library"],
tags = ["automanaged"],
deps = [
"//app/service/live/xlottery/model:go_default_library",
"//library/cache/redis:go_default_library",
"//library/container/pool:go_default_library",
"//library/database/sql:go_default_library",
"//library/time:go_default_library",
],
)
go_library(
name = "go_default_library",
srcs = [
"api.go",
"award.go",
"cache.go",
"capsule.go",
"capsule_extra.go",
"coin.go",
"coin_config.go",
"dao.go",
"pool.go",
"pool_prize.go",
"prize_extra.go",
"prize_log.go",
"storm.go",
],
importpath = "go-common/app/service/live/xlottery/dao",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/service/live/gift/api/liverpc:go_default_library",
"//app/service/live/rc/api/liverpc:go_default_library",
"//app/service/live/rc/api/liverpc/v1:go_default_library",
"//app/service/live/userext/api/liverpc:go_default_library",
"//app/service/live/xlottery/api/grpc/v1:go_default_library",
"//app/service/live/xlottery/conf:go_default_library",
"//app/service/live/xlottery/model:go_default_library",
"//app/service/main/account/rpc/client:go_default_library",
"//library/cache/redis:go_default_library",
"//library/database/sql:go_default_library",
"//library/log:go_default_library",
"//library/net/rpc/liverpc:go_default_library",
"//library/queue/databus/report: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,38 @@
package dao
import (
giftApi "go-common/app/service/live/gift/api/liverpc"
rcApi "go-common/app/service/live/rc/api/liverpc"
userExtApi "go-common/app/service/live/userext/api/liverpc"
"go-common/app/service/live/xlottery/conf"
account "go-common/app/service/main/account/rpc/client"
"go-common/library/net/rpc/liverpc"
)
// AccountApi liverpc user api
var AccountApi *account.Service3
// GiftApi liverpc gift api
var GiftApi *giftApi.Client
// RcApi rc api
var RcApi *rcApi.Client
// UserExtApi userext api
var UserExtApi *userExtApi.Client
// InitAPI init all service APIs
func InitAPI() {
AccountApi = account.New3(nil)
GiftApi = giftApi.New(getConf("gift"))
RcApi = rcApi.New(getConf("rc"))
UserExtApi = userExtApi.New(getConf("userext"))
}
func getConf(appName string) *liverpc.ClientConfig {
c := conf.Conf.LiveRpc
if c != nil {
return c[appName]
}
return nil
}

View File

@@ -0,0 +1,22 @@
package dao
import (
"context"
"time"
"go-common/library/log"
)
const (
_addAwardRecordMysql = "insert into ap_award_record(uid, gift_name, gift_type, gift_num, source, source_id, create_time, expire_time, status, user_extra_field) values(?,?,?,?,?,?,?,?,?,?)"
)
// AddAward .
func (d *Dao) AddAward(ctx context.Context, uid int64, expireTime string, giftType int64, giftName string, giftNum int64, source string, sourceId int64) bool {
sql := _addAwardRecordMysql
affect, err := d.execSqlWithBindParams(ctx, &sql, uid, giftName, giftType, giftNum, source, sourceId, time.Now().Format("2006-01-02 15:04:05"), expireTime, 0, "")
if err != nil {
log.Error("[dao.mysql_lottery | AddAward] uid(%d) gift_name(%s) gift_type(%s) source(%s) error(%v)", uid, giftName, giftType, source, err)
}
return affect > 0
}

View File

@@ -0,0 +1,169 @@
package dao
import (
"context"
"time"
stderr "errors"
"github.com/pkg/errors"
"go-common/library/cache/redis"
"go-common/library/log"
)
// Incr incr
func (d *Dao) Incr(context context.Context, name string) int {
conn := d.redis.Get(context)
defer conn.Close()
reply, _ := redis.Int(conn.Do("INCR", name))
return reply
}
// Lock .
func (d *Dao) Lock(ctx context.Context, realKey string, ttl int, retry int, retryDelay int) (gotLock bool, lockValue string, err error) {
if retry <= 0 {
retry = 1
}
lockValue = "locked:" + randomString(5)
retryTimes := 0
conn := d.redis.Get(ctx)
defer conn.Close()
for ; retryTimes < retry; retryTimes++ {
var res interface{}
res, err = conn.Do("SET", realKey, lockValue, "PX", ttl, "NX")
if err != nil {
log.Error("redis_lock failed:%s:%s", realKey, err.Error())
break
}
if res != nil {
gotLock = true
break
}
time.Sleep(time.Duration(retryDelay * 1000))
}
return
}
// UnLock .
func (d *Dao) UnLock(ctx context.Context, realKey string, lockValue string) (err error) {
conn := d.redis.Get(ctx)
defer conn.Close()
res, err := redis.String(conn.Do("GET", realKey))
if err != nil {
return
}
if res != lockValue {
err = ErrUnLockGet
return
}
_, err = conn.Do("DEL", realKey)
return
}
// Get get
func (d *Dao) Get(ctx context.Context, key string) (string, error) {
conn := d.redis.Get(ctx)
defer conn.Close()
return redis.String(conn.Do("GET", key))
}
// GetInt64 GetInt64
func (d *Dao) GetInt64(ctx context.Context, key string) (int64, error) {
conn := d.redis.Get(ctx)
defer conn.Close()
return redis.Int64(conn.Do("GET", key))
}
// Set Set
func (d *Dao) Set(ctx context.Context, key string, value interface{}) (bool, error) {
conn := d.redis.Get(ctx)
defer conn.Close()
if _, err := redis.String(conn.Do("SET", key, value)); err != nil {
return false, errors.WithStack(err)
}
return true, nil
}
// SetEx SetEx
func (d *Dao) SetEx(ctx context.Context, key string, value interface{}, ex int) (bool, error) {
conn := d.redis.Get(ctx)
defer conn.Close()
if _, err := redis.String(conn.Do("SET", key, value, "EX", ex)); err != nil {
return false, errors.WithStack(err)
}
return true, nil
}
//HMSet HMSet
func (d *Dao) HMSet(ctx context.Context, key string, value interface{}) (bool, error) {
conn := d.redis.Get(ctx)
defer conn.Close()
arg := redis.Args{}.Add(key).AddFlat(value)
if _, err := redis.String(conn.Do("HMSET", arg...)); err != nil {
return false, errors.WithStack(err)
}
return true, nil
}
//Del Del
func (d *Dao) Del(ctx context.Context, key string) (int64, error) {
conn := d.redis.Get(ctx)
defer conn.Close()
return redis.Int64(conn.Do("DEL", key))
}
//SIsMember SIsMember
func (d *Dao) SIsMember(ctx context.Context, key, value string) (bool, error) {
conn := d.redis.Get(ctx)
defer conn.Close()
return redis.Bool(conn.Do("SISMEMBER", key, value))
}
//SAdd SAdd
func (d *Dao) SAdd(ctx context.Context, key, value string) (int, error) {
conn := d.redis.Get(ctx)
defer conn.Close()
return redis.Int(conn.Do("SADD", key, value))
}
// Expire Expire
func (d *Dao) Expire(ctx context.Context, key string, ttl int) (bool, error) {
conn := d.redis.Get(ctx)
defer conn.Close()
return redis.Bool(conn.Do("EXPIRE", key, ttl))
}
//ErrEmptyMap ErrEmptyMap
var ErrEmptyMap = stderr.New("empty map")
// HGetAll HGetAll
func (d *Dao) HGetAll(ctx context.Context, key string) (map[string]string, error) {
conn := d.redis.Get(ctx)
defer conn.Close()
m, err := redis.StringMap(conn.Do("HGETALL", key))
if err != nil {
return nil, err
}
if len(m) <= 0 {
return nil, ErrEmptyMap
}
return m, nil
}
// SetWithNxEx SetWithNxEx
func (d *Dao) SetWithNxEx(ctx context.Context, key, value string, ex int64) (string, error) {
conn := d.redis.Get(ctx)
defer conn.Close()
return redis.String(conn.Do("SET", key, value, "EX", ex, "NX"))
}

View File

@@ -0,0 +1,91 @@
package dao
import (
"context"
"fmt"
"testing"
"time"
"go-common/library/cache/redis"
"go-common/library/container/pool"
xtime "go-common/library/time"
)
var p *redis.Pool
var config *redis.Config
var d *Dao
var ctx = context.TODO()
func init() {
config = getConfig()
p = redis.NewPool(config)
d = &Dao{redis: p}
}
func getConfig() (c *redis.Config) {
c = &redis.Config{
Name: "test",
Proto: "tcp",
Addr: "127.0.0.1:6379",
DialTimeout: xtime.Duration(time.Second),
ReadTimeout: xtime.Duration(time.Second),
WriteTimeout: xtime.Duration(time.Second),
}
c.Config = &pool.Config{
Active: 20,
Idle: 2,
IdleTimeout: xtime.Duration(90 * time.Second),
}
return
}
func TestDao_Get(t *testing.T) {
v, e := d.Get(ctx, "golang")
fmt.Println(v, e)
}
func TestDao_Set(t *testing.T) {
fmt.Println(d.Set(ctx, "golang", 22))
}
func TestDao_SetEx(t *testing.T) {
got, err := d.SetEx(ctx, "golang", "b", 10)
fmt.Println(got, err)
}
func TestDao_Del(t *testing.T) {
got, err := d.Del(ctx, "golang")
fmt.Println(got, err)
}
func TestDao_HMSet(t *testing.T) {
m := map[string]interface{}{"id": 1, "uid": 2, "content": "sss"}
got, err := d.HMSet(ctx, "myhash", m)
fmt.Println(got, err)
v, _ := d.HGetAll(ctx, "myhash")
fmt.Printf("%+v\n", v)
}
func TestDao_Expire(t *testing.T) {
got, err := d.Expire(ctx, "ttl", 10)
fmt.Println(got, err)
}
func TestDao_HGetAll(t *testing.T) {
got, err := d.HGetAll(ctx, "myhash")
if err != nil && err != ErrEmptyMap {
fmt.Println(err.Error())
}
fmt.Printf("%+v ,%+v", got, err)
}
func TestDao_SetWithNxEx(t *testing.T) {
fmt.Println(d.SetWithNxEx(ctx, "python", "1", 100))
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,80 @@
package dao
import (
"context"
"fmt"
"go-common/app/service/live/xlottery/model"
"strings"
"go-common/library/database/sql"
"go-common/library/log"
)
const (
_getExtraDataByTypeMysql = "select type, item_value, item_extra, ctime, mtime from capsule_extra_data where uid = ? and type in (%v)"
_addExtraDataMysql = "insert into capsule_extra_data(uid,type,item_value,item_extra) values "
_updateExtraValueMysql = "update capsule_extra_data set item_value = ? where uid = ? and type = ?"
)
// GetExtraDataByType 添加数据
func (d *Dao) GetExtraDataByType(ctx context.Context, uid int64, types []string) (extraData []*model.ExtraData, err error) {
var rows *sql.Rows
var typeString []string
for _, etype := range types {
typeString = append(typeString, "'"+etype+"'")
}
sqlStr := fmt.Sprintf(_getExtraDataByTypeMysql, strings.Join(typeString, ","))
if rows, err = d.db.Query(ctx, sqlStr, uid); err != nil {
log.Error("[dao.extra | GetExtraDataByType] query(%s) error (%v)", sqlStr, err)
return
}
defer rows.Close()
extraData = make([]*model.ExtraData, 0)
for rows.Next() {
p := &model.ExtraData{}
if err = rows.Scan(&p.Type, &p.ItemValue, &p.ItemExtra, &p.Ctime, &p.Mtime); err != nil {
log.Error("[dao.extra | GetExtraDataByType] scan error, err %v", err)
return
}
extraData = append(extraData, p)
}
return
}
// AddExtraData 添加数据
func (d *Dao) AddExtraData(ctx context.Context, extraData []*model.ExtraData) (status bool, err error) {
sqlStr := _addExtraDataMysql + strings.Repeat("(?,?,?,?),", len(extraData))
slen := len(sqlStr) - 1
sqlStr = sqlStr[0:slen]
values := make([]interface{}, len(extraData)*4)
for i, extra := range extraData {
values[i*4] = extra.Uid
values[i*4+1] = extra.Type
values[i*4+2] = extra.ItemValue
values[i*4+3] = extra.ItemExtra
}
res, err := d.db.Exec(ctx, sqlStr, values...)
if err != nil {
log.Error("[dao.extra | AddExtraData] add(%s) error (%v)", sqlStr, err)
return
}
rows, _ := res.RowsAffected()
status = rows > 0
return
}
// UpdateExtraValue 更新数据
func (d *Dao) UpdateExtraValue(ctx context.Context, uid int64, etype string, itemValue int64) (status bool, err error) {
res, err := d.db.Exec(ctx, _updateExtraValueMysql, itemValue, uid, etype)
if err != nil {
log.Error("[dao.extra | UpdateExtraValue] update(%s) error(%v)", _updateExtraValueMysql, err)
return false, err
}
var rows int64
rows, err = res.RowsAffected()
if err != nil {
log.Error("[dao.extra | UpdateExtraValue] err %v", err)
return false, err
}
return rows > 0, nil
}

View File

@@ -0,0 +1,130 @@
package dao
import (
"context"
v1pb "go-common/app/service/live/xlottery/api/grpc/v1"
"go-common/app/service/live/xlottery/model"
"go-common/library/database/sql"
"go-common/library/log"
)
const (
_getAllCoin = "SELECT id, title, gift_type, change_num, start_time, end_time, status FROM capsule_coin"
_updateStatus = "UPDATE capsule_coin SET status = ? WHERE id = ?"
_delCoin = "DELETE FROM capsule_coin WHERE id = ?"
_createCoin = "INSERT INTO capsule_coin (title, gift_type, change_num, start_time, end_time, status) VALUES (?,?,?,?,?,?)"
_updateCoin = "UPDATE capsule_coin SET title = ?, gift_type = ?, change_num = ?, start_time = ?, end_time = ?, status = ? WHERE id = ?"
_getOnCoin = "SELECT id, title, gift_type, change_num, start_time, end_time, status FROM capsule_coin WHERE status = 1"
)
//GetAllCoin 获取所有扭蛋币
func (d *Dao) GetAllCoin(ctx context.Context) (coins []*model.Coin, err error) {
var rows *sql.Rows
if rows, err = d.db.Query(ctx, _getAllCoin); err != nil {
log.Error("[dao.coin | GetALLCoin] query(%s) error (%v)", _getAllCoin, err)
return
}
defer rows.Close()
for rows.Next() {
p := &model.Coin{}
if err = rows.Scan(&p.Id, &p.Title, &p.GiftType, &p.ChangeNum, &p.StartTime, &p.EndTime, &p.Status); err != nil {
log.Error("[dao.coin | GetAllCoin] scan error, err %v", err)
return
}
coins = append(coins, p)
}
return
}
//GetCoinMap 批量获取扭蛋币
func (d *Dao) GetCoinMap(ctx context.Context) (coinMap map[int64]*model.Coin, err error) {
var rows *sql.Rows
if rows, err = d.db.Query(ctx, _getOnCoin); err != nil {
log.Error("[dao.coin | GetCoinMap] query(%s) error(%v)", _getOnCoin, err)
return
}
defer rows.Close()
coinMap = make(map[int64]*model.Coin)
for rows.Next() {
p := &model.Coin{}
if err = rows.Scan(&p.Id, &p.Title, &p.GiftType, &p.ChangeNum, &p.StartTime, &p.EndTime, &p.Status); err != nil {
log.Error("[dao.coin | GetCoinMap] scan error, err %v", err)
return
}
coinMap[p.Id] = p
}
return
}
//UpdateCoinStatus 更新扭蛋币状态
func (d *Dao) UpdateCoinStatus(ctx context.Context, id int64, status int64) (bool, error) {
res, err := d.db.Exec(ctx, _updateStatus, status, id)
if err != nil {
log.Error("[dao.coin | UpdateCStatus] query(%s) error(%v)", _updateStatus, err)
return false, err
}
var rows int64
rows, err = res.RowsAffected()
if err != nil {
log.Error("[dao.coin | UpdateCStatus] no coin is affected, err %v", err)
return false, err
}
return rows > 0, nil
}
//DeleteCoin 删除扭蛋币
func (d *Dao) DeleteCoin(ctx context.Context, id int64) (status bool, err error) {
res, err := d.db.Exec(ctx, _delCoin, id)
if err != nil {
log.Error("[dao.coin | DelCoin] query(%s), error(%v)", _delCoin, err)
return
}
var rows int64
rows, err = res.RowsAffected()
if err != nil {
log.Error("[dao.coin | DelCoin] no coin is deleted, err %v", err)
return
}
status = rows > 0
return
}
// CreateCoin 更新扭蛋币
func (d *Dao) CreateCoin(ctx context.Context, data *v1pb.UpdateCoinConfigReq) (ret int64, err error) {
title := data.Title
giftType := data.GiftType
changeNum := data.ChangeNum
startTime := data.StartTime
endTime := data.EndTime
status := 1
res, err := d.db.Exec(ctx, _createCoin, title, giftType, changeNum, startTime, endTime, status)
if err != nil {
log.Error("[dao.coin | CreateCoin] query(%s) error (%v)", _createCoin, err)
return
}
ret, err = res.RowsAffected()
if err != nil {
log.Error("[dao.coin | CreateCoin]no coin is added, err %v", err)
return
}
return
}
// UpdateCoin 更新扭蛋币
func (d *Dao) UpdateCoin(ctx context.Context, data *v1pb.UpdateCoinConfigReq) (status bool, err error) {
id := data.Id
title := data.Title
giftType := data.GiftType
changeNum := data.ChangeNum
startTime := data.StartTime
endTime := data.EndTime
_, err = d.db.Exec(ctx, _updateCoin, title, giftType, changeNum, startTime, endTime, 1, id)
if err != nil {
log.Error("[dao.coin | UpdateCoin] query(%s) error %v", _updateCoin, err)
return
}
return true, nil
}

View File

@@ -0,0 +1,113 @@
package dao
import (
"context"
"fmt"
"strconv"
"strings"
v1pb "go-common/app/service/live/xlottery/api/grpc/v1"
"go-common/app/service/live/xlottery/model"
"go-common/library/database/sql"
"go-common/library/log"
)
const (
_getCoinConfig = "SELECT coin_id, type, area_v2_parent_id, area_v2_id, gift_id FROM capsule_coin_config WHERE coin_id = ? AND status = 1 "
_getCoinConfigMap = "SELECT coin_id, type, area_v2_parent_id, area_v2_id, gift_id FROM capsule_coin_config WHERE coin_id in (%v) AND status = 1"
_updateCoinConfig = "UPDATE capsule_coin_config SET status = ? WHERE coin_id = ?"
_addCoinConfig = "INSERT INTO capsule_coin_config (type,gift_id,area_v2_parent_id,area_v2_id,coin_id,status) VALUES "
)
//GetCoinConfig 获取扭蛋币配置
func (d *Dao) GetCoinConfig(ctx context.Context, coinId int64) (configs []*model.CoinConfig, err error) {
var rows *sql.Rows
if rows, err = d.db.Query(ctx, _getCoinConfig, coinId); err != nil {
log.Error("[dao.coin_config | GetCoinConfig]query(%s) error(%v)", _getCoinConfig, err)
return
}
defer rows.Close()
configs = make([]*model.CoinConfig, 0)
for rows.Next() {
d := &model.CoinConfig{}
if err = rows.Scan(&d.CoinId, &d.Type, &d.AreaV2ParentId, &d.AreaV2Id, &d.GiftId); err != nil {
log.Error("[dao.coin_config | GetCoinConfig] scan error, err %v", err)
return
}
configs = append(configs, d)
}
return
}
//GetCoinConfigMap 批量获取扭蛋币
func (d *Dao) GetCoinConfigMap(ctx context.Context, coinIds []int64) (configMap map[int64][]*model.CoinConfig, err error) {
var rows *sql.Rows
stringCoinIds := make([]string, 0)
for _, coinId := range coinIds {
stringCoinIds = append(stringCoinIds, strconv.FormatInt(coinId, 10))
}
coinString := strings.Join(stringCoinIds, ",")
if rows, err = d.db.Query(ctx, fmt.Sprintf(_getCoinConfigMap, coinString)); err != nil {
log.Error("[dao.coin_config | GetCoinConfigMap]query(%s) error(%v)", _getCoinConfigMap, err)
return
}
defer rows.Close()
configMap = make(map[int64][]*model.CoinConfig)
for rows.Next() {
d := &model.CoinConfig{}
if err = rows.Scan(&d.CoinId, &d.Type, &d.AreaV2ParentId, &d.AreaV2Id, &d.GiftId); err != nil {
log.Error("[dao.coin_config | GetCoinConfigMap] scan error, err %v", err)
return
}
if _, ok := configMap[d.CoinId]; !ok {
configMap[d.CoinId] = make([]*model.CoinConfig, 0)
}
configMap[d.CoinId] = append(configMap[d.CoinId], d)
}
return
}
//UpdateCoinConfig 更新扭蛋币配置
func (d *Dao) UpdateCoinConfig(ctx context.Context, coinId int64, areaIds []*v1pb.UpdateCoinConfigReq_AreaIds, giftIds []int64) (status bool, err error) {
_, err = d.db.Exec(ctx, _updateCoinConfig, 2, coinId)
if err != nil {
log.Error("[dao.coin_config | UpdateCoinConfig] query(%s) error(%v)", _updateCoinConfig, err)
return
}
rowFields := make([][6]interface{}, 0)
for _, giftId := range giftIds {
rowFields = append(rowFields, [6]interface{}{2, giftId, 0, 0, coinId, 1})
}
if len(areaIds) != 0 {
for _, areaId := range areaIds {
if areaId.IsAll == 1 {
rowFields = append(rowFields, [6]interface{}{1, 0, areaId.ParentId, areaId.ParentId, coinId, 1})
} else {
for _, areaV2Id := range areaId.List {
rowFields = append(rowFields, [6]interface{}{1, 0, areaId.ParentId, areaV2Id, coinId, 1})
}
}
}
}
sqlStr := _addCoinConfig + strings.Repeat("(?,?,?,?,?,?),", len(rowFields))
slen := len(sqlStr) - 1
sqlStr = sqlStr[0:slen]
values := make([]interface{}, len(rowFields)*6)
for i, field := range rowFields {
for j, v := range field {
ix := i*6 + j
values[ix] = v
}
}
res, err := d.db.Exec(ctx, sqlStr, values...)
if err != nil {
log.Error("[dao.coin_config | UpdateCoinConfig] insert(%s) error(%v)", sqlStr, err)
return
}
rows, _ := res.RowsAffected()
status = rows > 0
return
}

View File

@@ -0,0 +1,63 @@
package dao
import (
"context"
"time"
"math/rand"
"go-common/app/service/live/xlottery/conf"
"go-common/library/cache/redis"
xsql "go-common/library/database/sql"
"go-common/library/log"
)
// Dao dao
type Dao struct {
c *conf.Config
//mc *memcache.Pool
redis *redis.Pool
db *xsql.DB
}
// New init mysql db
func New(c *conf.Config) (dao *Dao) {
dao = &Dao{
c: c,
redis: redis.NewPool(c.Redis.Lottery),
db: xsql.NewMySQL(c.Database.Lottery),
}
return
}
// Close close the resource.
func (d *Dao) Close() {
d.redis.Close()
d.db.Close()
}
// Ping dao ping
func (d *Dao) Ping(c context.Context) error {
return d.db.Ping(c)
}
// 通过提供的sql和bind来更新没有实际业务意义只是为了少写重复代码
func (d *Dao) execSqlWithBindParams(c context.Context, sql *string, bindParams ...interface{}) (affect int64, err error) {
res, err := d.db.Exec(c, *sql, bindParams...)
if err != nil {
log.Error("db.Exec(%s) error(%v)", *sql, err)
return
}
return res.RowsAffected()
}
func randomString(l int) string {
str := "0123456789abcdefghijklmnopqrstuvwxyz"
bytes := []byte(str)
result := []byte{}
r := rand.New(rand.NewSource(time.Now().UnixNano()))
for i := 0; i < l; i++ {
result = append(result, bytes[r.Intn(len(bytes))])
}
return string(result)
}

View File

@@ -0,0 +1,169 @@
package dao
import (
"context"
"fmt"
"strconv"
"strings"
v1pb "go-common/app/service/live/xlottery/api/grpc/v1"
"go-common/app/service/live/xlottery/model"
"go-common/library/database/sql"
"go-common/library/log"
)
const (
_getPool = "SELECT id, coin_id, title, rule, start_time, end_time, status, is_bottom FROM capsule_pool"
_getOnePool = "SELECT id, coin_id,title,rule,start_time,end_time,status, is_bottom FROM capsule_pool WHERE id = ?"
_updatePStatus = "UPDATE capsule_pool SET status = ? WHERE id = ?"
_delPool = "DELETE FROM capsule_pool WHERE id = ?"
_createPool = "INSERT INTO capsule_pool (coin_id,title,rule,start_time,end_time,status) VALUES (?,?,?,?,?,0)"
_updatePool = "UPDATE capsule_pool SET coin_id = ?, title = ?, rule = ?, start_time = ?, end_time = ? WHERE id = ?"
_getPoolMap = "SELECT id, coin_id, title, rule, start_time, end_time, status, is_bottom FROM capsule_pool WHERE status = 1 and coin_id in (%v)"
)
//GetAllPool 获取奖池信息
func (d *Dao) GetAllPool(ctx context.Context) (pools []*model.Pool, err error) {
var rows *sql.Rows
if rows, err = d.db.Query(ctx, _getPool); err != nil {
log.Error("[dao.pool | GetAllPool] query(%s) error(%v)", _getPool, err)
return
}
defer rows.Close()
for rows.Next() {
p := &model.Pool{}
if err = rows.Scan(&p.Id, &p.CoinId, &p.Title, &p.Description, &p.StartTime, &p.EndTime, &p.Status, &p.IsBottom); err != nil {
log.Error("[dao.pool | GetAllPool] scan error, err %v", err)
return
}
pools = append(pools, p)
}
return
}
//GetPoolMap 批量奖池信息
func (d *Dao) GetPoolMap(ctx context.Context, coinIds []int64) (poolMap map[int64][]*model.Pool, err error) {
var rows *sql.Rows
stringCoinIds := make([]string, 0)
for _, coinId := range coinIds {
stringCoinIds = append(stringCoinIds, strconv.FormatInt(coinId, 10))
}
coinString := strings.Join(stringCoinIds, ",")
if rows, err = d.db.Query(ctx, fmt.Sprintf(_getPoolMap, coinString)); err != nil {
log.Error("[dao.pool | GetPoolMap]query(%s) error(%v)", _getPoolMap, err)
return
}
defer rows.Close()
poolMap = make(map[int64][]*model.Pool)
for rows.Next() {
d := &model.Pool{}
if err = rows.Scan(&d.Id, &d.CoinId, &d.Title, &d.Description, &d.StartTime, &d.EndTime, &d.Status, &d.IsBottom); err != nil {
log.Error("[dao.pool |GetPoolMap] scan error, err %v", err)
return
}
if _, ok := poolMap[d.CoinId]; !ok {
poolMap[d.CoinId] = make([]*model.Pool, 0)
}
poolMap[d.CoinId] = append(poolMap[d.CoinId], d)
}
return
}
// GetPoolById 通过ID获取奖池信息
func (d *Dao) GetPoolById(ctx context.Context, poolID int64) (pool *model.Pool, err error) {
var rows *sql.Rows
if rows, err = d.db.Query(ctx, _getOnePool, poolID); err != nil {
log.Error("[dao.pool | GetPoolById]query(%s) error(%v)", _getOnePool, err)
return
}
defer rows.Close()
for rows.Next() {
p := &model.Pool{}
if err = rows.Scan(&p.Id, &p.CoinId, &p.Title, &p.Description, &p.StartTime, &p.EndTime, &p.Status, &p.IsBottom); err != nil {
log.Error("[dao.pool | GetPoolById] scan error, err %v", err)
return
}
pool = p
}
return
}
//UpdatePoolStatus 更新奖池状态
func (d *Dao) UpdatePoolStatus(ctx context.Context, id int64, status int64) (bool, error) {
//s := fmt.Sprintf(_updatePStatus, status, id)
res, err := d.db.Exec(ctx, _updatePStatus, status, id)
if err != nil {
log.Error("[dao.pool | UpdatePoolStatus] query(%s) error(%v)", _updatePStatus, err)
return false, err
}
var rows int64
rows, err = res.RowsAffected()
if err != nil {
log.Error("[dao.pool | UpdatePoolStatus] no coin is affected, err %v", err)
return false, err
}
return rows > 0, nil
}
//DelPool 删除奖池
func (d *Dao) DelPool(ctx context.Context, id int64) (rows int64, err error) {
res, err := d.db.Exec(ctx, _delPool, id)
if err != nil {
log.Error("[dao.pool | DelPool]query(%s) error(%v)", _delPool, err)
return
}
rows, err = res.RowsAffected()
if err != nil {
log.Error("[dao.pool | DelPool]no pool is deleted, err %v", err)
return
}
return
}
//CreatePool 新建奖池
func (d *Dao) CreatePool(ctx context.Context, data *v1pb.UpdatePoolReq) (status bool, err error) {
coinId := data.CoinId
title := data.Title
startTime := data.StartTime
endTime := data.EndTime
rule := data.Rule
//s := fmt.Sprintf(_createPool,coinTitle,title,rule,startTime,endTime)
res, err := d.db.Exec(ctx, _createPool, coinId, title, rule, startTime, endTime)
if err != nil {
log.Error("[dao.pool | CreatePool] query(%s) error(%v)", _createPool, err)
return
}
var rows int64
rows, err = res.RowsAffected()
if err != nil {
log.Error("[dao.pool | CreatePool] no pool is created, err %v", err)
return
}
status = rows > 0
return
}
//UpdatePool 更新奖池
func (d *Dao) UpdatePool(ctx context.Context, data *v1pb.UpdatePoolReq) (status bool, err error) {
id := data.Id
coinId := data.CoinId
title := data.Title
startTime := data.StartTime
endTime := data.EndTime
rule := data.Rule
res, err := d.db.Exec(ctx, _updatePool, coinId, title, rule, startTime, endTime, id)
if err != nil {
log.Error("[dao.pool | UpdatePool] query(%s) error(%v)", _updatePool, err)
return
}
var rows int64
rows, err = res.RowsAffected()
if err != nil {
log.Error("[dao.pool | UpdatePool] no pool is updated, err %v", err)
return
}
status = rows > 0
return
}

View File

@@ -0,0 +1,116 @@
package dao
import (
"context"
"fmt"
"strconv"
"strings"
v1pb "go-common/app/service/live/xlottery/api/grpc/v1"
"go-common/app/service/live/xlottery/model"
"go-common/library/database/sql"
"go-common/library/log"
)
const (
_getPoolPrize = "SELECT id, pool_id, type, num, object_id,expire, web_url, mobile_url, description, jump_url, pro_type, chance, loop_num, limit_num, weight FROM capsule_pool_prize WHERE pool_id = ? and status = 1 order by ctime"
_delPoolPrize = "UPDATE capsule_pool_prize set status = 0 WHERE id = ?"
_addPoolPrize = "INSERT INTO capsule_pool_prize(pool_id,type,num,object_id,expire,web_url,mobile_url,description,jump_url,pro_type,chance,loop_num,limit_num,weight,status) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)"
_updatePoolPrize = "UPDATE capsule_pool_prize SET pool_id=?, type=?, num=?, object_id=?,expire=?, web_url=?, mobile_url=?, description=?, jump_url=?, pro_type=?, chance=?, loop_num=?, limit_num=?,weight=? WHERE id = ?"
_getPoolPrizeMap = "SELECT id, pool_id, type, num, object_id,expire, web_url, mobile_url, description, jump_url, pro_type, chance, loop_num, limit_num, weight FROM capsule_pool_prize WHERE pool_id in (%s) and status = 1 order by ctime"
)
// GetPoolPrize 获取奖池奖品
func (d *Dao) GetPoolPrize(ctx context.Context, poolID int64) (poolPrize []*model.PoolPrize, err error) {
var rows *sql.Rows
if rows, err = d.db.Query(ctx, _getPoolPrize, poolID); err != nil {
log.Error("[dao.pool_prize | GetPoolPrize]query(%s) error(%v)", _getPoolPrize, err)
return
}
defer rows.Close()
poolPrize = make([]*model.PoolPrize, 0)
for rows.Next() {
p := &model.PoolPrize{}
if err = rows.Scan(&p.Id, &p.PoolId, &p.Type, &p.Num, &p.ObjectId, &p.Expire, &p.WebUrl, &p.MobileUrl, &p.Description, &p.JumpUrl, &p.ProType, &p.Chance, &p.LoopNum, &p.LimitNum, &p.Weight); err != nil {
log.Error("[dao.pool_prize | GetPoolPrize] scan error, err %v", err)
return
}
poolPrize = append(poolPrize, p)
}
return
}
// GetPoolPrizeMap 批量奖池奖品
func (d *Dao) GetPoolPrizeMap(ctx context.Context, poolIds []int64) (poolPrizeMap map[int64][]*model.PoolPrize, err error) {
var rows *sql.Rows
stringPoolIds := make([]string, 0)
for _, poolId := range poolIds {
stringPoolIds = append(stringPoolIds, strconv.FormatInt(poolId, 10))
}
poolString := strings.Join(stringPoolIds, ",")
if rows, err = d.db.Query(ctx, fmt.Sprintf(_getPoolPrizeMap, poolString)); err != nil {
log.Error("[dao.pool_prize | GetPoolPrizeMap] query(%s) error(%v)", _getPoolPrizeMap, err)
return
}
defer rows.Close()
poolPrizeMap = make(map[int64][]*model.PoolPrize)
for rows.Next() {
d := &model.PoolPrize{}
if err = rows.Scan(&d.Id, &d.PoolId, &d.Type, &d.Num, &d.ObjectId, &d.Expire, &d.WebUrl, &d.MobileUrl, &d.Description, &d.JumpUrl, &d.ProType, &d.Chance, &d.LoopNum, &d.LimitNum, &d.Weight); err != nil {
log.Error("[dao.pool_prize | GetPoolPrizeMap] scan error, err %v", err)
return
}
if _, ok := PrizeNameMap[d.Type]; !ok {
continue
}
if _, ok := poolPrizeMap[d.PoolId]; !ok {
poolPrizeMap[d.PoolId] = make([]*model.PoolPrize, 0)
}
poolPrizeMap[d.PoolId] = append(poolPrizeMap[d.PoolId], d)
}
return
}
// DeletePoolPrize 删除奖池奖品
func (d *Dao) DeletePoolPrize(ctx context.Context, id int64) (status bool, err error) {
res, err := d.db.Exec(ctx, _delPoolPrize, id)
if err != nil {
log.Error("[dao.pool_prize | DelPoolDetail] query(%s) error (%v)", _delPoolPrize, err)
return
}
var rows int64
rows, err = res.RowsAffected()
if err != nil {
log.Error("[dao.pool_prize | DelPoolDetail] no poolDetail is deleted, err %v", err)
return
}
status = rows > 0
return
}
// AddPoolPrize 创建奖池奖品
func (d *Dao) AddPoolPrize(ctx context.Context, data *v1pb.UpdatePoolPrizeReq) (prizeId int64, err error) {
res, err := d.db.Exec(ctx, _addPoolPrize, data.PoolId, data.Type, data.Num, data.ObjectId, data.Expire, data.WebUrl, data.MobileUrl, data.Description, data.JumpUrl, data.ProType, data.Chance, data.Loop, data.Limit, data.Weight, 1)
if err != nil {
log.Error("[dao.pool_prize | CreatePoolPrize] query(%s) error (%v)", _addPoolPrize, err)
return
}
prizeId, err = res.LastInsertId()
if err != nil {
log.Error("[dao.pool_prize | CreatePoolPrize] insertLastId error(%v)", err)
return
}
return
}
// UpdatePoolPrize 更新奖池奖品
func (d *Dao) UpdatePoolPrize(ctx context.Context, data *v1pb.UpdatePoolPrizeReq) (status bool, err error) {
_, err = d.db.Exec(ctx, _updatePoolPrize, data.PoolId, data.Type, data.Num, data.ObjectId, data.Expire, data.WebUrl, data.MobileUrl, data.Description, data.JumpUrl, data.ProType, data.Chance, data.Loop, data.Limit, data.Weight, data.Id)
if err != nil {
log.Error("[dao.pool_prize | UpdatePoolPrize] query(%s) error(%v)", _updatePoolPrize, err)
return
}
return true, nil
}

View File

@@ -0,0 +1,78 @@
package dao
import (
"context"
"fmt"
"strconv"
"strings"
"go-common/library/database/sql"
"go-common/library/log"
)
const (
_getWhiteList = "select prize_id, item_id from capsule_prize_extra WHERE prize_id in (%v) and type = ? and status = 1 order by id"
_updateWhiteList = "UPDATE capsule_prize_extra SET status = ? WHERE prize_id = ?"
_addWhiteUser = "INSERT INTO capsule_prize_extra (prize_id,type,item_id,status) VALUES "
_whiteType = 1
)
// GetWhiteUserMap 获取白名单用户
func (d *Dao) GetWhiteUserMap(ctx context.Context, prizeIds []int64) (userMap map[int64][]int64, err error) {
var rows *sql.Rows
stringPrizeIds := make([]string, len(prizeIds))
for ix, prizeId := range prizeIds {
stringPrizeIds[ix] = strconv.FormatInt(prizeId, 10)
}
sqlStr := fmt.Sprintf(_getWhiteList, strings.Join(stringPrizeIds, ","))
if rows, err = d.db.Query(ctx, sqlStr, _whiteType); err != nil {
log.Error("[dao.pool_prize | GetPoolPrize]query(%s) error(%v)", _getWhiteList, err)
return
}
defer rows.Close()
userMap = make(map[int64][]int64)
for rows.Next() {
var uid, prizeId int64
if err = rows.Scan(&prizeId, &uid); err != nil {
log.Error("[dao.pool_prize | GetWhiteUser] scan error, err %v", err)
return
}
if _, ok := userMap[prizeId]; !ok {
userMap[prizeId] = make([]int64, 0)
}
userMap[prizeId] = append(userMap[prizeId], uid)
}
return
}
// AddWhiteUser 添加白名单用户
func (d *Dao) AddWhiteUser(ctx context.Context, prizeId int64, uids []int64) (status bool, err error) {
_, err = d.db.Exec(ctx, _updateWhiteList, 0, prizeId)
if err != nil {
log.Error("[dao.pool_prize | AddWhiteUids] update(%s) error(%v)", _updateWhiteList, err)
return
}
rowFields := make([][4]interface{}, 0)
for _, uid := range uids {
rowFields = append(rowFields, [4]interface{}{prizeId, _whiteType, uid, 1})
}
sqlStr := _addWhiteUser + strings.Repeat("(?,?,?,?),", len(rowFields))
slen := len(sqlStr) - 1
sqlStr = sqlStr[0:slen]
values := make([]interface{}, len(rowFields)*4)
for i, field := range rowFields {
for j, v := range field {
ix := i*4 + j
values[ix] = v
}
}
res, err := d.db.Exec(ctx, sqlStr, values...)
if err != nil {
log.Error("[dao.prize_extra | AddWhiteUser] add(%s) error (%v)", sqlStr, err)
return
}
rows, _ := res.RowsAffected()
status = rows > 0
return
}

View File

@@ -0,0 +1,56 @@
package dao
import (
"context"
"go-common/app/service/live/xlottery/model"
"go-common/library/database/sql"
"go-common/library/log"
)
const (
_addPrizeData = "INSERT INTO capsule_prize_log (prize_id,uid,day,timestamp) VALUES (?,?,?,?)"
_getUserPrizeData = "SELECT prize_id,uid,day,`timestamp` from capsule_prize_log where prize_id = ? and uid = ? order by id desc"
_getPrizeDayData = "SELECT prize_id,uid,day,`timestamp` from capsule_prize_log where prize_id = ? and day = ? order by id desc"
)
// AddPrizeData 添加特殊奖品获奖记录
func (d *Dao) AddPrizeData(ctx context.Context, prizeId, uid int64, day string, timestamp int64) (status bool, err error) {
res, err := d.db.Exec(ctx, _addPrizeData, prizeId, uid, day, timestamp)
if err != nil {
log.Error("[dao.prize_extra | AddWhiteUser] add(%s) error (%v)", _addPrizeData, err)
return
}
rows, _ := res.RowsAffected()
status = rows > 0
return
}
// GetUserPrizeLog 获取特殊奖品记录
func (d *Dao) GetUserPrizeLog(ctx context.Context, prizeId int64, uid int64) (prizeLog *model.PrizeLog, err error) {
row := d.db.QueryRow(ctx, _getUserPrizeData, prizeId, uid)
prizeLog = &model.PrizeLog{}
err = row.Scan(&prizeLog.PrizeId, &prizeLog.Uid, &prizeLog.Day, &prizeLog.Timestamp)
if err == sql.ErrNoRows {
return nil, nil
}
if err != nil {
log.Error("[dao.pool_prize | GetUserPrizeLog]query(%s) error(%v)", _getUserPrizeData, err)
return
}
return prizeLog, nil
}
// GetPrizeDayLog 获取特殊奖品记录
func (d *Dao) GetPrizeDayLog(ctx context.Context, prizeId int64, day string) (prizeLog *model.PrizeLog, err error) {
row := d.db.QueryRow(ctx, _getPrizeDayData, prizeId, day)
prizeLog = &model.PrizeLog{}
err = row.Scan(&prizeLog.PrizeId, &prizeLog.Uid, &prizeLog.Day, &prizeLog.Timestamp)
if err == sql.ErrNoRows {
return nil, nil
}
if err != nil {
log.Error("[dao.pool_prize | GetPrizeDayLog]query(%s) error(%v)", _getPrizeDayData, err)
return
}
return
}

View File

@@ -0,0 +1,54 @@
package dao
import (
"context"
"go-common/app/service/live/xlottery/model"
"github.com/pkg/errors"
)
// InsertSpecialGift 插入SpecialGift
func (d *Dao) InsertSpecialGift(sg *model.SpecialGift) (int64, error) {
stmt, err := d.db.Prepare("insert into ap_special_gift (uid,room_id,gift_id,gift_num,create_time, custom_field) values (?,?,?,?,?,?) ")
if err != nil {
return 0, errors.WithStack(err)
}
result, err := stmt.Exec(context.TODO(), sg.UID, sg.RoomID, sg.GiftID, sg.GiftNum, sg.CreateTime, sg.CustomField)
if err != nil {
return 0, errors.WithStack(err)
}
return result.LastInsertId()
}
// FindBeatByBeatIDAndUID 根据beatid 和 uid 查询 beat
func (d *Dao) FindBeatByBeatIDAndUID(id, uid int64) (*model.Beat, error) {
row := d.db.QueryRow(context.TODO(), "select id ,uid ,content,status, operator,update_time,ctime ,mtime from ap_user_beats_info where id = ? and uid = ?", id, uid)
var b model.Beat
err := row.Scan(&b.ID, &b.UID, &b.Content, &b.Status, &b.Operator, &b.UpdateTime, &b.Ctime, &b.Mtime)
if err != nil {
return nil, errors.WithStack(err)
}
return &b, nil
}
// FindShieldKeyWorkByUID 根据uid 查找屏蔽词
func (d *Dao) FindShieldKeyWorkByUID(uid int64) ([]*model.ShieldKeyWork, error) {
row, err := d.db.Query(context.TODO(), "select id, uid ,original_keyword, keyword, ctime from ap_shield_keywork where uid = ?", uid)
if err != nil {
return nil, errors.WithStack(err)
}
skArray := make([]*model.ShieldKeyWork, 0)
for row.Next() {
var b model.ShieldKeyWork
err := row.Scan(&b.ID, &b.UID, &b.OriginalKeyword, &b.KeyWord, &b.Ctime)
if err != nil {
return nil, errors.WithStack(err)
}
skArray = append(skArray, &b)
}
return skArray, nil
}

View File

@@ -0,0 +1,58 @@
package dao
import (
"fmt"
"testing"
"time"
"go-common/app/service/live/xlottery/model"
xsql "go-common/library/database/sql"
xtime "go-common/library/time"
)
var db *xsql.DB
func init() {
c := &xsql.Config{
Addr: "172.16.38.117:3312",
DSN: "live:oWni@ElNs0P0C(dphdj*F1y4@tcp(172.16.38.117:3312)/live-app?timeout=2000ms&readTimeout=2000ms&writeTimeout=2000ms&parseTime=true&loc=Local&charset=utf8,utf8mb4",
Active: 10,
Idle: 5,
IdleTimeout: xtime.Duration(time.Minute),
QueryTimeout: xtime.Duration(time.Minute),
ExecTimeout: xtime.Duration(time.Minute),
TranTimeout: xtime.Duration(time.Minute),
}
db = xsql.NewMySQL(c)
}
func TestDao_InsertSpecialGift(t *testing.T) {
d := &Dao{
db: db,
}
got, err := d.InsertSpecialGift(&model.SpecialGift{
UID: 333,
RoomID: 33,
GiftID: 33,
GiftNum: 1,
CreateTime: time.Now(),
CustomField: "{\"content\":\"真是无法无天了。\" }",
})
fmt.Println(got, err)
}
func TestDao_FindBeatByBeatIDAndUID(t *testing.T) {
d := &Dao{
db: db,
}
got, err := d.FindBeatByBeatIDAndUID(123, 10666892)
fmt.Println(got, err)
}
func TestDao_FindShieldKeyWorkByUID(t *testing.T) {
d := &Dao{
db: db,
}
got, err := d.FindShieldKeyWorkByUID(12209)
fmt.Println(got, err)
}

View File

@@ -0,0 +1,33 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = [
"beats.go",
"model.go",
"shield_keywork.go",
"special_gift.go",
],
importpath = "go-common/app/service/live/xlottery/model",
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,15 @@
package model
import "time"
// Beat ap_user_beats_info
type Beat struct {
ID int64 `json:"id"`
UID int64 `json:"uid"`
Content string `json:"content"`
Status int `json:"status"`
Operator string `json:"operator"`
UpdateTime time.Time `json:"update_time"`
Ctime time.Time `json:"ctime"`
Mtime time.Time `json:"mtime"`
}

View File

@@ -0,0 +1,85 @@
package model
// Coin .
type Coin struct {
Id int64 `json:"id"`
Title string `json:"title"`
GiftType int64 `json:"gift_type"`
ChangeNum int64 `json:"change_num"`
StartTime int64 `json:"start_time"`
EndTime int64 `json:"end_time"`
Status int64 `json:"status"`
}
// CoinConfig .
type CoinConfig struct {
CoinId int64 `json:"coin_id"`
Type int64 `json:"type"`
AreaV2ParentId int64 `json:"area_v2_parent_id"`
AreaV2Id int64 `json:"area_v2_id"`
GiftId int64 `json:"gift_id"`
IsAll int64 `json:"is_all"`
}
// Pool .
type Pool struct {
Id int64 `json:"id"`
CoinId int64 `json:"coin_id"`
Title string `json:"title"`
Description string `json:"description"`
StartTime int64 `json:"start_time"`
EndTime int64 `json:"end_time"`
Status int64 `json:"status"`
IsBottom int64 `json:"is_bottom"`
}
// PoolPrize .
type PoolPrize struct {
Id int64 `json:"id"`
PoolId int64 `json:"pool_id"`
Type int64 `json:"type"`
Num int64 `json:"num"`
ObjectId int64 `json:"object_id"`
Expire int64 `json:"expire"`
WebUrl string `json:"web_url"`
MobileUrl string `json:"mobile_url"`
Description string `json:"description"`
JumpUrl string `json:"jump_url"`
ProType int64 `json:"pro_type"`
Chance int64 `json:"chance"`
LoopNum int64 `json:"loop_num"`
LimitNum int64 `json:"limit_num"`
Weight int64 `json:"Weight"`
}
// PoolChance .
type PoolChance struct {
PrizeId int64 `json:"prize_id"`
Type int64 `json:"type"`
Chance string `json:"chance"`
Loop int64 `json:"loop"`
Limit int64 `json:"limit"`
}
// PrizeLog .
type PrizeLog struct {
PrizeId int64 `json:"prize_id"`
Uid int64 `json:"Uid"`
Day string `json:"Day"`
Timestamp int64 `json:"Timestamp"`
}
// ExtraData .
type ExtraData struct {
Uid int64 `json:"uid"`
Type string `json:"type"`
ItemValue int64 `json:"item_value"`
ItemExtra string `json:"item_extra"`
Ctime string `json:"ctime"`
Mtime string `json:"mtime"`
}
// ObjIds .
type ObjIds struct {
ObjectId int64 `json:"object_id"`
}

View File

@@ -0,0 +1,12 @@
package model
import "time"
// ShieldKeyWork ShieldKeyWorks
type ShieldKeyWork struct {
ID int64 `json:"id"`
UID int64 `json:"uid"`
OriginalKeyword string `json:"origin_keyword"`
KeyWord string `json:"keyword"`
Ctime time.Time `json:"ctime"`
}

View File

@@ -0,0 +1,15 @@
package model
import "time"
// SpecialGift SpecialGift
type SpecialGift struct {
ID int64 `json:"id"`
UID int64 `json:"uid"`
GiftID int64 `json:"gift_id"`
GiftNum int64 `json:"gift_num"`
RoomID int64 `json:"room_id"`
CreateTime time.Time `json:"create_time"`
CustomField string `json:"custom_fields"`
Status int `json:"status"`
}

View File

@@ -0,0 +1,36 @@
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/xlottery/server/grpc",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/service/live/xlottery/api/grpc/v1:go_default_library",
"//app/service/live/xlottery/conf:go_default_library",
"//app/service/live/xlottery/dao:go_default_library",
"//app/service/live/xlottery/service/v1:go_default_library",
"//library/log: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,29 @@
package grpc
import (
"fmt"
"go-common/app/service/live/xlottery/dao"
pb "go-common/app/service/live/xlottery/api/grpc/v1"
"go-common/app/service/live/xlottery/conf"
svc "go-common/app/service/live/xlottery/service/v1"
"go-common/library/log"
"go-common/library/net/rpc/warden"
)
// Init .
func Init(c *conf.Config) *warden.Server {
dao.InitAPI()
s := warden.NewServer(nil) // 酌情传入config
gs := svc.NewCapsuleService(c)
pb.RegisterCapsuleServer(s.Server(), gs)
pb.RegisterStormServer(s.Server(), svc.NewStromService(c))
_, err := s.Start()
if err != nil {
log.Error("grpc Start error(%v)", err)
panic(err)
}
fmt.Println("start")
return s
}

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/xlottery/server/http",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/service/live/xlottery/conf:go_default_library",
"//app/service/live/xlottery/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,54 @@
package http
import (
"net/http"
"go-common/app/service/live/xlottery/conf"
"go-common/app/service/live/xlottery/service"
"go-common/library/log"
bm "go-common/library/net/http/blademaster"
"go-common/library/net/http/blademaster/middleware/verify"
)
var (
srv *service.Service
vfy *verify.Verify
)
// Init init
func Init(c *conf.Config, service *service.Service) {
srv = service
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/lottery")
{
//g.GET("/start", vfy.Verify, howToStart)
g.GET("/start", howToStart)
}
}
func ping(c *bm.Context) {
if err := srv.Ping(c); err != nil {
log.Error("ping error(%v)", err)
c.AbortWithStatus(http.StatusServiceUnavailable)
}
}
func register(c *bm.Context) {
c.JSON(map[string]interface{}{}, nil)
}
// example for http request handler
func howToStart(c *bm.Context) {
c.String(0, "Golang 大法好 !!!")
}

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 = ["service.go"],
importpath = "go-common/app/service/live/xlottery/service",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/service/live/xlottery/conf:go_default_library",
"//app/service/live/xlottery/dao:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [
":package-srcs",
"//app/service/live/xlottery/service/v1:all-srcs",
],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,33 @@
package service
import (
"context"
"go-common/app/service/live/xlottery/conf"
"go-common/app/service/live/xlottery/dao"
)
// Service struct
type Service struct {
c *conf.Config
dao *dao.Dao
}
// New init
func New(c *conf.Config) (s *Service) {
s = &Service{
c: c,
dao: dao.New(c),
}
return s
}
// Ping Service
func (s *Service) Ping(c context.Context) (err error) {
return s.dao.Ping(c)
}
// Close Service
func (s *Service) Close() {
s.dao.Close()
}

View File

@@ -0,0 +1,71 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)
go_library(
name = "go_default_library",
srcs = [
"capsule.go",
"storm.go",
],
importpath = "go-common/app/service/live/xlottery/service/v1",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/service/live/banned_service/api/liverpc:go_default_library",
"//app/service/live/banned_service/api/liverpc/v1:go_default_library",
"//app/service/live/broadcast-proxy/api/v1:go_default_library",
"//app/service/live/captcha/api/liverpc:go_default_library",
"//app/service/live/captcha/api/liverpc/v0:go_default_library",
"//app/service/live/gift/api/liverpc:go_default_library",
"//app/service/live/gift/api/liverpc/v1:go_default_library",
"//app/service/live/live-dm/api/grpc/v1:go_default_library",
"//app/service/live/rc/api/liverpc/v1:go_default_library",
"//app/service/live/userext/api/liverpc/v1:go_default_library",
"//app/service/live/xlottery/api/grpc/v1:go_default_library",
"//app/service/live/xlottery/conf:go_default_library",
"//app/service/live/xlottery/dao:go_default_library",
"//app/service/live/xlottery/model:go_default_library",
"//app/service/live/xuser/api/grpc/v1:go_default_library",
"//app/service/main/account/api:go_default_library",
"//app/service/main/account/model:go_default_library",
"//app/service/main/filter/api/grpc/v1: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/report:go_default_library",
"//library/sync/pipeline/fanout: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"],
)
go_test(
name = "go_default_test",
srcs = ["capsule_test.go"],
embed = [":go_default_library"],
tags = ["automanaged"],
deps = [
"//app/service/live/xlottery/api/grpc/v1:go_default_library",
"//app/service/live/xlottery/conf:go_default_library",
"//vendor/github.com/smartystreets/goconvey/convey:go_default_library",
],
)

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,180 @@
package v1
import (
"context"
"flag"
"fmt"
"testing"
"github.com/smartystreets/goconvey/convey"
pb "go-common/app/service/live/xlottery/api/grpc/v1"
"go-common/app/service/live/xlottery/conf"
)
var (
s *CapsuleService
)
func init() {
flag.Set("conf", "../../cmd/test.toml")
var err error
if err = conf.Init(); err != nil {
panic(err)
}
s = NewCapsuleService(conf.Conf)
}
func TestCapsuleService_UpdateCoin(t *testing.T) {
areaIdsTest := make([]*pb.UpdateCoinConfigReq_AreaIds, 0)
areaIdTest := &pb.UpdateCoinConfigReq_AreaIds{}
areaIdTest.ParentId = 2
areaIdTest.IsAll = 1
areaIdsTest = append(areaIdsTest, areaIdTest)
convey.Convey("TestAdd", t, func() {
res, err := s.UpdateCoinConfig(context.TODO(), &pb.UpdateCoinConfigReq{
Id: 1,
Title: "普通扭蛋",
ChangeNum: 2,
StartTime: 1541751430,
EndTime: 1541891430,
Status: 0,
GiftType: 2,
GiftIds: []int64{1, 2, 3},
AreaIds: areaIdsTest,
})
fmt.Println(res, err)
t.Logf("%v,%s", res, err)
convey.So(err, convey.ShouldBeNil)
})
}
func TestCapsuleService_UpdatePool(t *testing.T) {
convey.Convey("TestUpdatePool", t, func() {
res, err := s.UpdatePool(context.TODO(), &pb.UpdatePoolReq{
Id: 13,
CoinId: 1,
Title: "普通扭蛋币aa",
StartTime: 1,
EndTime: 2114352000,
Rule: "使用价值累计达到10000瓜子的礼物包含直接使用瓜子购买、道具包裹但不包括产生梦幻扭蛋币的活动道具可以获得1枚扭蛋币。使用扭蛋币可以参与抽奖。",
})
fmt.Println(res, err)
t.Logf("%v,%s", res, err)
convey.So(err, convey.ShouldBeNil)
})
}
func TestCapsuleService_UpdatePoolDetail(t *testing.T) {
convey.Convey("TestUpdatePoolDetail", t, func() {
res, err := s.UpdatePoolPrize(context.TODO(), &pb.UpdatePoolPrizeReq{
PoolId: 1,
Id: 14,
Type: 6,
Num: 1,
ObjectId: 25,
Expire: 0,
WebUrl: "https://i0.hdslb.com/bfs/live/8e7a4dc8de374faee22fca7f9a3f801a1712a36b.png",
MobileUrl: "https://i0.hdslb.com/bfs/live/8e7a4dc8de374faee22fca7f9a3f801a1712a36b.png",
Description: "小电视是一种直播虚拟礼物,可以在直播间送给自己喜爱的主播哦~",
JumpUrl: "https://i0.hdslb.com/bfs/live/8e7a4dc8de374faee22fca7f9a3f801a1712a36b.png",
ProType: 1,
Chance: 6000,
})
fmt.Println(res, err)
t.Logf("%v,%s", res, err)
convey.So(err, convey.ShouldBeNil)
})
}
func TestCapsuleService_UpdateCoinStatus(t *testing.T) {
convey.Convey("TestUpdateCoinStatus", t, func() {
res, err := s.UpdateCoinStatus(context.TODO(), &pb.UpdateCoinStatusReq{
Id: 1,
Status: 1,
})
fmt.Println(res, err)
t.Logf("%v,%s", res, err)
convey.So(err, convey.ShouldBeNil)
})
}
func TestCapsuleService_DeleteCoin(t *testing.T) {
convey.Convey("TestDeleteCoin", t, func() {
res, err := s.DeleteCoin(context.TODO(), &pb.DeleteCoinReq{
Id: 1,
})
fmt.Println(res, err)
t.Logf("%v,%s", res, err)
convey.So(err, convey.ShouldBeNil)
})
}
func TestCapsuleService_DeletePool(t *testing.T) {
convey.Convey("TestDeletePool", t, func() {
res, err := s.DeletePool(context.TODO(), &pb.DeletePoolReq{
Id: 7,
})
fmt.Println(res, err)
t.Logf("%v,%s", res, err)
convey.So(err, convey.ShouldBeNil)
})
}
func TestCapsuleService_UpdatePoolStatus(t *testing.T) {
convey.Convey("TestUpdatePoolStatus", t, func() {
res, err := s.UpdatePoolStatus(context.TODO(), &pb.UpdatePoolStatusReq{
Id: 1,
Status: 1,
})
fmt.Println(res, err)
t.Logf("%v,%s", res, err)
convey.So(err, convey.ShouldBeNil)
})
}
func TestCapsuleService_GetPoolDetail(t *testing.T) {
convey.Convey("TestGetPoolDetail", t, func() {
res, err := s.GetPoolPrize(context.TODO(), &pb.GetPoolPrizeReq{
PoolId: 1,
})
fmt.Println(res, err)
t.Logf("%v,%s", res, err)
convey.So(err, convey.ShouldBeNil)
})
}
func TestCapsuleService_DeletePoolDetail(t *testing.T) {
convey.Convey("TestDeletePool", t, func() {
res, err := s.DeletePoolPrize(context.TODO(), &pb.DeletePoolPrizeReq{
Id: 2,
})
fmt.Println(res, err)
t.Logf("%v,%s", res, err)
convey.So(err, convey.ShouldBeNil)
})
}
func TestCapsuleService_GetPoolConfig(t *testing.T) {
convey.Convey("TestGetPool", t, func() {
res, err := s.GetPoolList(context.TODO(), &pb.GetPoolListReq{
Page: 1,
PageSize: 16,
})
fmt.Println(res, err)
t.Logf("%v,%s", res, err)
convey.So(err, convey.ShouldBeNil)
})
}
func TestCapsuleService_GetCoinList(t *testing.T) {
convey.Convey("TestGetCoinList", t, func() {
res, err := s.GetCoinList(context.TODO(), &pb.GetCoinListReq{
Page: 1,
PageSize: 16,
})
fmt.Println(res, err)
t.Logf("%v,%s", res, err)
convey.So(err, convey.ShouldBeNil)
})
}

View File

@@ -0,0 +1,744 @@
package v1
import (
"context"
"encoding/json"
"fmt"
"sync"
banclient "go-common/app/service/live/banned_service/api/liverpc"
ban "go-common/app/service/live/banned_service/api/liverpc/v1"
danmuku "go-common/app/service/live/broadcast-proxy/api/v1"
captchaclient "go-common/app/service/live/captcha/api/liverpc"
captcha "go-common/app/service/live/captcha/api/liverpc/v0"
giftclient "go-common/app/service/live/gift/api/liverpc"
gift "go-common/app/service/live/gift/api/liverpc/v1"
dm "go-common/app/service/live/live-dm/api/grpc/v1"
v1 "go-common/app/service/live/xlottery/api/grpc/v1"
filter "go-common/app/service/main/filter/api/grpc/v1"
"go-common/app/service/live/xlottery/conf"
"go-common/app/service/live/xlottery/dao"
"go-common/app/service/live/xlottery/model"
xuserclient "go-common/app/service/live/xuser/api/grpc/v1"
"math/rand"
"strconv"
"strings"
"time"
accountApi "go-common/app/service/main/account/api"
"go-common/library/log"
"go-common/library/net/metadata"
"go-common/library/sync/pipeline/fanout"
"github.com/pkg/errors"
)
const (
ttl = 90 // 抽奖持续时间 90s
cacheTTL = 100 // 缓存过期时间
awardNum = 100 // 单个节奏风暴发放奖励个数 100
roomStormInfo = "room:storm:info:%d" // 房间节奏风暴信息
stormInfo = "storm:info:%d" // 节奏风暴信息
stormAwardCount = "storm:award:%d" // 节奏风暴已经发放奖品数量
joinFlag = "storm:join:%d" // 用户参加标识
stormGif = "http://static.hdslb.com/live-static/live-room/images/gift-section/mobilegift/2/jiezou.gif?2017011901"
stormDanmuLimit = "storm:danmu:limit:%d:%d" //lotteryid, uid
stormDanmuInterval = 5 //用户点击节奏风暴没中奖时发的弹幕的间隔时间
)
var errCodeMsg = map[errCode]string{
UserNotAuthErr: "用户未登陆",
UserCaptchaFail: "验证码没通过",
StormExpireErr: "节奏风暴抽奖过期",
AlreadyPickErr: "已经领取奖励",
StormFullErr: "领奖人数已经满了",
StormContentEmpty: "节奏风暴内容为空",
JustSupportOneStorm: "同时仅支持一个节奏风暴,请稍后使用本功能",
BeatNotExsit: "尚未设置自定义节奏",
BeatFailureAudit: "你自定义的节奏未通过审核",
BeatAuditing: "你编辑的自定义节奏正在审核中,请稍等",
WrongTryAgain: "出错啦,再试试吧",
BeatNotAudited: "节奏内容未通过审核,请尝试修改为其他内容哦",
BeatBanned: "该自定义内容被屏蔽",
BeatIsShield: "看来你的弹幕里有直播间屏蔽词,这节奏怕是带不起来了",
UserNotVip: "你现在还不是年费老爷,无法使用自定义节奏",
MissBeat: "你错过了奖励,下次要更快一点哦~",
InnerErr: "内部错误",
ParamErr: "参数错误",
}
type errCode int32
const (
//UserNotAuthErr 用户未登陆
UserNotAuthErr errCode = 1005001
//UserCaptchaFail 验证码没通过
UserCaptchaFail errCode = 1005002
//StormExpireErr 节奏风暴抽奖过期
StormExpireErr errCode = 1005003
//AlreadyPickErr 已经领取奖励
AlreadyPickErr errCode = 1005004
//StormFullErr 领奖人数已经满了
StormFullErr errCode = 1005005
//StormContentEmpty 节奏风暴内容为空
StormContentEmpty errCode = 1005006
//JustSupportOneStorm 一个房间只支持一个节奏风暴,请稍后
JustSupportOneStorm errCode = 1005007
//BeatNotExsit 尚未设置自定义节奏
BeatNotExsit errCode = 1005008
//BeatFailureAudit 你自定义的节奏未通过审核
BeatFailureAudit errCode = 1005009
//BeatAuditing 你编辑的自定义节奏正在审核中,请稍等
BeatAuditing errCode = 1005010
//WrongTryAgain 出错啦,再试试吧
WrongTryAgain errCode = 1005011
//BeatNotAudited 节奏内容未通过审核,请尝试修改为其他内容哦
BeatNotAudited errCode = 1005012
//BeatBanned 该自定义内容被屏蔽
BeatBanned errCode = 1005013
//BeatIsShield 看来你的弹幕里有直播间屏蔽词,这节奏怕是带不起来了
BeatIsShield errCode = 1005014
//UserNotVip 你现在还不是年费老爷,无法使用自定义节奏
UserNotVip errCode = 1005015
//MissBeat 你错过了奖励,下次要更快一点哦~
MissBeat errCode = 1005016
//InnerErr 内部错误
InnerErr errCode = 1005017
//ParamErr 参数错误
ParamErr errCode = 1005018
)
var publicBeats = map[int64]string{
1: "前方高能预警,注意这不是演习",
2: "我从未见过如此厚颜无耻之人",
3: "那万一赢了呢",
4: "你们城里人真会玩",
5: "左舷弹幕太薄了",
7: "要优雅,不要污",
8: "我选择狗带",
9: "可爱即正义~~",
10: "糟了,是心动的感觉!",
41000: "这个直播间已经被我们承包了!",
41001: "妈妈问我为什么跪着看直播 w(゚Д゚)w",
41002: "你们对力量一无所知~( ̄▽ ̄)~",
}
// StormService 节奏风暴服务
type StormService struct {
c *conf.Config
dao *dao.Dao
DMClient dm.DMClient
AccountClient accountApi.AccountClient
GiftClient *giftclient.Client
BanClient *banclient.Client
CaptchaClient *captchaclient.Client
VipClient xuserclient.VipClient
DanmakuClient danmuku.DanmakuClient
FilterClient filter.FilterClient
task *fanout.Fanout
}
// NewStromService 获得StormService实例并初始化
func NewStromService(c *conf.Config) (s *StormService) {
dmClient, err := dm.NewClient(nil)
if err != nil {
panic(err)
}
accountClient, err := accountApi.NewClient(nil)
if err != nil {
panic(err)
}
xuserClient, err := xuserclient.NewClient(nil)
if err != nil {
panic(err)
}
danmuku, err := danmuku.NewClient(nil)
if err != nil {
panic(err)
}
fliterClient, err := filter.NewClient(nil)
if err != nil {
panic(err)
}
s = &StormService{
c: c,
dao: dao.New(c),
DMClient: dmClient,
AccountClient: accountClient,
GiftClient: giftclient.New(nil),
BanClient: banclient.New(nil),
CaptchaClient: captchaclient.New(nil),
VipClient: xuserClient.VipClient,
DanmakuClient: danmuku,
FilterClient: fliterClient,
task: fanout.New("storm", fanout.Worker(10), fanout.Buffer(10240)),
}
expireLocalCache()
return s
}
// Ping Service
func (s *StormService) Ping(c context.Context) (err error) {
return s.dao.Ping(c)
}
// Close Service
func (s *StormService) Close() {
s.dao.Close()
}
// Start 开启节奏风暴抽奖
func (s *StormService) Start(ctx context.Context, req *v1.StartStormReq) (*v1.StartStormResp, error) {
roomID := req.GetRoomid()
uID := req.GetUid()
beatID := req.GetBeatid()
useShield := req.GetUseShield()
ruID := req.GetRuid()
skipExternalCheck := req.GetSkipExternalCheck()
log.Info("#storm_start# beatid=%d uid=%d ruid=%d roomdid=%d num=%d", beatID, uID, ruID, roomID, req.GetNum())
if roomID == 0 || uID == 0 || beatID == 0 || ruID == 0 {
return &v1.StartStormResp{Code: int32(ParamErr), Msg: errCodeMsg[ParamErr]}, nil
}
content, ec := s.preCheck(ctx, roomID, uID, beatID, ruID, skipExternalCheck, useShield)
if ec != 0 {
return &v1.StartStormResp{Code: int32(ec), Msg: errCodeMsg[ec]}, nil
}
//创建 SpecialGift 得到 返回的id
dbid, err := s.dao.InsertSpecialGift(&model.SpecialGift{
UID: req.GetUid(),
RoomID: req.GetRoomid(),
GiftNum: req.GetNum(),
GiftID: 39,
CreateTime: time.Now(),
CustomField: fmt.Sprintf(`{"content":"%s"}`, content),
})
if err != nil {
log.Error("insert special gift err:%s", err.Error())
return &v1.StartStormResp{Code: int32(InnerErr), Msg: errCodeMsg[InnerErr]}, nil
}
//节奏风暴对外id, 是数据库中的id拼接上一个随机6位数而成, 是个递增的值
rand.Seed(time.Now().Unix())
randid := rand.Intn(999999)
id := dbid*1000000 + int64(randid)
newCtx := metadata.WithContext(ctx)
s.setCache(newCtx, id, req.GetRoomid(), req.GetNum(), content)
//broadcast message
m := map[string]interface{}{
"cmd": "SPECIAL_GIFT",
"data": map[string]interface{}{
"39": map[string]interface{}{
"id": fmt.Sprintf("%d", id),
"time": ttl,
"hadJoin": 0,
"num": req.GetNum(),
"content": content,
"action": "start",
"storm_gif": stormGif,
},
},
}
str, err := json.Marshal(m)
if err != nil {
log.Error("json marshal err:%s", err.Error())
return &v1.StartStormResp{Code: int32(InnerErr), Msg: errCodeMsg[InnerErr]}, nil
}
err = s.callBroadCastRoom(ctx, string(str), req.GetRoomid())
if err != nil {
return &v1.StartStormResp{Code: int32(InnerErr), Msg: errCodeMsg[InnerErr]}, nil
}
log.Info("#storm_start_success# beatid=%d uid=%d ruid=%d roomdid=%d num=%d", beatID, uID, ruID, roomID, req.GetNum())
return &v1.StartStormResp{Start: &v1.StartData{Time: ttl, Id: id}}, nil
}
// CanStart 检查房间是否能开启节奏风暴抽奖
func (s *StormService) CanStart(ctx context.Context, req *v1.StartStormReq) (*v1.CanStartStormResp, error) {
log.Info("#storm_canstart# beatid=%d uid=%d ruid=%d roomdid=%d num=%d", req.GetBeatid(), req.GetUid(), req.GetRuid(), req.GetRoomid(), req.GetNum())
_, ec := s.preCheck(ctx, req.GetRoomid(), req.GetUid(), req.GetBeatid(), req.GetRuid(), req.GetSkipExternalCheck(), req.GetUseShield())
if ec != 0 {
return &v1.CanStartStormResp{Code: int32(ec), Msg: errCodeMsg[ec]}, nil
}
return &v1.CanStartStormResp{RetStatus: true}, nil
}
// Join 用户参加节奏风暴抽奖
func (s *StormService) Join(ctx context.Context, req *v1.JoinStormReq) (resp *v1.JoinStormResp, err error) {
var m map[string]string
// 本来defer后面的逻辑是在controller层中做的go的话把这块代码移到网关层就不太好
defer func() {
//resp不为nil 说明抽奖成功 ,发送弹幕
if m != nil {
if resp.GetJoin() != nil {
if err1 := s.task.Do(metadata.WithContext(ctx), func(ctx context.Context) {
s.sendDamu(ctx, req.GetRoomid(), req.GetMid(), m["content"], req.GetPlatform())
}); err1 == fanout.ErrFull {
log.Info("sendDamu_task_is_full roomid= %d ", req.GetRoomid())
s.sendDamu(ctx, req.GetRoomid(), req.GetMid(), m["content"], req.GetPlatform())
}
} else {
//抽奖不成功也要发送弹幕概率20% 造成一种很多人中奖的假象
rand.Seed(time.Now().Unix())
if s.canSendDamu(ctx, req.GetId(), req.GetMid()) && rand.Intn(5) == 2 {
if err1 := s.task.Do(metadata.WithContext(ctx), func(ctx context.Context) {
s.sendDamu(ctx, req.GetRoomid(), req.GetMid(), m["content"], req.GetPlatform())
}); err1 == fanout.ErrFull {
log.Info("sendDamu_task_is_full roomid= %d ", req.GetRoomid())
s.sendDamu(ctx, req.GetRoomid(), req.GetMid(), m["content"], req.GetPlatform())
}
}
}
}
}()
mid := req.GetMid()
platform := req.GetPlatform()
if req.GetId() != 0 {
m = s.getStormByID(ctx, req.GetId())
} else {
m = s.getByRoomID(ctx, req.GetRoomid(), mid)
}
if m == nil {
return &v1.JoinStormResp{Code: int32(MissBeat), Msg: errCodeMsg[MissBeat]}, nil
}
//过期了
if t, _ := strconv.ParseInt(m["time"], 10, 64); t < time.Now().Unix() {
newCtx := metadata.WithContext(ctx)
s.delCache(newCtx, req.GetId(), req.GetRoomid())
m = nil
return &v1.JoinStormResp{Code: int32(StormExpireErr), Msg: errCodeMsg[StormExpireErr]}, nil
}
roomid, _ := strconv.ParseInt(m["roomid"], 10, 64)
req.Roomid = roomid
//验证码
notMobile := !strings.Contains("android,ios", strings.ToLower(platform))
if !s.isVerity(ctx, mid) && notMobile {
if isv, _ := s.callCaptcha(ctx, req.GetCaptchaToken(), req.GetCaptchaPhrase()); !isv {
m = nil
return &v1.JoinStormResp{Code: int32(UserCaptchaFail), Msg: errCodeMsg[UserCaptchaFail]}, nil
}
}
stormID, err := strconv.ParseInt(m["id"], 10, 64)
if err != nil {
m = nil
return &v1.JoinStormResp{Code: int32(MissBeat), Msg: errCodeMsg[MissBeat]}, nil
}
joinKey := fmt.Sprintf(joinFlag, stormID)
exist, _ := s.dao.SIsMember(ctx, joinKey, fmt.Sprintf("%d", mid))
if exist {
m = nil
return &v1.JoinStormResp{Code: int32(AlreadyPickErr), Msg: errCodeMsg[AlreadyPickErr]}, nil
}
awardKey := fmt.Sprintf(stormAwardCount, stormID)
count, _ := s.dao.GetInt64(ctx, awardKey)
num, _ := strconv.ParseInt(m["num"], 10, 64)
if count > num {
return &v1.JoinStormResp{Code: int32(MissBeat), Msg: errCodeMsg[MissBeat]}, nil
}
//10秒钟放出10个奖励防止一下子就被抽完了
coldNum := num / 10
coldKey := fmt.Sprintf("storm_cold:%d:%d", stormID, time.Now().Unix())
finalcoldNum := s.dao.Incr(ctx, coldKey)
s.dao.Expire(ctx, coldKey, 10)
if int64(finalcoldNum) > coldNum {
return &v1.JoinStormResp{Code: int32(MissBeat), Msg: errCodeMsg[MissBeat]}, nil
}
countIncr := s.dao.Incr(ctx, awardKey)
s.dao.SAdd(ctx, joinKey, strconv.Itoa(int(mid)))
s.dao.Expire(ctx, joinKey, cacheTTL)
year, month, day := time.Now().Date()
ts := time.Date(year, month, day, 0, 0, 0, 0, time.Now().Location()).Add(7 * 86400 * time.Second).Unix()
if err := s.task.Do(metadata.WithContext(ctx), func(ctx context.Context) {
s.callGiftAddFreeGift(ctx, mid, 1, 6 /*亿圆*/, ts)
}); err == fanout.ErrFull {
log.Info("add_free_gift_task_is_full mid= %d ", mid)
s.callGiftAddFreeGift(ctx, mid, 1, 6 /*亿圆*/, ts)
}
if int64(countIncr) >= num {
jsonMap := map[string]interface{}{
"cmd": "SPECIAL_GIFT",
"data": map[string]interface{}{
"39": map[string]interface{}{
"id": stormID,
"action": "end",
},
},
}
jsonByte, _ := json.Marshal(jsonMap)
err := s.callBroadCastRoom(ctx, string(jsonByte), roomid)
if err != nil {
log.Error("send_end_broadcase_err:%s", err.Error())
}
s.delCache(metadata.WithContext(ctx), stormID, roomid)
}
log.Info("#storm_join_success#%v#%v#%v", stormID, req.GetMid(), countIncr)
resp = &v1.JoinStormResp{Join: &v1.JoinData{
GiftId: 39,
Title: "节奏风暴",
Content: fmt.Sprintf("<p>你是前 %d 位跟风大师<br />恭喜你获得一个亿圆(7天有效期)</p>", countIncr),
MobileContent: fmt.Sprintf("你是前 %d 位跟风大师", countIncr),
GiftImg: "http://static.hdslb.com/live-static/live-room/images/gift-section/gift-6.png?2017011901",
GiftNum: 1,
GiftName: "亿圆",
}}
return resp, nil
}
// Check 检查用户能否加入
func (s *StormService) Check(ctx context.Context, req *v1.CheckStormReq) (*v1.CheckStormResp, error) {
m := s.getByRoomID(ctx, req.GetRoomid(), req.GetUid())
if m == nil {
return &v1.CheckStormResp{
Code: int32(StormExpireErr),
Msg: errCodeMsg[StormExpireErr],
}, nil
}
return mapTOCheckStormResp(m), nil
}
func mapTOCheckStormResp(m map[string]string) *v1.CheckStormResp {
ret := &v1.CheckData{}
for k, v := range m {
switch k {
case "id":
ret.Id, _ = strconv.ParseInt(v, 10, 64)
case "roomid":
ret.Roomid, _ = strconv.ParseInt(v, 10, 64)
case "num":
ret.Num, _ = strconv.ParseInt(v, 10, 64)
case "send_num":
ret.SendNum = v
case "time":
ret.Time, _ = strconv.ParseInt(v, 10, 64)
ret.Time = ret.Time - time.Now().Unix()
case "content":
ret.Content = v
case "hasJoin":
r, _ := strconv.ParseInt(v, 10, 32)
ret.HasJoin = int32(r)
case "storm_gift":
ret.StormGif = v
}
}
return &v1.CheckStormResp{Check: ret}
}
// preCheck 返回的error=nil 表示 check 失败
func (s *StormService) preCheck(ctx context.Context, roomid, uid, beatid, ruid, skipExternalCheck int64, userShield bool) (content string, ec errCode) {
m := s.getByRoomID(ctx, roomid, uid)
if m != nil {
ec = JustSupportOneStorm
return
}
content, ec = s.getBeat(ctx, beatid, uid, ruid, skipExternalCheck, userShield)
return
}
func (s *StormService) getBeat(ctx context.Context, beatid, uid, ruid, skipExternalCheck int64, userShield bool) (string, errCode) {
if v, isexit := publicBeats[beatid]; isexit {
content := v
if userShield {
if ec := s.checkShield(ctx, uid, content); ec != 0 {
return "", ec
}
}
return content, 0
}
if skipExternalCheck != 0 {
//判断账户是不是vip不是vip就return 无法使用自定义
resp, err := s.VipClient.Info(ctx, &xuserclient.UidReq{Uid: uid})
if err != nil {
return "", InnerErr
}
if resp.GetInfo().GetSvip() <= 0 {
return "", UserNotVip
}
}
beat, err := s.dao.FindBeatByBeatIDAndUID(beatid, uid)
if err != nil {
log.Error("find_beat_by_beatid = %d and uid %d err :%s ", beatid, uid, err.Error())
return "", BeatNotExsit
}
if beat.Content == "" {
return "", StormContentEmpty
}
if beat.Status == -1 {
return "", BeatFailureAudit
}
if beat.Status == 1 {
return "", BeatAuditing
}
if skipExternalCheck != 0 {
resp, err := s.FilterClient.Filter(ctx, &filter.FilterReq{
Area: "live_danmu",
Message: beat.Content,
})
if err != nil {
return "", InnerErr
}
if resp.GetLevel() > 15 { // level 值大于15 就要被过滤
return "", BeatNotAudited
}
log.Info("filter_pass ")
}
if userShield {
if ec := s.checkShield(ctx, uid, beat.Content); ec != 0 {
return "", ec
}
}
return beat.Content, 0
}
func (s *StormService) checkShield(ctx context.Context, uid int64, content string) errCode {
resp, err := s.BanClient.V1Shield.IsShieldContent(ctx, &ban.ShieldIsShieldContentReq{
Uid: uid,
Content: content,
})
if err != nil {
log.Error("call_shield_uid = %d content= %s fail err: %s", uid, content, err.Error())
return InnerErr
}
if resp.GetData().GetIsShieldContent() {
log.Info("call_shield_IsShieldContent is true ")
return BeatBanned
}
log.Info("call_shield_success ")
return 0
}
// getByRoomID 获取正在进行的节奏风暴信息和当前用户的参加信息
func (s *StormService) getByRoomID(ctx context.Context, roomID int64, uid int64) map[string]string {
ret, err := s.dao.HGetAll(ctx, fmt.Sprintf(roomStormInfo, roomID))
if err != nil {
if err != dao.ErrEmptyMap {
log.Info("redis_hgetall_roomid = %d uid = %d err : %s", roomID, uid, err.Error())
}
return nil
}
var id, t int64
if id, err = strconv.ParseInt(ret["id"], 10, 64); err != nil {
log.Info("ParseInt ret[id] err : %s", err.Error())
return nil
}
if t, err = strconv.ParseInt(ret["time"], 10, 64); err != nil {
log.Info("ParseInt ret[id] err : %s", err.Error())
return nil
}
// 过期了
if t < time.Now().Unix() {
log.Info("the_roomid %d beat_storm_is_expired ", roomID)
roomid, _ := strconv.ParseInt(ret["roomid"], 10, 64)
s.delCache(ctx, id, roomid)
return nil
}
ret["hasJoin"] = "0"
joinKey := fmt.Sprintf(joinFlag, id)
if joined, _ := s.dao.SIsMember(ctx, joinKey, strconv.Itoa(int(uid))); joined {
ret["hasJoin"] = "1"
}
ret["storm_gift"] = stormGif
return ret
}
var localCache map[int64]map[string]string
var localCacheLock sync.RWMutex
func init() {
localCache = make(map[int64]map[string]string)
}
func expireLocalCache() {
go func() {
ticker := time.NewTicker(time.Second * 50)
for range ticker.C {
localCacheLock.Lock()
for k, v := range localCache {
if t, _ := strconv.ParseInt(v["time"], 10, 64); t < time.Now().Unix() {
delete(localCache, k)
}
}
localCacheLock.Unlock()
}
}()
}
func (s *StormService) getStormByID(ctx context.Context, id int64) map[string]string {
localCacheLock.RLock()
m, ok := localCache[id]
localCacheLock.RUnlock()
if ok {
log.Info("load_data_from_localcache_id = %d", id)
return m
}
m, _ = s.dao.HGetAll(ctx, fmt.Sprintf(stormInfo, id))
if m != nil {
localCacheLock.Lock()
localCache[id] = m
localCacheLock.Unlock()
log.Info("load_data_from_redis_id = %d", id)
return m
}
return nil
}
func (s *StormService) setCache(ctx context.Context, id, roomID, num int64, content string) {
sr := map[string]interface{}{
"id": id,
"roomid": roomID,
"num": num * awardNum, //奖品数量
"send_num": num, //节奏风暴数量
"time": time.Now().Unix() + ttl,
"content": content,
}
roomKey := fmt.Sprintf(roomStormInfo, roomID)
s.dao.HMSet(ctx, roomKey, sr)
s.dao.Expire(ctx, roomKey, ttl)
stormKey := fmt.Sprintf(stormInfo, id)
s.dao.HMSet(ctx, stormKey, sr)
s.dao.Expire(ctx, stormKey, ttl)
awardKey := fmt.Sprintf(stormAwardCount, id)
s.dao.SetEx(ctx, awardKey, 0, ttl)
}
func (s *StormService) delCache(ctx context.Context, _id, roomID int64) {
roomKey := fmt.Sprintf(roomStormInfo, roomID)
s.dao.Del(ctx, roomKey)
stormKey := fmt.Sprintf(stormInfo, _id)
s.dao.Del(ctx, stormKey)
awardKey := fmt.Sprintf(stormAwardCount, _id)
s.dao.Expire(ctx, awardKey, ttl)
joinFlagKey := fmt.Sprintf(joinFlag, _id)
s.dao.Del(ctx, joinFlagKey)
}
func (s *StormService) isVerity(ctx context.Context, uid int64) bool {
key := fmt.Sprintf("user:identification:%d", uid)
ret, _ := s.dao.Get(ctx, key)
if ret != "" {
return true
}
pr, err := s.AccountClient.Profile3(ctx, &accountApi.MidReq{Mid: uid})
if err != nil {
return false
}
if pr.GetProfile().GetIdentification() == 1 {
s.dao.SetEx(ctx, key, 1, 3600)
return true
}
return false
}
// 发送room弹幕
func (s *StormService) callBroadCastRoom(ctx context.Context, jsonStr string, roomID int64) error {
_, err := s.DanmakuClient.RoomMessage(ctx, &danmuku.RoomMessageRequest{
RoomId: int32(roomID),
Ensure: 0,
Message: jsonStr,
})
if err != nil {
log.Error("call_DanmakuClient_roomid = %d err :%s", roomID, err.Error())
return err
}
log.Info("call_DanmakuClient_success_roomid=%d message=%s ", roomID, jsonStr)
return nil
}
func (s *StormService) callGiftAddFreeGift(ctx context.Context, uid, num, giftid, expireat int64) error {
_, err := s.GiftClient.V1Gift.AddFreeGift(ctx, &gift.GiftAddFreeGiftReq{
Uid: uid,
Num: num,
Giftid: giftid,
Expireat: expireat,
})
if err != nil {
log.Error("call_gift_liverpc_AddFreeGift_err: %s uid=%d num=%d giftid=%d expireAt=%d", err.Error(), uid, num, giftid, expireat)
return err
}
log.Info("call_gift_liverpc_AddFreeGift_success uid=%d num=%d giftid=%d expireAt=%d", uid, num, giftid, expireat)
return nil
}
// 验证码验证
func (s *StormService) callCaptcha(ctx context.Context, token, phrase string) (bool, error) {
resp, err := s.CaptchaClient.V0Captcha.Check(ctx, &captcha.CaptchaCheckReq{
Token: token,
Phrase: phrase,
})
if err != nil {
log.Error("call_captcha_err:%s token=%s ,phrase=%s", err.Error(), token, phrase)
return false, err
}
if resp.Code != 0 {
log.Info("captcha_fail_token=%s ,phrase=%s", token, phrase)
return false, errors.New(resp.Msg)
}
log.Info("captcha_success_token=%s ,phrase=%s", token, phrase)
return true, nil
}
// 用户抽奖成功了发弹幕
func (s *StormService) sendDamu(ctx context.Context, roomid, uid int64, content, platform string) error {
if _, err := s.DMClient.SendMsg(ctx, &dm.SendMsgReq{
Uid: uid,
Roomid: roomid,
Msg: content,
Msgtype: 1,
Platform: platform,
Mode: 1,
Fontsize: 25,
Ip: metadata.String(ctx, metadata.RemoteIP),
}); err != nil {
log.Error("send_damu_err: %s", err.Error())
return err
}
log.Info("#send_damu_success#content= %s roomid=%d uid=%d", content, roomid, uid)
return nil
}
// 检查是否可以发送弹幕
func (s *StormService) canSendDamu(ctx context.Context, id, uid int64) bool {
key := fmt.Sprintf(stormDanmuLimit, id, uid)
_, err := s.dao.SetWithNxEx(ctx, key, "1", stormDanmuInterval)
return err == nil
}