Initial commit

This commit is contained in:
Donny
2019-04-22 20:46:32 +08:00
commit 49ab8aadd1
25441 changed files with 4055000 additions and 0 deletions

View File

@@ -0,0 +1,26 @@
package(default_visibility = ["//visibility:public"])
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [
":package-srcs",
"//app/service/bbq/push/api/grpc/v1:all-srcs",
"//app/service/bbq/push/api/http/v1:all-srcs",
"//app/service/bbq/push/cmd:all-srcs",
"//app/service/bbq/push/conf:all-srcs",
"//app/service/bbq/push/dao:all-srcs",
"//app/service/bbq/push/model:all-srcs",
"//app/service/bbq/push/server/grpc:all-srcs",
"//app/service/bbq/push/server/http:all-srcs",
"//app/service/bbq/push/service:all-srcs",
],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,11 @@
# v1.0.3
1. 推送Extra schema fix
# v1.0.2
1. 推送增加异步接口
# v1.0.1
1. 推送功能重构
# v1.0.0
1. 上线功能bbq push

View File

@@ -0,0 +1,8 @@
# Owner
daiwei
# Author
daiwei
# Reviewer
daiwei

View File

@@ -0,0 +1,12 @@
# See the OWNERS docs at https://go.k8s.io/owners
approvers:
- daiwei
labels:
- bbq
- service
- service/bbq/push
options:
no_parent_owners: true
reviewers:
- daiwei

View File

@@ -0,0 +1,12 @@
# push-service
# 项目简介
1.bbq push
# 编译环境
# 依赖包
# 编译执行

View File

@@ -0,0 +1,56 @@
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/bbq/push/api/grpc/v1",
proto = ":v1_proto",
tags = ["automanaged"],
deps = ["@com_github_gogo_protobuf//gogoproto:go_default_library"],
)
go_library(
name = "go_default_library",
srcs = [],
embed = [":v1_go_proto"],
importpath = "go-common/app/service/bbq/push/api/grpc/v1",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"@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"],
)

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,72 @@
syntax = "proto3";
package bbq.service.push.v1;
import "github.com/gogo/protobuf/gogoproto/gogo.proto";
option go_package = "v1";
option (gogoproto.goproto_getters_all) = false;
option (gogoproto.goproto_stringer_all) = false;
option (gogoproto.stringer_all) = true;
message Device {
string register_id = 1 [(gogoproto.jsontag) = "register_id",(gogoproto.moretags) = "form:\"register_id\"",(gogoproto.customname) = "RegisterID"];
int32 platform = 2 [(gogoproto.jsontag) = "platform",(gogoproto.moretags) = "form:\"platform\"",(gogoproto.customname) = "Platform"];
int32 sdk = 3 [(gogoproto.jsontag) = "sdk",(gogoproto.moretags) = "form:\"sdk\"",(gogoproto.customname) = "SDK"];
int64 send_no = 4 [(gogoproto.jsontag) = "send_no",(gogoproto.moretags) = "form:\"send_no\"",(gogoproto.customname) = "SendNo"];
}
message Extra {
string token = 1 [(gogoproto.jsontag) = "token",(gogoproto.moretags) = "form:\"token\"",(gogoproto.customname) = "Token"];
string schema = 2 [(gogoproto.jsontag) = "schema",(gogoproto.moretags) = "form:\"schema\"",(gogoproto.customname) = "Schema"];
}
message NotificationBody {
string title = 1 [(gogoproto.jsontag) = "title",(gogoproto.moretags) = "form:\"title\"",(gogoproto.customname) = "Title"];
string content = 2 [(gogoproto.jsontag) = "content",(gogoproto.moretags) = "form:\"content\"",(gogoproto.customname) = "Content"];
string sound = 3 [(gogoproto.jsontag) = "sound",(gogoproto.moretags) = "form:\"sound\"",(gogoproto.customname) = "Sound"];
int32 badge = 4 [(gogoproto.jsontag) = "badge",(gogoproto.moretags) = "form:\"badge\"",(gogoproto.customname) = "Badge"];
string extra = 5 [(gogoproto.jsontag) = "extra",(gogoproto.moretags) = "form:\"extra\"",(gogoproto.customname) = "Extra"];
}
message MessageBody {
string title = 1 [(gogoproto.jsontag) = "title",(gogoproto.moretags) = "form:\"title\"",(gogoproto.customname) = "Title"];
string content = 2 [(gogoproto.jsontag) = "content",(gogoproto.moretags) = "form:\"content\"",(gogoproto.customname) = "Content"];
string content_type = 3 [(gogoproto.jsontag) = "content_type",(gogoproto.moretags) = "form:\"content_type\"",(gogoproto.customname) = "ContentType"];
string extra = 4 [(gogoproto.jsontag) = "extra",(gogoproto.moretags) = "form:\"extra\"",(gogoproto.customname) = "Extra"];
}
message NotificationRequest {
repeated Device dev = 1 [(gogoproto.jsontag) = "dev",(gogoproto.moretags) = "form:\"dev\"",(gogoproto.customname) = "Dev"];
NotificationBody body = 2 [(gogoproto.jsontag) = "body",(gogoproto.moretags) = "form:\"body\"",(gogoproto.customname) = "Body"];
}
message PushResult {
string send_no = 1 [(gogoproto.jsontag) = "sendno,omitempty",(gogoproto.moretags) = "form:\"sendno\"",(gogoproto.customname) = "SendNo"];
string msg_id = 2 [(gogoproto.jsontag) = "msg_id,omitempty",(gogoproto.moretags) = "form:\"msg_id\"",(gogoproto.customname) = "MsgID"];
PushError err = 3 [(gogoproto.jsontag) = "error,omitempty",(gogoproto.moretags) = "form:\"error\"",(gogoproto.customname) = "Error"];
}
message PushError {
string msg = 1 [(gogoproto.jsontag) = "message",(gogoproto.moretags) = "form:\"message\"",(gogoproto.customname) = "Message"];
int32 code = 2 [(gogoproto.jsontag) = "code",(gogoproto.moretags) = "form:\"code\"",(gogoproto.customname) = "Code"];
}
message NotificationResponse {
repeated PushResult result = 1 [(gogoproto.jsontag) = "result",(gogoproto.moretags) = "form:\"result\"",(gogoproto.customname) = "Result"];
}
message MessageRequest {
repeated Device dev = 1 [(gogoproto.jsontag) = "dev",(gogoproto.moretags) = "form:\"dev\"",(gogoproto.customname) = "Dev"];
MessageBody body = 2 [(gogoproto.jsontag) = "body",(gogoproto.moretags) = "form:\"body\"",(gogoproto.customname) = "Body"];
}
message MessageResponse {
repeated PushResult result = 1 [(gogoproto.jsontag) = "result",(gogoproto.moretags) = "form:\"result\"",(gogoproto.customname) = "Result"];
}
service Push {
rpc Notification(NotificationRequest) returns (NotificationResponse);
rpc Message(MessageRequest) returns (MessageResponse);
rpc AsyncNotification(NotificationRequest) returns (NotificationResponse);
rpc AsyncMessage(MessageRequest) returns (MessageResponse);
}

View File

@@ -0,0 +1,28 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = ["api.go"],
importpath = "go-common/app/service/bbq/push/api/http/v1",
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,30 @@
package v1
// NoticeRequest .
type NoticeRequest struct {
RegID string `json:"register_id" form:"register_id" validate:"required"`
Platform int32 `json:"platform" form:"platform" validate:"required"`
SDK int32 `json:"sdk" form:"sdk" validate:"required"`
Title string `json:"title" form:"title" validate:"required"`
Content string `json:"content" form:"content" validate:"required"`
Schema string `json:"schema" form:"schema" validate:"required"`
Callback string `json:"callback" form:"callback" validate:"required"`
}
// NoticeResponse .
type NoticeResponse struct{}
// MessageRequest .
type MessageRequest struct {
RegID string `json:"register_id" form:"register_id" validate:"required"`
Platform int32 `json:"platform" form:"platform" validate:"required"`
SDK int32 `json:"sdk" form:"sdk" validate:"required"`
Title string `json:"title" form:"title" validate:"required"`
Content string `json:"content" form:"content" validate:"required"`
ContentType string `json:"content_type" form:"content_type" validate:"required"`
Schema string `json:"schema" form:"schema" validate:"required"`
Callback string `json:"callback" form:"callback" validate:"required"`
}
// MessageResponse .
type MessageResponse struct{}

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/bbq/push/cmd",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/service/bbq/push/conf:go_default_library",
"//app/service/bbq/push/server/grpc:go_default_library",
"//app/service/bbq/push/server/http:go_default_library",
"//app/service/bbq/push/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/bbq/push/cmd/client:all-srcs",
],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,40 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_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/bbq/push/cmd/client",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/service/bbq/push/api/grpc/v1:go_default_library",
"//library/net/rpc/warden:go_default_library",
"//library/time:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,53 @@
package main
import (
"context"
"flag"
"fmt"
"log"
"time"
"go-common/app/service/bbq/push/api/grpc/v1"
"go-common/library/net/rpc/warden"
xtime "go-common/library/time"
)
var (
addr string
)
func init() {
flag.StringVar(&addr, "addr", "127.0.0.1:9000", "server addr")
}
func main() {
flag.Parse()
cfg := &warden.ClientConfig{
Dial: xtime.Duration(time.Second * 3),
Timeout: xtime.Duration(time.Second * 3),
}
cc, err := warden.NewClient(cfg).Dial(context.Background(), addr)
if err != nil {
log.Fatalf("new client failed!err:=%v", err)
return
}
client := v1.NewPushClient(cc)
dev := []*v1.Device{
{
RegisterID: "1a0018970a8ddcaef46",
Platform: 1,
SDK: 1,
SendNo: 1,
},
}
fmt.Println(client.Notification(context.Background(), &v1.NotificationRequest{
Dev: dev,
Body: &v1.NotificationBody{
Title: "test title",
Content: "test content",
Extra: "{\"schema\":\"schema\"}",
},
}))
}

View File

@@ -0,0 +1,56 @@
package main
import (
"context"
"flag"
"os"
"os/signal"
"syscall"
"time"
"go-common/app/service/bbq/push/conf"
"go-common/app/service/bbq/push/server/grpc"
"go-common/app/service/bbq/push/server/http"
"go-common/app/service/bbq/push/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("start")
trace.Init(conf.Conf.Tracer)
defer trace.Close()
ecode.Init(conf.Conf.Ecode)
serv := service.New(conf.Conf)
http.Init(conf.Conf, serv)
gsrv, err := grpc.New(conf.Conf.RPCConf, serv)
if err != nil {
log.Error("grpc server start error: %s", err)
return
}
c := make(chan os.Signal, 1)
signal.Notify(c, syscall.SIGHUP, syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT)
ctx, cancel := context.WithTimeout(context.Background(), time.Second*3)
defer cancel()
for {
s := <-c
log.Info("get a signal %s", s.String())
switch s {
case syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT:
gsrv.Shutdown(ctx)
log.Info("exit")
return
case syscall.SIGHUP:
default:
return
}
}
}

View File

@@ -0,0 +1,24 @@
[log]
stdout = true
[jpush]
appKey = "43a726afe88848e30a87fe25"
secretKey = "6f31834066f0c6a1a9a2e655"
timeout = "3s"
apnsProduction = false
[rpcConf]
addr = "0.0.0.0:9092"
[workPool]
capacity = 1024
maxWorkers = 512
maxIdleWorkers = 256
minIdleWorkers = 128
keepAlive = "30s"
[infoc]
taskID = "001730"
proto = "tcp"
addr = "172.18.33.124:15140"
chanSize = 10240

View File

@@ -0,0 +1,43 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = [
"conf.go",
"jpush.go",
],
importpath = "go-common/app/service/bbq/push/conf",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//library/conf:go_default_library",
"//library/ecode/tip:go_default_library",
"//library/log:go_default_library",
"//library/log/infoc:go_default_library",
"//library/net/http/blademaster:go_default_library",
"//library/net/http/blademaster/middleware/verify:go_default_library",
"//library/net/rpc/warden:go_default_library",
"//library/net/trace:go_default_library",
"//library/time: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,98 @@
package conf
import (
"errors"
"flag"
"go-common/library/conf"
ecode "go-common/library/ecode/tip"
"go-common/library/log"
"go-common/library/log/infoc"
bm "go-common/library/net/http/blademaster"
"go-common/library/net/http/blademaster/middleware/verify"
"go-common/library/net/rpc/warden"
"go-common/library/net/trace"
xtime "go-common/library/time"
"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
RPCConf *warden.ServerConfig
WorkPool *WorkPoolConfig
JPush *JPushConfig
Infoc *infoc.Config
}
// WorkPoolConfig .
type WorkPoolConfig struct {
Capacity uint64
MaxWorkers uint64
MaxIdleWorkers uint64
MinIdleWorkers uint64
KeepAlive xtime.Duration
}
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
}

View File

@@ -0,0 +1,13 @@
package conf
import (
xtime "go-common/library/time"
)
// JPushConfig 极光推送配置
type JPushConfig struct {
AppKey string
SecretKey string
Timeout xtime.Duration
ApnsProduction bool
}

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

View File

@@ -0,0 +1,28 @@
package dao
import (
"time"
"go-common/app/service/bbq/push/conf"
"go-common/app/service/bbq/push/dao/jpush"
)
// Dao dao
type Dao struct {
c *conf.Config
JPush *jpush.Client
}
// New init mysql db
func New(c *conf.Config) (dao *Dao) {
client := jpush.New(c.JPush.AppKey, c.JPush.SecretKey, time.Duration(c.JPush.Timeout))
dao = &Dao{
c: c,
JPush: client,
}
return
}
// Close close the resource.
func (d *Dao) Close() {
}

View File

@@ -0,0 +1,39 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = [
"audience.go",
"client.go",
"message.go",
"notification.go",
"option.go",
"payload.go",
],
importpath = "go-common/app/service/bbq/push/dao/jpush",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//library/stat:go_default_library",
"//library/stat/prom: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,12 @@
package jpush
// Audience .
type Audience struct {
Tag interface{} `json:"tag,omitempty"`
TagAnd interface{} `json:"tag_and,omitempty"`
TagNot interface{} `json:"tag_not,omitempty"`
Alias interface{} `json:"alias,omitempty"`
RegID interface{} `json:"registration_id,omitempty"`
Segment interface{} `json:"segment,omitempty"`
AbTest interface{} `json:"abtest,omitempty"`
}

View File

@@ -0,0 +1,67 @@
package jpush
import (
"bytes"
"encoding/base64"
"io/ioutil"
"net/http"
"time"
"go-common/library/stat"
"go-common/library/stat/prom"
)
const (
pushURL = "https://api.jpush.cn/v3/push"
// 如果创建的极光应用分配的北京机房,并且 API 调用方的服务器也位于北京,则比较适合调用极光北京机房的 API可以提升一定的响应速度。
// PUSH_URL = "https://bjapi.push.jiguang.cn/v3/push"
// VALIDATE_URL = "https://api.jpush.cn/v3/push/validate"
// GROUP_PUSH_URL = "https://api.jpush.cn/v3/grouppush"
)
// Client for JPush
type Client struct {
Auth string
Stats stat.Stat
Timeout time.Duration
}
// New .
func New(appKey string, secretKey string, timeout time.Duration) *Client {
auth := "Basic " + base64.StdEncoding.EncodeToString([]byte(appKey+":"+secretKey))
return &Client{
Auth: auth,
Stats: prom.HTTPClient,
Timeout: timeout,
}
}
// Push .
func (clt *Client) Push(b []byte) (resp []byte, err error) {
if clt.Stats != nil {
now := time.Now()
defer func() {
clt.Stats.Timing(pushURL, int64(time.Since(now)/time.Millisecond))
// log.Info("jpush stats timing: %v", int64(time.Since(now)/time.Millisecond))
if err != nil {
clt.Stats.Incr(pushURL, "failed")
}
}()
}
req, err := http.NewRequest("POST", pushURL, bytes.NewBuffer(b))
req.Header.Add("Charset", "UTF-8")
req.Header.Add("Authorization", clt.Auth)
req.Header.Add("Content-Type", "application/json")
client := &http.Client{Timeout: clt.Timeout}
httpResp, err := client.Do(req)
if err != nil {
return
}
defer httpResp.Body.Close()
return ioutil.ReadAll(httpResp.Body)
}
// GetTimeout .
func (clt *Client) GetTimeout() time.Duration {
return clt.Timeout
}

View File

@@ -0,0 +1,9 @@
package jpush
// Message .
type Message struct {
Title string `json:"title"`
ContentType string `json:"content_type"`
MsgContent string `json:"msg_content"`
Extras interface{} `json:"extras"`
}

View File

@@ -0,0 +1,35 @@
package jpush
// Notification .
type Notification struct {
Android *AndroidNotification `json:"android,omitempty"`
IOS *IOSNotification `json:"ios,omitempty"`
}
// AndroidNotification .
type AndroidNotification struct {
Alert string `json:"alert"`
Title string `json:"title,omitempty"`
AlertType int `json:"alert_type,omitempty"`
BuilderID int `json:"builder_id,omitempty"`
Style int `json:"style,omitempty"`
BigPicPath string `json:"big_pic_path,omitempty"`
Extras interface{} `json:"extras,omitempty"`
}
// IOSAlert .
type IOSAlert struct {
Title string `json:"title"`
Body string `json:"body"`
}
// IOSNotification .
type IOSNotification struct {
Alert interface{} `json:"alert"`
Sound string `json:"sound,omitempty"`
Badge int32 `json:"badge,omitempty"`
ContentAvailable bool `json:"content-available,omitempty"`
MutableContent bool `json:"mutable-content,omitempty"`
Category string `json:"category,omitempty"`
Extras interface{} `json:"extras,omitempty"`
}

View File

@@ -0,0 +1,10 @@
package jpush
// Option .
type Option struct {
SendNo int `json:"sendno,omitempty"`
TimeLive int `json:"time_to_live,omitempty"`
ApnsProduction bool `json:"apns_production"`
OverrideMsgID int64 `json:"override_msg_id,omitempty"`
BigPushDuration int `json:"big_push_duration,omitempty"`
}

View File

@@ -0,0 +1,11 @@
package jpush
// Payload .
type Payload struct {
CID string `json:"cid,omitempty"`
Platform interface{} `json:"platform"`
Audience interface{} `json:"audience"`
Notification interface{} `json:"notification,omitempty"`
Message interface{} `json:"message,omitempty"`
Options *Option `json:"options,omitempty"`
}

View File

@@ -0,0 +1,28 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = ["model.go"],
importpath = "go-common/app/service/bbq/push/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,12 @@
package model
// 推送设备
const (
PlatformAndroid = 1
PlatformIOS = 2
)
// 推送SDK
const (
PushSDKJPush = 1
)

View File

@@ -0,0 +1,34 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = ["server.go"],
importpath = "go-common/app/service/bbq/push/server/grpc",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/service/bbq/push/api/grpc/v1:go_default_library",
"//app/service/bbq/push/service:go_default_library",
"//library/net/rpc/warden:go_default_library",
"@org_golang_google_grpc//: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,28 @@
package grpc
import (
"context"
"go-common/app/service/bbq/push/api/grpc/v1"
"go-common/app/service/bbq/push/service"
"go-common/library/net/rpc/warden"
"google.golang.org/grpc"
)
//New 生成rpc服务
func New(conf *warden.ServerConfig, srv *service.Service) (s *warden.Server, err error) {
s = warden.NewServer(conf)
s.Use(middleware())
v1.RegisterPushServer(s.Server(), srv)
_, err = s.Start()
return
}
func middleware() grpc.UnaryServerInterceptor {
return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) {
//call chain
resp, err = handler(ctx, req)
return
}
}

View File

@@ -0,0 +1,39 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = ["http.go"],
importpath = "go-common/app/service/bbq/push/server/http",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/service/bbq/push/api/grpc/v1:go_default_library",
"//app/service/bbq/push/api/http/v1:go_default_library",
"//app/service/bbq/push/conf:go_default_library",
"//app/service/bbq/push/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",
"//vendor/github.com/json-iterator/go: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,119 @@
package http
import (
grpc "go-common/app/service/bbq/push/api/grpc/v1"
"go-common/app/service/bbq/push/api/http/v1"
"go-common/app/service/bbq/push/conf"
"go-common/app/service/bbq/push/service"
"go-common/library/log"
bm "go-common/library/net/http/blademaster"
"go-common/library/net/http/blademaster/middleware/verify"
"github.com/json-iterator/go"
"github.com/pkg/errors"
)
var (
srv *service.Service
vfy *verify.Verify
)
// Init init
func Init(c *conf.Config, svc *service.Service) {
srv = svc
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("/bbq/internal/push")
{
g.POST("/notice", notice)
g.POST("/message", message)
}
}
func ping(c *bm.Context) {
c.String(0, "pong")
}
func register(c *bm.Context) {
c.JSON(map[string]interface{}{}, nil)
}
func notice(c *bm.Context) {
args := &v1.NoticeRequest{}
if err := c.Bind(args); err != nil {
errors.Wrap(err, "参数验证失败")
return
}
devs := []*grpc.Device{
{
RegisterID: args.RegID,
Platform: args.Platform,
SDK: args.SDK,
SendNo: 0,
},
}
ext := make(map[string]string)
ext["schema"] = args.Schema
ext["callback"] = args.Callback
extBytes, _ := jsoniter.Marshal(ext)
req := &grpc.NotificationRequest{
Dev: devs,
Body: &grpc.NotificationBody{
Title: args.Title,
Content: args.Content,
Extra: string(extBytes),
},
}
resp, err := srv.Notification(c, req)
if err != nil {
log.Errorv(c, log.KV("error", err))
}
c.String(0, resp.String())
}
func message(c *bm.Context) {
args := &v1.MessageRequest{}
if err := c.Bind(args); err != nil {
errors.Wrap(err, "参数验证失败")
return
}
devs := []*grpc.Device{
{
RegisterID: args.RegID,
Platform: args.Platform,
SDK: args.SDK,
SendNo: 0,
},
}
ext := make(map[string]string)
ext["schema"] = args.Schema
ext["callback"] = args.Callback
extBytes, _ := jsoniter.Marshal(ext)
req := &grpc.MessageRequest{
Dev: devs,
Body: &grpc.MessageBody{
Title: args.Title,
Content: args.Content,
ContentType: args.ContentType,
Extra: string(extBytes),
},
}
resp, err := srv.Message(c, req)
if err != nil {
log.Errorv(c, log.KV("error", err))
}
c.String(0, resp.String())
}

View File

@@ -0,0 +1,45 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = [
"message.go",
"notification.go",
"parallel.go",
"service.go",
],
importpath = "go-common/app/service/bbq/push/service",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/service/bbq/push/api/grpc/v1:go_default_library",
"//app/service/bbq/push/conf:go_default_library",
"//app/service/bbq/push/dao:go_default_library",
"//app/service/bbq/push/dao/jpush:go_default_library",
"//app/service/bbq/push/model:go_default_library",
"//library/log:go_default_library",
"//library/log/infoc:go_default_library",
"//library/net/trace:go_default_library",
"//vendor/github.com/Dai0522/workpool:go_default_library",
"//vendor/github.com/json-iterator/go: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,115 @@
package service
import (
"context"
"fmt"
"time"
"go-common/app/service/bbq/push/api/grpc/v1"
"go-common/app/service/bbq/push/conf"
"go-common/app/service/bbq/push/dao"
"go-common/app/service/bbq/push/dao/jpush"
"go-common/app/service/bbq/push/model"
"go-common/library/log"
"go-common/library/net/trace"
"github.com/Dai0522/workpool"
jsoniter "github.com/json-iterator/go"
)
// Message .
func (s *Service) Message(ctx context.Context, in *v1.MessageRequest) (*v1.MessageResponse, error) {
tasks := make([]workpool.Task, len(in.Dev))
for i, dev := range in.Dev {
tasks[i] = NewMessageTask(&ctx, s.c.JPush, s.dao, dev, in.Body)
}
ftasks, err := s.parallel(&ctx, tasks)
if err != nil {
log.Errorv(ctx, log.KV("log", "parallel message error"), log.KV("error", err))
}
result := s.wait(ctx, ftasks)
log.Infov(ctx, log.KV("log", result))
return &v1.MessageResponse{
Result: result,
}, err
}
// AsyncMessage .
func (s *Service) AsyncMessage(ctx context.Context, in *v1.MessageRequest) (*v1.MessageResponse, error) {
tasks := make([]workpool.Task, len(in.Dev))
for i, dev := range in.Dev {
tasks[i] = NewMessageTask(&ctx, s.c.JPush, s.dao, dev, in.Body)
}
_, err := s.parallel(&ctx, tasks)
if err != nil {
log.Errorv(ctx, log.KV("log", "AsyncNotification parallel error"), log.KV("error", err))
}
return &v1.MessageResponse{}, err
}
// MessageTask .
type MessageTask struct {
ctx *context.Context
c *conf.JPushConfig
d *dao.Dao
dev *v1.Device
body *v1.MessageBody
}
// NewMessageTask .
func NewMessageTask(ctx *context.Context, c *conf.JPushConfig, d *dao.Dao, dev *v1.Device, body *v1.MessageBody) *MessageTask {
return &MessageTask{
ctx: ctx,
c: c,
d: d,
dev: dev,
body: body,
}
}
// Run .
func (pt *MessageTask) Run() *[]byte {
ext := make(map[string]string)
jsoniter.Unmarshal([]byte(pt.body.Extra), ext)
tracer, _ := trace.FromContext(*pt.ctx)
ext["callback"] = fmt.Sprintf("http://bbq.bilibili.com/bbq/app-bbq/push/callback?tid=%s&nid=%d", tracer, pt.dev.SendNo)
var platform []string
if pt.dev.Platform == model.PlatformAndroid {
platform = append(platform, "android")
} else {
platform = append(platform, "ios")
}
msg := &jpush.Message{
Title: pt.body.Title,
ContentType: pt.body.ContentType,
MsgContent: pt.body.Content,
Extras: ext,
}
payload := &jpush.Payload{
Platform: platform,
Audience: &jpush.Audience{
RegID: []string{pt.dev.RegisterID},
},
Message: msg,
Options: &jpush.Option{
SendNo: int(pt.dev.SendNo),
ApnsProduction: pt.c.ApnsProduction,
},
}
b, _ := jsoniter.Marshal(payload)
res, _ := pt.d.JPush.Push(b)
// 埋点
result := v1.PushResult{}
result.Unmarshal(res)
Infoc.Info(tracer, "", "", pt.dev.SendNo, "", "", "", time.Now().Unix(), result)
return &res
}

View File

@@ -0,0 +1,133 @@
package service
import (
"context"
"fmt"
"time"
"go-common/app/service/bbq/push/api/grpc/v1"
"go-common/app/service/bbq/push/conf"
"go-common/app/service/bbq/push/dao"
"go-common/app/service/bbq/push/dao/jpush"
"go-common/app/service/bbq/push/model"
"go-common/library/log"
"go-common/library/net/trace"
"github.com/Dai0522/workpool"
"github.com/json-iterator/go"
)
// Notification .
func (s *Service) Notification(ctx context.Context, in *v1.NotificationRequest) (*v1.NotificationResponse, error) {
tasks := make([]workpool.Task, len(in.Dev))
for i, dev := range in.Dev {
tasks[i] = NewNotificationTask(&ctx, s.c.JPush, s.dao, dev, in.Body)
}
ftasks, err := s.parallel(&ctx, tasks)
if err != nil {
log.Errorv(ctx, log.KV("log", "Notification parallel error"), log.KV("error", err))
}
result := s.wait(ctx, ftasks)
log.Infov(ctx, log.KV("log", result))
return &v1.NotificationResponse{
Result: result,
}, err
}
// AsyncNotification .
func (s *Service) AsyncNotification(ctx context.Context, in *v1.NotificationRequest) (*v1.NotificationResponse, error) {
tasks := make([]workpool.Task, len(in.Dev))
for i, dev := range in.Dev {
tasks[i] = NewNotificationTask(&ctx, s.c.JPush, s.dao, dev, in.Body)
}
_, err := s.parallel(&ctx, tasks)
if err != nil {
log.Errorv(ctx, log.KV("log", "AsyncNotification parallel error"), log.KV("error", err))
}
return &v1.NotificationResponse{}, err
}
// NotificationTask .
type NotificationTask struct {
ctx *context.Context
c *conf.JPushConfig
d *dao.Dao
dev *v1.Device
body *v1.NotificationBody
}
// NewNotificationTask .
func NewNotificationTask(ctx *context.Context, c *conf.JPushConfig, d *dao.Dao, dev *v1.Device, body *v1.NotificationBody) *NotificationTask {
return &NotificationTask{
ctx: ctx,
c: c,
d: d,
dev: dev,
body: body,
}
}
// Run .
func (pt *NotificationTask) Run() *[]byte {
ext := make(map[string]string)
err := jsoniter.UnmarshalFromString(pt.body.Extra, &ext)
if err != nil {
log.Error("NotificationTask json Unmarshal error [%+v]", err)
}
tracer, _ := trace.FromContext(*pt.ctx)
ext["callback"] = fmt.Sprintf("http://bbq.bilibili.com/bbq/app-bbq/push/callback?tid=%s&nid=%d", tracer, pt.dev.SendNo)
var platform []string
notify := &jpush.Notification{}
if pt.dev.Platform == model.PlatformAndroid {
platform = append(platform, "android")
notify.Android = &jpush.AndroidNotification{
Alert: pt.body.Content,
Extras: ext,
}
} else {
platform = append(platform, "ios")
var alert interface{}
if pt.body.Title == "" {
alert = pt.body.Content
} else {
alert = &jpush.IOSAlert{
Title: pt.body.Title,
Body: pt.body.Content,
}
}
notify.IOS = &jpush.IOSNotification{
Alert: alert,
Sound: pt.body.Sound,
Badge: pt.body.Badge,
Extras: ext,
}
}
payload := &jpush.Payload{
Platform: platform,
Audience: &jpush.Audience{
RegID: []string{pt.dev.RegisterID},
},
Notification: notify,
Options: &jpush.Option{
SendNo: int(pt.dev.SendNo),
ApnsProduction: pt.c.ApnsProduction,
},
}
b, _ := jsoniter.Marshal(payload)
res, _ := pt.d.JPush.Push(b)
// 埋点
result := v1.PushResult{}
result.Unmarshal(res)
Infoc.Info(tracer, "", "", pt.dev.SendNo, "", "", "", time.Now().Unix(), result)
return &res
}

View File

@@ -0,0 +1,54 @@
package service
import (
"context"
"time"
"go-common/app/service/bbq/push/api/grpc/v1"
"go-common/library/log"
"github.com/Dai0522/workpool"
"github.com/json-iterator/go"
)
func (s *Service) parallel(ctx *context.Context, tasks []workpool.Task) (ftasks []*workpool.FutureTask, err error) {
ftasks = make([]*workpool.FutureTask, len(tasks))
for i := range tasks {
ft := workpool.NewFutureTask(tasks[i])
err = s.wp.Submit(ft)
retry := 0
for err != nil && retry < 3 {
log.Errorv(*ctx, log.KV("workpool", err))
err = s.wp.Submit(ft)
retry++
}
if err != nil {
return
}
ftasks[i] = ft
}
return
}
func (s *Service) wait(ctx context.Context, ftasks []*workpool.FutureTask) []*v1.PushResult {
result := make([]*v1.PushResult, 0)
for _, t := range ftasks {
raw, err := t.Wait(800 * time.Millisecond)
if err != nil || raw == nil || len(*raw) <= 0 {
log.Errorv(ctx, log.KV("future task wait", err))
continue
}
res := &v1.PushResult{}
err = jsoniter.Unmarshal(*raw, res)
if err != nil {
log.Errorv(ctx, log.KV("parse push result", err))
continue
}
result = append(result, res)
}
return result
}

View File

@@ -0,0 +1,50 @@
package service
import (
"time"
"go-common/app/service/bbq/push/conf"
"go-common/app/service/bbq/push/dao"
"go-common/library/log/infoc"
"github.com/Dai0522/workpool"
)
var (
// Infoc lancer data collect
Infoc *infoc.Infoc
)
// Service struct
type Service struct {
c *conf.Config
dao *dao.Dao
wp *workpool.Pool
}
// New init
func New(c *conf.Config) (s *Service) {
workpoolConf := &workpool.PoolConfig{
MaxWorkers: c.WorkPool.MaxWorkers,
MaxIdleWorkers: c.WorkPool.MaxIdleWorkers,
MinIdleWorkers: c.WorkPool.MinIdleWorkers,
KeepAlive: time.Duration(c.WorkPool.KeepAlive),
}
wp, err := workpool.NewWorkerPool(c.WorkPool.Capacity, workpoolConf)
if err != nil {
panic(err)
}
Infoc = infoc.New(c.Infoc)
return &Service{
c: c,
dao: dao.New(c),
wp: wp,
}
}
// Close Service
func (s *Service) Close() {
s.dao.Close()
}