upgrade
This commit is contained in:
@@ -1,45 +0,0 @@
|
||||
package config
|
||||
|
||||
var (
|
||||
ApiAuthConfig = map[string]map[string]string{
|
||||
|
||||
// 调用方
|
||||
"DEMO": {
|
||||
"md5": "IgkibX71IEf382PT",
|
||||
"aes": "IgkibX71IEf382PT",
|
||||
"rsa": "rsa/public.pem",
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
const (
|
||||
AppName = "go-gin-api"
|
||||
|
||||
// 签名超时时间
|
||||
AppSignExpiry = "120"
|
||||
|
||||
// RSA Private File
|
||||
AppRsaPrivateFile = "rsa/private.pem"
|
||||
|
||||
// 日志文件
|
||||
AppErrorLogName = "log/" + AppName + "-error.log"
|
||||
AppGrpcLogName = "log/" + AppName + "-grpc.log"
|
||||
|
||||
// 系统告警邮箱信息
|
||||
SystemEmailUser = "xinliangnote@163.com"
|
||||
SystemEmailPass = "" //密码或授权码
|
||||
SystemEmailHost = "smtp.163.com"
|
||||
SystemEmailPort = 465
|
||||
|
||||
// 告警接收人
|
||||
ErrorNotifyUser = "xinliangnote@163.com"
|
||||
|
||||
// 告警开关 1=开通 -1=关闭
|
||||
ErrorNotifyOpen = -1
|
||||
|
||||
// Jaeger 配置信息
|
||||
JaegerHostPort = "172.20.2.212:6831"
|
||||
|
||||
// Jaeger 配置开关 1=开通 -1=关闭
|
||||
JaegerOpen = 1
|
||||
)
|
||||
@@ -101,7 +101,8 @@ type response []struct {
|
||||
// @Tags Demo
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param name path string true "用户名"
|
||||
// @Param name path string true "用户名(Tom)"
|
||||
// @Param Authorization header string true "签名"
|
||||
// @Success 200 {object} response "用户信息"
|
||||
// @Router /demo/user/{name} [get]
|
||||
func (d *Demo) User() core.HandlerFunc {
|
||||
@@ -121,6 +122,7 @@ func (d *Demo) User() core.HandlerFunc {
|
||||
httpclient.WithTTL(time.Second*2),
|
||||
httpclient.WithJournal(c.Journal()),
|
||||
httpclient.WithLogger(c.Logger()),
|
||||
httpclient.WithHeader("Authorization", c.GetHeader("Authorization")),
|
||||
)
|
||||
if err1 != nil {
|
||||
d.logger.Error("get [demo/get] err", zap.Error(err1))
|
||||
@@ -132,22 +134,25 @@ func (d *Demo) User() core.HandlerFunc {
|
||||
httpclient.WithTTL(time.Second*2),
|
||||
httpclient.WithJournal(c.Journal()),
|
||||
httpclient.WithLogger(c.Logger()),
|
||||
httpclient.WithHeader("Authorization", c.GetHeader("Authorization")),
|
||||
)
|
||||
if err2 != nil {
|
||||
d.logger.Error("post [demo/post] err", zap.Error(err2))
|
||||
}
|
||||
|
||||
data := &response{
|
||||
{
|
||||
Name: jsonparse.Get(string(body1), "data.name").(string),
|
||||
Job: jsonparse.Get(string(body1), "data.job").(string),
|
||||
},
|
||||
{
|
||||
Name: jsonparse.Get(string(body2), "data.name").(string),
|
||||
Job: jsonparse.Get(string(body2), "data.job").(string),
|
||||
},
|
||||
data := &response{}
|
||||
if err1 == nil && err2 == nil {
|
||||
data = &response{
|
||||
{
|
||||
Name: jsonparse.Get(string(body1), "data.name").(string),
|
||||
Job: jsonparse.Get(string(body1), "data.job").(string),
|
||||
},
|
||||
{
|
||||
Name: jsonparse.Get(string(body2), "data.name").(string),
|
||||
Job: jsonparse.Get(string(body2), "data.job").(string),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
c.SetPayload(errno.OK.WithData(data))
|
||||
}
|
||||
}
|
||||
@@ -158,9 +163,9 @@ func (d *Demo) RsaTest() core.HandlerFunc {
|
||||
encryptStr := "param_1=xxx¶m_2=xxx&ak=xxx&ts=1111111111"
|
||||
count := 500
|
||||
|
||||
cfg := configs.Get()
|
||||
rsaPublic := rsa.NewPublic(cfg.Rsa.Public)
|
||||
rsaPrivate := rsa.NewPrivate(cfg.Rsa.Private)
|
||||
cfg := configs.Get().Rsa
|
||||
rsaPublic := rsa.NewPublic(cfg.Public)
|
||||
rsaPrivate := rsa.NewPrivate(cfg.Private)
|
||||
|
||||
for i := 0; i < count; i++ {
|
||||
// 生成签名
|
||||
@@ -187,8 +192,8 @@ func (d *Demo) AesTest() core.HandlerFunc {
|
||||
encryptStr := "param_1=xxx¶m_2=xxx&ak=xxx&ts=1111111111"
|
||||
count := 1000000
|
||||
|
||||
cfg := configs.Get()
|
||||
aes := aes.New(cfg.Aes.Key, cfg.Aes.Iv)
|
||||
cfg := configs.Get().Aes
|
||||
aes := aes.New(cfg.Key, cfg.Iv)
|
||||
for i := 0; i < count; i++ {
|
||||
// 生成签名
|
||||
sn, err := aes.Encrypt(encryptStr)
|
||||
|
||||
47
internal/api/controller/demo/login.go
Normal file
47
internal/api/controller/demo/login.go
Normal file
@@ -0,0 +1,47 @@
|
||||
package demo
|
||||
|
||||
import (
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/errno"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/token"
|
||||
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type loginRequest struct {
|
||||
UserID int `json:"user_id" form:"user_id"` // 用户ID(>0)
|
||||
UserName string `json:"user_name" form:"user_name"` // 用户名
|
||||
}
|
||||
|
||||
type loginResponse struct {
|
||||
Authorization string `json:"authorization"` // 签名
|
||||
}
|
||||
|
||||
// 登录获取 Authorization 码
|
||||
// @Summary 登录获取 Authorization 码
|
||||
// @Description 登录获取 Authorization 码
|
||||
// @Tags Demo
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param loginRequest body loginRequest true "请求信息"
|
||||
// @Success 200 {object} loginResponse "签名信息"
|
||||
// @Router /user/login [post]
|
||||
func (d *Demo) Login() core.HandlerFunc {
|
||||
return func(c core.Context) {
|
||||
req := new(loginRequest)
|
||||
res := new(loginResponse)
|
||||
if err := c.ShouldBindJSON(req); err != nil {
|
||||
c.SetPayload(errno.ErrParam)
|
||||
return
|
||||
}
|
||||
|
||||
tokenString, err := token.Sign(req.UserID, req.UserName)
|
||||
if err != nil {
|
||||
d.logger.Error("token sign err", zap.Error(err))
|
||||
res.Authorization = ""
|
||||
} else {
|
||||
res.Authorization = tokenString
|
||||
}
|
||||
c.SetPayload(errno.OK.WithData(res))
|
||||
}
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
package jaeger_conn
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/xinliangnote/go-gin-api/internal/api/model/proto/listen"
|
||||
"github.com/xinliangnote/go-gin-api/internal/api/model/proto/read"
|
||||
"github.com/xinliangnote/go-gin-api/internal/api/model/proto/speak"
|
||||
"github.com/xinliangnote/go-gin-api/internal/api/model/proto/write"
|
||||
"github.com/xinliangnote/go-gin-api/internal/api/util/grpc_client"
|
||||
"github.com/xinliangnote/go-gin-api/internal/api/util/request"
|
||||
"github.com/xinliangnote/go-gin-api/internal/api/util/response"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func JaegerTest(c *gin.Context) {
|
||||
|
||||
// 调用 gRPC 服务
|
||||
conn := grpc_client.CreateServiceListenConn(c)
|
||||
grpcListenClient := listen.NewListenClient(conn)
|
||||
resListen, _ := grpcListenClient.ListenData(context.Background(), &listen.Request{Name: "listen"})
|
||||
|
||||
// 调用 gRPC 服务
|
||||
conn = grpc_client.CreateServiceSpeakConn(c)
|
||||
grpcSpeakClient := speak.NewSpeakClient(conn)
|
||||
resSpeak, _ := grpcSpeakClient.SpeakData(context.Background(), &speak.Request{Name: "speak"})
|
||||
|
||||
// 调用 gRPC 服务
|
||||
conn = grpc_client.CreateServiceReadConn(c)
|
||||
grpcReadClient := read.NewReadClient(conn)
|
||||
resRead, _ := grpcReadClient.ReadData(context.Background(), &read.Request{Name: "read"})
|
||||
|
||||
// 调用 gRPC 服务
|
||||
conn = grpc_client.CreateServiceWriteConn(c)
|
||||
grpcWriteClient := write.NewWriteClient(conn)
|
||||
resWrite, _ := grpcWriteClient.WriteData(context.Background(), &write.Request{Name: "write"})
|
||||
|
||||
defer conn.Close()
|
||||
|
||||
// 调用 HTTP 服务
|
||||
resHttpGet := ""
|
||||
_, err := request.HttpGet("http://localhost:9905/sing", c)
|
||||
if err == nil {
|
||||
resHttpGet = "[HttpGetOk]"
|
||||
}
|
||||
|
||||
// 业务处理...
|
||||
|
||||
msg := resListen.Message + "-" +
|
||||
resSpeak.Message + "-" +
|
||||
resRead.Message + "-" +
|
||||
resWrite.Message + "-" +
|
||||
resHttpGet
|
||||
|
||||
utilGin := response.Gin{Ctx: c}
|
||||
utilGin.Response(1, msg, nil)
|
||||
}
|
||||
@@ -1,208 +0,0 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// source: listen.proto
|
||||
|
||||
package listen
|
||||
|
||||
import (
|
||||
context "context"
|
||||
fmt "fmt"
|
||||
math "math"
|
||||
|
||||
proto "github.com/golang/protobuf/proto"
|
||||
grpc "google.golang.org/grpc"
|
||||
codes "google.golang.org/grpc/codes"
|
||||
status "google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ = proto.Marshal
|
||||
var _ = fmt.Errorf
|
||||
var _ = math.Inf
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the proto package it is being compiled against.
|
||||
// A compilation error at this line likely means your copy of the
|
||||
// proto package needs to be updated.
|
||||
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
|
||||
|
||||
// Request 请求结构
|
||||
type Request struct {
|
||||
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *Request) Reset() { *m = Request{} }
|
||||
func (m *Request) String() string { return proto.CompactTextString(m) }
|
||||
func (*Request) ProtoMessage() {}
|
||||
func (*Request) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_f05da38a4a3e5177, []int{0}
|
||||
}
|
||||
|
||||
func (m *Request) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_Request.Unmarshal(m, b)
|
||||
}
|
||||
func (m *Request) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_Request.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (m *Request) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_Request.Merge(m, src)
|
||||
}
|
||||
func (m *Request) XXX_Size() int {
|
||||
return xxx_messageInfo_Request.Size(m)
|
||||
}
|
||||
func (m *Request) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_Request.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_Request proto.InternalMessageInfo
|
||||
|
||||
func (m *Request) GetName() string {
|
||||
if m != nil {
|
||||
return m.Name
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// Response 响应结构
|
||||
type Response struct {
|
||||
Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *Response) Reset() { *m = Response{} }
|
||||
func (m *Response) String() string { return proto.CompactTextString(m) }
|
||||
func (*Response) ProtoMessage() {}
|
||||
func (*Response) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_f05da38a4a3e5177, []int{1}
|
||||
}
|
||||
|
||||
func (m *Response) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_Response.Unmarshal(m, b)
|
||||
}
|
||||
func (m *Response) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_Response.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (m *Response) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_Response.Merge(m, src)
|
||||
}
|
||||
func (m *Response) XXX_Size() int {
|
||||
return xxx_messageInfo_Response.Size(m)
|
||||
}
|
||||
func (m *Response) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_Response.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_Response proto.InternalMessageInfo
|
||||
|
||||
func (m *Response) GetMessage() string {
|
||||
if m != nil {
|
||||
return m.Message
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func init() {
|
||||
proto.RegisterType((*Request)(nil), "listen.Request")
|
||||
proto.RegisterType((*Response)(nil), "listen.Response")
|
||||
}
|
||||
|
||||
func init() { proto.RegisterFile("listen.proto", fileDescriptor_f05da38a4a3e5177) }
|
||||
|
||||
var fileDescriptor_f05da38a4a3e5177 = []byte{
|
||||
// 133 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0xc9, 0xc9, 0x2c, 0x2e,
|
||||
0x49, 0xcd, 0xd3, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x62, 0x83, 0xf0, 0x94, 0x64, 0xb9, 0xd8,
|
||||
0x83, 0x52, 0x0b, 0x4b, 0x53, 0x8b, 0x4b, 0x84, 0x84, 0xb8, 0x58, 0xf2, 0x12, 0x73, 0x53, 0x25,
|
||||
0x18, 0x15, 0x18, 0x35, 0x38, 0x83, 0xc0, 0x6c, 0x25, 0x15, 0x2e, 0x8e, 0xa0, 0xd4, 0xe2, 0x82,
|
||||
0xfc, 0xbc, 0xe2, 0x54, 0x21, 0x09, 0x2e, 0xf6, 0xdc, 0xd4, 0xe2, 0xe2, 0xc4, 0x74, 0x98, 0x12,
|
||||
0x18, 0xd7, 0xc8, 0x9a, 0x8b, 0xcd, 0x07, 0x6c, 0x9c, 0x90, 0x21, 0x17, 0x17, 0x84, 0xe5, 0x92,
|
||||
0x58, 0x92, 0x28, 0xc4, 0xaf, 0x07, 0xb5, 0x13, 0x6a, 0x85, 0x94, 0x00, 0x42, 0x00, 0x62, 0xa8,
|
||||
0x12, 0x43, 0x12, 0x1b, 0xd8, 0x41, 0xc6, 0x80, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcb, 0xbf, 0x9d,
|
||||
0x0d, 0xa0, 0x00, 0x00, 0x00,
|
||||
}
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ context.Context
|
||||
var _ grpc.ClientConn
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the grpc package it is being compiled against.
|
||||
const _ = grpc.SupportPackageIsVersion4
|
||||
|
||||
// ListenClient is the client API for Listen service.
|
||||
//
|
||||
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
|
||||
type ListenClient interface {
|
||||
// 定义 ListenData 方法
|
||||
ListenData(ctx context.Context, in *Request, opts ...grpc.CallOption) (*Response, error)
|
||||
}
|
||||
|
||||
type listenClient struct {
|
||||
cc *grpc.ClientConn
|
||||
}
|
||||
|
||||
func NewListenClient(cc *grpc.ClientConn) ListenClient {
|
||||
return &listenClient{cc}
|
||||
}
|
||||
|
||||
func (c *listenClient) ListenData(ctx context.Context, in *Request, opts ...grpc.CallOption) (*Response, error) {
|
||||
out := new(Response)
|
||||
err := c.cc.Invoke(ctx, "/listen.Listen/ListenData", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// ListenServer is the server API for Listen service.
|
||||
type ListenServer interface {
|
||||
// 定义 ListenData 方法
|
||||
ListenData(context.Context, *Request) (*Response, error)
|
||||
}
|
||||
|
||||
// UnimplementedListenServer can be embedded to have forward compatible implementations.
|
||||
type UnimplementedListenServer struct {
|
||||
}
|
||||
|
||||
func (*UnimplementedListenServer) ListenData(ctx context.Context, req *Request) (*Response, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method ListenData not implemented")
|
||||
}
|
||||
|
||||
func RegisterListenServer(s *grpc.Server, srv ListenServer) {
|
||||
s.RegisterService(&_Listen_serviceDesc, srv)
|
||||
}
|
||||
|
||||
func _Listen_ListenData_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(Request)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(ListenServer).ListenData(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/listen.Listen/ListenData",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(ListenServer).ListenData(ctx, req.(*Request))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
var _Listen_serviceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "listen.Listen",
|
||||
HandlerType: (*ListenServer)(nil),
|
||||
Methods: []grpc.MethodDesc{
|
||||
{
|
||||
MethodName: "ListenData",
|
||||
Handler: _Listen_ListenData_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{},
|
||||
Metadata: "listen.proto",
|
||||
}
|
||||
@@ -1,208 +0,0 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// source: read.proto
|
||||
|
||||
package read
|
||||
|
||||
import (
|
||||
context "context"
|
||||
fmt "fmt"
|
||||
math "math"
|
||||
|
||||
proto "github.com/golang/protobuf/proto"
|
||||
grpc "google.golang.org/grpc"
|
||||
codes "google.golang.org/grpc/codes"
|
||||
status "google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ = proto.Marshal
|
||||
var _ = fmt.Errorf
|
||||
var _ = math.Inf
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the proto package it is being compiled against.
|
||||
// A compilation error at this line likely means your copy of the
|
||||
// proto package needs to be updated.
|
||||
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
|
||||
|
||||
// Request 请求结构
|
||||
type Request struct {
|
||||
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *Request) Reset() { *m = Request{} }
|
||||
func (m *Request) String() string { return proto.CompactTextString(m) }
|
||||
func (*Request) ProtoMessage() {}
|
||||
func (*Request) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_7b10ec61df6818dd, []int{0}
|
||||
}
|
||||
|
||||
func (m *Request) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_Request.Unmarshal(m, b)
|
||||
}
|
||||
func (m *Request) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_Request.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (m *Request) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_Request.Merge(m, src)
|
||||
}
|
||||
func (m *Request) XXX_Size() int {
|
||||
return xxx_messageInfo_Request.Size(m)
|
||||
}
|
||||
func (m *Request) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_Request.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_Request proto.InternalMessageInfo
|
||||
|
||||
func (m *Request) GetName() string {
|
||||
if m != nil {
|
||||
return m.Name
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// Response 响应结构
|
||||
type Response struct {
|
||||
Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *Response) Reset() { *m = Response{} }
|
||||
func (m *Response) String() string { return proto.CompactTextString(m) }
|
||||
func (*Response) ProtoMessage() {}
|
||||
func (*Response) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_7b10ec61df6818dd, []int{1}
|
||||
}
|
||||
|
||||
func (m *Response) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_Response.Unmarshal(m, b)
|
||||
}
|
||||
func (m *Response) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_Response.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (m *Response) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_Response.Merge(m, src)
|
||||
}
|
||||
func (m *Response) XXX_Size() int {
|
||||
return xxx_messageInfo_Response.Size(m)
|
||||
}
|
||||
func (m *Response) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_Response.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_Response proto.InternalMessageInfo
|
||||
|
||||
func (m *Response) GetMessage() string {
|
||||
if m != nil {
|
||||
return m.Message
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func init() {
|
||||
proto.RegisterType((*Request)(nil), "read.Request")
|
||||
proto.RegisterType((*Response)(nil), "read.Response")
|
||||
}
|
||||
|
||||
func init() { proto.RegisterFile("read.proto", fileDescriptor_7b10ec61df6818dd) }
|
||||
|
||||
var fileDescriptor_7b10ec61df6818dd = []byte{
|
||||
// 132 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0x2a, 0x4a, 0x4d, 0x4c,
|
||||
0xd1, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x62, 0x01, 0xb1, 0x95, 0x64, 0xb9, 0xd8, 0x83, 0x52,
|
||||
0x0b, 0x4b, 0x53, 0x8b, 0x4b, 0x84, 0x84, 0xb8, 0x58, 0xf2, 0x12, 0x73, 0x53, 0x25, 0x18, 0x15,
|
||||
0x18, 0x35, 0x38, 0x83, 0xc0, 0x6c, 0x25, 0x15, 0x2e, 0x8e, 0xa0, 0xd4, 0xe2, 0x82, 0xfc, 0xbc,
|
||||
0xe2, 0x54, 0x21, 0x09, 0x2e, 0xf6, 0xdc, 0xd4, 0xe2, 0xe2, 0xc4, 0x74, 0x98, 0x12, 0x18, 0xd7,
|
||||
0xc8, 0x98, 0x8b, 0x25, 0x28, 0x35, 0x31, 0x45, 0x48, 0x1b, 0xa4, 0x3a, 0x31, 0xc5, 0x25, 0xb1,
|
||||
0x24, 0x51, 0x88, 0x57, 0x0f, 0x6c, 0x17, 0xd4, 0x70, 0x29, 0x3e, 0x18, 0x17, 0x62, 0x98, 0x12,
|
||||
0x43, 0x12, 0x1b, 0xd8, 0x19, 0xc6, 0x80, 0x00, 0x00, 0x00, 0xff, 0xff, 0x42, 0x4a, 0x9b, 0x2d,
|
||||
0x94, 0x00, 0x00, 0x00,
|
||||
}
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ context.Context
|
||||
var _ grpc.ClientConn
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the grpc package it is being compiled against.
|
||||
const _ = grpc.SupportPackageIsVersion4
|
||||
|
||||
// ReadClient is the client API for Read service.
|
||||
//
|
||||
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
|
||||
type ReadClient interface {
|
||||
// 定义方法
|
||||
ReadData(ctx context.Context, in *Request, opts ...grpc.CallOption) (*Response, error)
|
||||
}
|
||||
|
||||
type readClient struct {
|
||||
cc *grpc.ClientConn
|
||||
}
|
||||
|
||||
func NewReadClient(cc *grpc.ClientConn) ReadClient {
|
||||
return &readClient{cc}
|
||||
}
|
||||
|
||||
func (c *readClient) ReadData(ctx context.Context, in *Request, opts ...grpc.CallOption) (*Response, error) {
|
||||
out := new(Response)
|
||||
err := c.cc.Invoke(ctx, "/read.Read/ReadData", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// ReadServer is the server API for Read service.
|
||||
type ReadServer interface {
|
||||
// 定义方法
|
||||
ReadData(context.Context, *Request) (*Response, error)
|
||||
}
|
||||
|
||||
// UnimplementedReadServer can be embedded to have forward compatible implementations.
|
||||
type UnimplementedReadServer struct {
|
||||
}
|
||||
|
||||
func (*UnimplementedReadServer) ReadData(ctx context.Context, req *Request) (*Response, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method ReadData not implemented")
|
||||
}
|
||||
|
||||
func RegisterReadServer(s *grpc.Server, srv ReadServer) {
|
||||
s.RegisterService(&_Read_serviceDesc, srv)
|
||||
}
|
||||
|
||||
func _Read_ReadData_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(Request)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(ReadServer).ReadData(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/read.Read/ReadData",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(ReadServer).ReadData(ctx, req.(*Request))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
var _Read_serviceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "read.Read",
|
||||
HandlerType: (*ReadServer)(nil),
|
||||
Methods: []grpc.MethodDesc{
|
||||
{
|
||||
MethodName: "ReadData",
|
||||
Handler: _Read_ReadData_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{},
|
||||
Metadata: "read.proto",
|
||||
}
|
||||
@@ -1,208 +0,0 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// source: speak.proto
|
||||
|
||||
package speak
|
||||
|
||||
import (
|
||||
context "context"
|
||||
fmt "fmt"
|
||||
math "math"
|
||||
|
||||
proto "github.com/golang/protobuf/proto"
|
||||
grpc "google.golang.org/grpc"
|
||||
codes "google.golang.org/grpc/codes"
|
||||
status "google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ = proto.Marshal
|
||||
var _ = fmt.Errorf
|
||||
var _ = math.Inf
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the proto package it is being compiled against.
|
||||
// A compilation error at this line likely means your copy of the
|
||||
// proto package needs to be updated.
|
||||
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
|
||||
|
||||
// Request 请求结构
|
||||
type Request struct {
|
||||
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *Request) Reset() { *m = Request{} }
|
||||
func (m *Request) String() string { return proto.CompactTextString(m) }
|
||||
func (*Request) ProtoMessage() {}
|
||||
func (*Request) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_3c7d3e2f29338937, []int{0}
|
||||
}
|
||||
|
||||
func (m *Request) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_Request.Unmarshal(m, b)
|
||||
}
|
||||
func (m *Request) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_Request.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (m *Request) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_Request.Merge(m, src)
|
||||
}
|
||||
func (m *Request) XXX_Size() int {
|
||||
return xxx_messageInfo_Request.Size(m)
|
||||
}
|
||||
func (m *Request) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_Request.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_Request proto.InternalMessageInfo
|
||||
|
||||
func (m *Request) GetName() string {
|
||||
if m != nil {
|
||||
return m.Name
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// Response 响应结构
|
||||
type Response struct {
|
||||
Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *Response) Reset() { *m = Response{} }
|
||||
func (m *Response) String() string { return proto.CompactTextString(m) }
|
||||
func (*Response) ProtoMessage() {}
|
||||
func (*Response) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_3c7d3e2f29338937, []int{1}
|
||||
}
|
||||
|
||||
func (m *Response) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_Response.Unmarshal(m, b)
|
||||
}
|
||||
func (m *Response) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_Response.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (m *Response) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_Response.Merge(m, src)
|
||||
}
|
||||
func (m *Response) XXX_Size() int {
|
||||
return xxx_messageInfo_Response.Size(m)
|
||||
}
|
||||
func (m *Response) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_Response.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_Response proto.InternalMessageInfo
|
||||
|
||||
func (m *Response) GetMessage() string {
|
||||
if m != nil {
|
||||
return m.Message
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func init() {
|
||||
proto.RegisterType((*Request)(nil), "speak.Request")
|
||||
proto.RegisterType((*Response)(nil), "speak.Response")
|
||||
}
|
||||
|
||||
func init() { proto.RegisterFile("speak.proto", fileDescriptor_3c7d3e2f29338937) }
|
||||
|
||||
var fileDescriptor_3c7d3e2f29338937 = []byte{
|
||||
// 132 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0x2e, 0x2e, 0x48, 0x4d,
|
||||
0xcc, 0xd6, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x62, 0x05, 0x73, 0x94, 0x64, 0xb9, 0xd8, 0x83,
|
||||
0x52, 0x0b, 0x4b, 0x53, 0x8b, 0x4b, 0x84, 0x84, 0xb8, 0x58, 0xf2, 0x12, 0x73, 0x53, 0x25, 0x18,
|
||||
0x15, 0x18, 0x35, 0x38, 0x83, 0xc0, 0x6c, 0x25, 0x15, 0x2e, 0x8e, 0xa0, 0xd4, 0xe2, 0x82, 0xfc,
|
||||
0xbc, 0xe2, 0x54, 0x21, 0x09, 0x2e, 0xf6, 0xdc, 0xd4, 0xe2, 0xe2, 0xc4, 0x74, 0x98, 0x12, 0x18,
|
||||
0xd7, 0xc8, 0x9c, 0x8b, 0x35, 0x18, 0x64, 0x9a, 0x90, 0x1e, 0x17, 0x27, 0x98, 0xe1, 0x92, 0x58,
|
||||
0x92, 0x28, 0xc4, 0xa7, 0x07, 0xb1, 0x0f, 0x6a, 0xbe, 0x14, 0x3f, 0x9c, 0x0f, 0x31, 0x50, 0x89,
|
||||
0x21, 0x89, 0x0d, 0xec, 0x16, 0x63, 0x40, 0x00, 0x00, 0x00, 0xff, 0xff, 0x4d, 0x04, 0x6f, 0x05,
|
||||
0x9a, 0x00, 0x00, 0x00,
|
||||
}
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ context.Context
|
||||
var _ grpc.ClientConn
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the grpc package it is being compiled against.
|
||||
const _ = grpc.SupportPackageIsVersion4
|
||||
|
||||
// SpeakClient is the client API for Speak service.
|
||||
//
|
||||
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
|
||||
type SpeakClient interface {
|
||||
// 定义方法
|
||||
SpeakData(ctx context.Context, in *Request, opts ...grpc.CallOption) (*Response, error)
|
||||
}
|
||||
|
||||
type speakClient struct {
|
||||
cc *grpc.ClientConn
|
||||
}
|
||||
|
||||
func NewSpeakClient(cc *grpc.ClientConn) SpeakClient {
|
||||
return &speakClient{cc}
|
||||
}
|
||||
|
||||
func (c *speakClient) SpeakData(ctx context.Context, in *Request, opts ...grpc.CallOption) (*Response, error) {
|
||||
out := new(Response)
|
||||
err := c.cc.Invoke(ctx, "/speak.Speak/SpeakData", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// SpeakServer is the server API for Speak service.
|
||||
type SpeakServer interface {
|
||||
// 定义方法
|
||||
SpeakData(context.Context, *Request) (*Response, error)
|
||||
}
|
||||
|
||||
// UnimplementedSpeakServer can be embedded to have forward compatible implementations.
|
||||
type UnimplementedSpeakServer struct {
|
||||
}
|
||||
|
||||
func (*UnimplementedSpeakServer) SpeakData(ctx context.Context, req *Request) (*Response, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method SpeakData not implemented")
|
||||
}
|
||||
|
||||
func RegisterSpeakServer(s *grpc.Server, srv SpeakServer) {
|
||||
s.RegisterService(&_Speak_serviceDesc, srv)
|
||||
}
|
||||
|
||||
func _Speak_SpeakData_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(Request)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(SpeakServer).SpeakData(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/speak.Speak/SpeakData",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(SpeakServer).SpeakData(ctx, req.(*Request))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
var _Speak_serviceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "speak.Speak",
|
||||
HandlerType: (*SpeakServer)(nil),
|
||||
Methods: []grpc.MethodDesc{
|
||||
{
|
||||
MethodName: "SpeakData",
|
||||
Handler: _Speak_SpeakData_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{},
|
||||
Metadata: "speak.proto",
|
||||
}
|
||||
@@ -1,208 +0,0 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// source: write.proto
|
||||
|
||||
package write
|
||||
|
||||
import (
|
||||
context "context"
|
||||
fmt "fmt"
|
||||
math "math"
|
||||
|
||||
proto "github.com/golang/protobuf/proto"
|
||||
grpc "google.golang.org/grpc"
|
||||
codes "google.golang.org/grpc/codes"
|
||||
status "google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ = proto.Marshal
|
||||
var _ = fmt.Errorf
|
||||
var _ = math.Inf
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the proto package it is being compiled against.
|
||||
// A compilation error at this line likely means your copy of the
|
||||
// proto package needs to be updated.
|
||||
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
|
||||
|
||||
// Request 请求结构
|
||||
type Request struct {
|
||||
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *Request) Reset() { *m = Request{} }
|
||||
func (m *Request) String() string { return proto.CompactTextString(m) }
|
||||
func (*Request) ProtoMessage() {}
|
||||
func (*Request) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_67966b2b12a73214, []int{0}
|
||||
}
|
||||
|
||||
func (m *Request) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_Request.Unmarshal(m, b)
|
||||
}
|
||||
func (m *Request) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_Request.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (m *Request) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_Request.Merge(m, src)
|
||||
}
|
||||
func (m *Request) XXX_Size() int {
|
||||
return xxx_messageInfo_Request.Size(m)
|
||||
}
|
||||
func (m *Request) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_Request.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_Request proto.InternalMessageInfo
|
||||
|
||||
func (m *Request) GetName() string {
|
||||
if m != nil {
|
||||
return m.Name
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// Response 响应结构
|
||||
type Response struct {
|
||||
Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *Response) Reset() { *m = Response{} }
|
||||
func (m *Response) String() string { return proto.CompactTextString(m) }
|
||||
func (*Response) ProtoMessage() {}
|
||||
func (*Response) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_67966b2b12a73214, []int{1}
|
||||
}
|
||||
|
||||
func (m *Response) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_Response.Unmarshal(m, b)
|
||||
}
|
||||
func (m *Response) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_Response.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (m *Response) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_Response.Merge(m, src)
|
||||
}
|
||||
func (m *Response) XXX_Size() int {
|
||||
return xxx_messageInfo_Response.Size(m)
|
||||
}
|
||||
func (m *Response) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_Response.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_Response proto.InternalMessageInfo
|
||||
|
||||
func (m *Response) GetMessage() string {
|
||||
if m != nil {
|
||||
return m.Message
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func init() {
|
||||
proto.RegisterType((*Request)(nil), "write.Request")
|
||||
proto.RegisterType((*Response)(nil), "write.Response")
|
||||
}
|
||||
|
||||
func init() { proto.RegisterFile("write.proto", fileDescriptor_67966b2b12a73214) }
|
||||
|
||||
var fileDescriptor_67966b2b12a73214 = []byte{
|
||||
// 132 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0x2e, 0x2f, 0xca, 0x2c,
|
||||
0x49, 0xd5, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x62, 0x05, 0x73, 0x94, 0x64, 0xb9, 0xd8, 0x83,
|
||||
0x52, 0x0b, 0x4b, 0x53, 0x8b, 0x4b, 0x84, 0x84, 0xb8, 0x58, 0xf2, 0x12, 0x73, 0x53, 0x25, 0x18,
|
||||
0x15, 0x18, 0x35, 0x38, 0x83, 0xc0, 0x6c, 0x25, 0x15, 0x2e, 0x8e, 0xa0, 0xd4, 0xe2, 0x82, 0xfc,
|
||||
0xbc, 0xe2, 0x54, 0x21, 0x09, 0x2e, 0xf6, 0xdc, 0xd4, 0xe2, 0xe2, 0xc4, 0x74, 0x98, 0x12, 0x18,
|
||||
0xd7, 0xc8, 0x9c, 0x8b, 0x35, 0x1c, 0x64, 0x9a, 0x90, 0x1e, 0x17, 0x27, 0x98, 0xe1, 0x92, 0x58,
|
||||
0x92, 0x28, 0xc4, 0xa7, 0x07, 0xb1, 0x0f, 0x6a, 0xbe, 0x14, 0x3f, 0x9c, 0x0f, 0x31, 0x50, 0x89,
|
||||
0x21, 0x89, 0x0d, 0xec, 0x16, 0x63, 0x40, 0x00, 0x00, 0x00, 0xff, 0xff, 0xb3, 0x7d, 0xc4, 0x7d,
|
||||
0x9a, 0x00, 0x00, 0x00,
|
||||
}
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ context.Context
|
||||
var _ grpc.ClientConn
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the grpc package it is being compiled against.
|
||||
const _ = grpc.SupportPackageIsVersion4
|
||||
|
||||
// WriteClient is the client API for Write service.
|
||||
//
|
||||
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
|
||||
type WriteClient interface {
|
||||
// 定义方法
|
||||
WriteData(ctx context.Context, in *Request, opts ...grpc.CallOption) (*Response, error)
|
||||
}
|
||||
|
||||
type writeClient struct {
|
||||
cc *grpc.ClientConn
|
||||
}
|
||||
|
||||
func NewWriteClient(cc *grpc.ClientConn) WriteClient {
|
||||
return &writeClient{cc}
|
||||
}
|
||||
|
||||
func (c *writeClient) WriteData(ctx context.Context, in *Request, opts ...grpc.CallOption) (*Response, error) {
|
||||
out := new(Response)
|
||||
err := c.cc.Invoke(ctx, "/write.Write/WriteData", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// WriteServer is the server API for Write service.
|
||||
type WriteServer interface {
|
||||
// 定义方法
|
||||
WriteData(context.Context, *Request) (*Response, error)
|
||||
}
|
||||
|
||||
// UnimplementedWriteServer can be embedded to have forward compatible implementations.
|
||||
type UnimplementedWriteServer struct {
|
||||
}
|
||||
|
||||
func (*UnimplementedWriteServer) WriteData(ctx context.Context, req *Request) (*Response, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method WriteData not implemented")
|
||||
}
|
||||
|
||||
func RegisterWriteServer(s *grpc.Server, srv WriteServer) {
|
||||
s.RegisterService(&_Write_serviceDesc, srv)
|
||||
}
|
||||
|
||||
func _Write_WriteData_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(Request)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(WriteServer).WriteData(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/write.Write/WriteData",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(WriteServer).WriteData(ctx, req.(*Request))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
var _Write_serviceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "write.Write",
|
||||
HandlerType: (*WriteServer)(nil),
|
||||
Methods: []grpc.MethodDesc{
|
||||
{
|
||||
MethodName: "WriteData",
|
||||
Handler: _Write_WriteData_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{},
|
||||
Metadata: "write.proto",
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
package exception
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"runtime/debug"
|
||||
"strings"
|
||||
|
||||
"github.com/xinliangnote/go-gin-api/internal/api/config"
|
||||
"github.com/xinliangnote/go-gin-api/internal/api/util/response"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/xinliangnote/go-util/mail"
|
||||
"github.com/xinliangnote/go-util/time"
|
||||
)
|
||||
|
||||
func SetUp() gin.HandlerFunc {
|
||||
|
||||
return func(c *gin.Context) {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
|
||||
DebugStack := ""
|
||||
for _, v := range strings.Split(string(debug.Stack()), "\n") {
|
||||
DebugStack += v + "<br>"
|
||||
}
|
||||
|
||||
subject := fmt.Sprintf("【重要错误】%s 项目出错了!", config.AppName)
|
||||
|
||||
body := strings.ReplaceAll(MailTemplate, "{ErrorMsg}", fmt.Sprintf("%s", err))
|
||||
body = strings.ReplaceAll(body, "{RequestTime}", time.GetCurrentDate())
|
||||
body = strings.ReplaceAll(body, "{RequestURL}", c.Request.Method+" "+c.Request.Host+c.Request.RequestURI)
|
||||
body = strings.ReplaceAll(body, "{RequestUA}", c.Request.UserAgent())
|
||||
body = strings.ReplaceAll(body, "{RequestIP}", c.ClientIP())
|
||||
body = strings.ReplaceAll(body, "{DebugStack}", DebugStack)
|
||||
|
||||
options := &mail.Options{
|
||||
MailHost: config.SystemEmailHost,
|
||||
MailPort: config.SystemEmailPort,
|
||||
MailUser: config.SystemEmailUser,
|
||||
MailPass: config.SystemEmailPass,
|
||||
MailTo: config.ErrorNotifyUser,
|
||||
Subject: subject,
|
||||
Body: body,
|
||||
}
|
||||
_ = mail.Send(options)
|
||||
|
||||
utilGin := response.Gin{Ctx: c}
|
||||
utilGin.Response(500, "系统异常,请联系管理员!", nil)
|
||||
}
|
||||
}()
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
@@ -1,946 +0,0 @@
|
||||
package exception
|
||||
|
||||
var MailTemplate = `<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
</head>
|
||||
<body>
|
||||
<div class="content-wrap" style="margin: 0px auto; overflow: hidden; padding-top: 15px; background-color: rgb(255, 255, 255); width: 600px;">
|
||||
<!---->
|
||||
<div>
|
||||
<div style="margin: 0px auto; max-width: 600px;">
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" style="background-color: rgb(62, 207, 88); background-image: url(""); background-repeat: no-repeat; background-size: 100px; background-position: 1% 50%;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="direction: ltr; font-size: 0px; text-align: center; vertical-align: top; width: 600px;">
|
||||
<table border="0" cellpadding="0" cellspacing="0">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="width: 100%; max-width: 100%; min-height: 1px; font-size: 13px; text-align: left; direction: ltr; vertical-align: top; padding: 0px;">
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" style="vertical-align: top;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td align="center" style="font-size: 0px; word-break: break-word;">
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" style="border-collapse: collapse; border-spacing: 0px; width: 100%;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<div class="full">
|
||||
<div style="margin: 0px auto; max-width: 600px;">
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="width: 600px;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="direction: ltr; width: 600px; font-size: 0px; padding-bottom: 0px; text-align: center; vertical-align: top; background-image: url(""); background-repeat: no-repeat; background-size: 100px; background-position: 10% 50%;">
|
||||
<div style="font-size: 13px; text-align: left; direction: ltr; display: inline-block; vertical-align: top; width: 100%;">
|
||||
<table border="0" cellpadding="0" cellspacing="0" role="presentation" width="100%" style="vertical-align: top;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td align="left" style="font-size: 0px; padding: 20px; word-break: break-word;">
|
||||
<div class="text" style="margin: 0px; text-align: center; color: rgb(255, 255, 255); font-size: 16px;">
|
||||
<div>
|
||||
<p style="line-height: 20px; margin: 0px;">系统告警</p>
|
||||
</div>
|
||||
</div></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="full">
|
||||
<div style="margin: 0px auto; max-width: 600px;">
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="width: 600px;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="direction: ltr; font-size: 0px; padding-top: 0px; text-align: center; vertical-align: top;">
|
||||
<div class="outlook-group-fix" style="font-size: 13px; text-align: left; direction: ltr; display: inline-block; vertical-align: top; width: 100%;">
|
||||
<table border="0" cellpadding="0" cellspacing="0" role="presentation" width="100%" style="vertical-align: top;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td align="center" vertical-align="middle" style="padding-top: 40px; width: 600px; background-image: url(""); background-size: 100px; background-position: 10% 50%; background-repeat: no-repeat;"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div style="margin: 0px auto; max-width: 600px;">
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" style="background-color: rgb(255, 255, 255); background-image: url(""); background-repeat: no-repeat; background-size: 100px; background-position: 1% 50%;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="direction: ltr; font-size: 0px; text-align: center; vertical-align: top; width: 600px;">
|
||||
<table border="0" cellpadding="0" cellspacing="0">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="width: 40%; max-width: 40%; min-height: 1px; font-size: 13px; text-align: left; direction: ltr; vertical-align: top; padding: 0px;">
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" style="vertical-align: top;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td align="center" style="font-size: 0px; word-break: break-word;">
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" style="border-collapse: collapse; border-spacing: 0px; width: 100%;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<div class="full">
|
||||
<div style="margin: 0px auto; max-width: 600px; line-height: 0px;">
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="width: 240px;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="direction: ltr; font-size: 0px; padding: 0px; text-align: center; vertical-align: top;">
|
||||
<div class="outlook-group-fix" style="line-height: 0px; font-size: 13px; text-align: left; direction: ltr; display: inline-block; vertical-align: top; width: 100%;">
|
||||
<table border="0" cellpadding="0" cellspacing="0" role="presentation" width="100%" style="vertical-align: top;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td align="center" style="font-size: 0px; padding: 25px 0px; word-break: break-word; width: 240px; background-image: url(""); background-repeat: no-repeat; background-size: 100px; background-position: 10% 50%;">
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="border-collapse: collapse; border-spacing: 0px;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="width: 375px; border-top: 1px solid rgb(204, 204, 204);"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table></td>
|
||||
<td style="width: 20%; max-width: 20%; min-height: 1px; font-size: 13px; text-align: left; direction: ltr; vertical-align: top; padding: 0px;">
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" style="vertical-align: top;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td align="center" style="font-size: 0px; word-break: break-word;">
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" style="border-collapse: collapse; border-spacing: 0px; width: 100%;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<div class="full">
|
||||
<div style="margin: 0px auto; max-width: 600px;">
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="width: 120px;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="direction: ltr; width: 120px; font-size: 0px; padding-bottom: 0px; text-align: center; vertical-align: top; background-image: url(""); background-repeat: no-repeat; background-size: 100px; background-position: 10% 50%;">
|
||||
<div style="font-size: 13px; text-align: left; direction: ltr; display: inline-block; vertical-align: top; width: 100%;">
|
||||
<table border="0" cellpadding="0" cellspacing="0" role="presentation" width="100%" style="vertical-align: top;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td align="left" style="font-size: 0px; padding: 13px 20px; word-break: break-word;">
|
||||
<div class="text" style="margin: 0px; text-align: center; color: rgb(51, 51, 51); font-size: 16px;">
|
||||
<div>
|
||||
<p style="line-height: 20px; margin: 0px;">告警</p>
|
||||
</div>
|
||||
</div></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table></td>
|
||||
<td style="width: 40%; max-width: 40%; min-height: 1px; font-size: 13px; text-align: left; direction: ltr; vertical-align: top; padding: 0px;">
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" style="vertical-align: top;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td align="center" style="font-size: 0px; word-break: break-word;">
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" style="border-collapse: collapse; border-spacing: 0px; width: 100%;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<div class="full">
|
||||
<div style="margin: 0px auto; max-width: 600px; line-height: 0px;">
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="width: 240px;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="direction: ltr; font-size: 0px; padding: 0px; text-align: center; vertical-align: top;">
|
||||
<div class="outlook-group-fix" style="line-height: 0px; font-size: 13px; text-align: left; direction: ltr; display: inline-block; vertical-align: top; width: 100%;">
|
||||
<table border="0" cellpadding="0" cellspacing="0" role="presentation" width="100%" style="vertical-align: top;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td align="center" style="font-size: 0px; padding: 25px 0px; word-break: break-word; width: 240px; background-image: url(""); background-repeat: no-repeat; background-size: 100px; background-position: 10% 50%;">
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="border-collapse: collapse; border-spacing: 0px;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="width: 375px; border-top: 1px solid rgb(204, 204, 204);"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="full">
|
||||
<div style="margin: 0px auto; max-width: 600px;">
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="width: 600px;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="direction: ltr; width: 600px; font-size: 0px; padding-bottom: 0px; text-align: center; vertical-align: top; background-image: url(""); background-repeat: no-repeat; background-size: 100px; background-position: 10% 50%;">
|
||||
<div style="font-size: 13px; text-align: left; direction: ltr; display: inline-block; vertical-align: top; width: 100%;">
|
||||
<table border="0" cellpadding="0" cellspacing="0" role="presentation" width="100%" style="vertical-align: top;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td align="left" style="font-size: 0px; padding: 6px 20px; word-break: break-word;">
|
||||
<div class="text" style="margin: 0px; text-align: left; color: rgb(188, 12, 39); font-size: 21px;">
|
||||
<div>
|
||||
<p style="line-height: 20px; margin: 0px;">{ErrorMsg}</p>
|
||||
</div>
|
||||
</div></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="full">
|
||||
<div style="margin: 0px auto; max-width: 600px;">
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="width: 600px;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="direction: ltr; font-size: 0px; padding-top: 0px; text-align: center; vertical-align: top;">
|
||||
<div class="outlook-group-fix" style="font-size: 13px; text-align: left; direction: ltr; display: inline-block; vertical-align: top; width: 100%;">
|
||||
<table border="0" cellpadding="0" cellspacing="0" role="presentation" width="100%" style="vertical-align: top;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td align="center" vertical-align="middle" style="padding-top: 15px; width: 600px; background-image: url(""); background-size: 100px; background-position: 10% 50%; background-repeat: no-repeat;"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div style="margin: 0px auto; max-width: 600px;">
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" style="background-color: rgb(255, 255, 255); background-image: url(""); background-repeat: no-repeat; background-size: 100px; background-position: 1% 50%;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="direction: ltr; font-size: 0px; text-align: center; vertical-align: top; width: 600px;">
|
||||
<table border="0" cellpadding="0" cellspacing="0">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="width: 40%; max-width: 40%; min-height: 1px; font-size: 13px; text-align: left; direction: ltr; vertical-align: top; padding: 0px;">
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" style="vertical-align: top;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td align="center" style="font-size: 0px; word-break: break-word;">
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" style="border-collapse: collapse; border-spacing: 0px; width: 100%;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<div class="full">
|
||||
<div style="margin: 0px auto; max-width: 600px; line-height: 0px;">
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="width: 240px;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="direction: ltr; font-size: 0px; padding: 0px; text-align: center; vertical-align: top;">
|
||||
<div class="outlook-group-fix" style="line-height: 0px; font-size: 13px; text-align: left; direction: ltr; display: inline-block; vertical-align: top; width: 100%;">
|
||||
<table border="0" cellpadding="0" cellspacing="0" role="presentation" width="100%" style="vertical-align: top;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td align="center" style="font-size: 0px; padding: 25px 0px; word-break: break-word; width: 240px; background-image: url(""); background-repeat: no-repeat; background-size: 100px; background-position: 10% 50%;">
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="border-collapse: collapse; border-spacing: 0px;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="width: 375px; border-top: 1px solid rgb(204, 204, 204);"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table></td>
|
||||
<td style="width: 20%; max-width: 20%; min-height: 1px; font-size: 13px; text-align: left; direction: ltr; vertical-align: top; padding: 0px;">
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" style="vertical-align: top;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td align="center" style="font-size: 0px; word-break: break-word;">
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" style="border-collapse: collapse; border-spacing: 0px; width: 100%;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<div class="full">
|
||||
<div style="margin: 0px auto; max-width: 600px;">
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="width: 120px;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="direction: ltr; width: 120px; font-size: 0px; padding-bottom: 0px; text-align: center; vertical-align: top; background-image: url(""); background-repeat: no-repeat; background-size: 100px; background-position: 10% 50%;">
|
||||
<div style="font-size: 13px; text-align: left; direction: ltr; display: inline-block; vertical-align: top; width: 100%;">
|
||||
<table border="0" cellpadding="0" cellspacing="0" role="presentation" width="100%" style="vertical-align: top;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td align="left" style="font-size: 0px; padding: 13px 20px; word-break: break-word;">
|
||||
<div class="text" style="margin: 0px; text-align: center; color: rgb(51, 51, 51); font-size: 16px;">
|
||||
<div>
|
||||
<p style="line-height: 20px; margin: 0px;">详情</p>
|
||||
</div>
|
||||
</div></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table></td>
|
||||
<td style="width: 40%; max-width: 40%; min-height: 1px; font-size: 13px; text-align: left; direction: ltr; vertical-align: top; padding: 0px;">
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" style="vertical-align: top;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td align="center" style="font-size: 0px; word-break: break-word;">
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" style="border-collapse: collapse; border-spacing: 0px; width: 100%;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<div class="full">
|
||||
<div style="margin: 0px auto; max-width: 600px; line-height: 0px;">
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="width: 240px;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="direction: ltr; font-size: 0px; padding: 0px; text-align: center; vertical-align: top;">
|
||||
<div class="outlook-group-fix" style="line-height: 0px; font-size: 13px; text-align: left; direction: ltr; display: inline-block; vertical-align: top; width: 100%;">
|
||||
<table border="0" cellpadding="0" cellspacing="0" role="presentation" width="100%" style="vertical-align: top;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td align="center" style="font-size: 0px; padding: 25px 0px; word-break: break-word; width: 240px; background-image: url(""); background-repeat: no-repeat; background-size: 100px; background-position: 10% 50%;">
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="border-collapse: collapse; border-spacing: 0px;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="width: 375px; border-top: 1px solid rgb(204, 204, 204);"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div style="margin: 0px auto; max-width: 600px;">
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" style="background-color: rgb(241, 245, 240); background-image: url(""); background-repeat: no-repeat; background-size: 100px; background-position: 1% 50%;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="direction: ltr; font-size: 0px; text-align: center; vertical-align: top; width: 600px;">
|
||||
<table border="0" cellpadding="0" cellspacing="0">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="width: 100%; max-width: 100%; min-height: 1px; font-size: 13px; text-align: left; direction: ltr; vertical-align: top; padding: 0px;">
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" style="vertical-align: top;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td align="center" style="font-size: 0px; word-break: break-word;">
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" style="border-collapse: collapse; border-spacing: 0px; width: 100%;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<div columnnumber="3">
|
||||
<div>
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" style="width: 100%;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="direction: ltr; font-size: 0px; text-align: center; vertical-align: top; border: 0px;"><a target="_blank" href="javascript:;" style="cursor: default;">
|
||||
<div class="mj-column-per-25" style="width: 100%; max-width: 100%; font-size: 13px; text-align: left; direction: ltr; display: inline-block; vertical-align: top;">
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" style="vertical-align: top;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td align="center" border="0" style="font-size: 0px; word-break: break-word;">
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" style="border-collapse: collapse; border-spacing: 0px; width: 100%;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td border="0">
|
||||
<div class="full">
|
||||
<div style="margin: 0px auto; max-width: 600px;">
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="width: 600px;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="direction: ltr; width: 600px; font-size: 0px; padding-bottom: 0px; text-align: center; vertical-align: top; background-image: url(""); background-repeat: no-repeat; background-size: 100px; background-position: 10% 50%;">
|
||||
<div style="font-size: 13px; text-align: left; direction: ltr; display: inline-block; vertical-align: top; width: 100%;">
|
||||
<table border="0" cellpadding="0" cellspacing="0" role="presentation" width="100%" style="vertical-align: top;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td align="left" style="font-size: 0px; padding: 10px 20px; word-break: break-word;">
|
||||
<div class="text" style="margin: 0px; text-align: left; color: rgb(102, 102, 102); font-size: 12px;">
|
||||
<div>
|
||||
<p style="line-height: 20px; margin: 0px;">请求时间:</p>
|
||||
</div>
|
||||
</div></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="mj-column-per-25" style="width: 100%; max-width: 100%; font-size: 13px; text-align: left; direction: ltr; display: inline-block; vertical-align: top;">
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" style="vertical-align: top;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td align="center" border="0" style="font-size: 0px; word-break: break-word;">
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" style="border-collapse: collapse; border-spacing: 0px; width: 100%;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td border="0">
|
||||
<div class="full">
|
||||
<div style="margin: 0px auto; max-width: 600px;">
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="width: 600px;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="direction: ltr; width: 600px; font-size: 0px; padding-bottom: 0px; text-align: center; vertical-align: top; background-image: url(""); background-repeat: no-repeat; background-size: 100px; background-position: 10% 50%;">
|
||||
<div style="font-size: 13px; text-align: left; direction: ltr; display: inline-block; vertical-align: top; width: 100%;">
|
||||
<table border="0" cellpadding="0" cellspacing="0" role="presentation" width="100%" style="vertical-align: top;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td align="left" style="font-size: 0px; padding: 0px 20px; word-break: break-word;">
|
||||
<div class="text" style="margin: 0px; text-align: left; color: rgb(102, 102, 102); font-size: 12px;">
|
||||
<div>
|
||||
<p style="line-height: 20px; margin: 0px;">{RequestTime}</p>
|
||||
</div>
|
||||
</div></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="mj-column-per-25" style="width: 100%; max-width: 100%; font-size: 13px; text-align: left; direction: ltr; display: inline-block; vertical-align: top;">
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" style="vertical-align: top;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td align="center" border="0" style="font-size: 0px; word-break: break-word;">
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" style="border-collapse: collapse; border-spacing: 0px; width: 100%;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td border="0">
|
||||
<div class="full">
|
||||
<div style="margin: 0px auto; max-width: 600px;">
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="width: 600px;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="direction: ltr; width: 600px; font-size: 0px; padding-bottom: 0px; text-align: center; vertical-align: top; background-image: url(""); background-repeat: no-repeat; background-size: 100px; background-position: 10% 50%;">
|
||||
<div style="font-size: 13px; text-align: left; direction: ltr; display: inline-block; vertical-align: top; width: 100%;">
|
||||
<table border="0" cellpadding="0" cellspacing="0" role="presentation" width="100%" style="vertical-align: top;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td align="left" style="font-size: 0px; padding: 10px 20px; word-break: break-word;">
|
||||
<div class="text" style="margin: 0px; text-align: left; color: rgb(102, 102, 102); font-size: 12px;">
|
||||
<div>
|
||||
<p style="line-height: 20px; margin: 0px;">请求地址:</p>
|
||||
</div>
|
||||
</div></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="mj-column-per-25" style="width: 100%; max-width: 100%; font-size: 13px; text-align: left; direction: ltr; display: inline-block; vertical-align: top;">
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" style="vertical-align: top;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td align="center" border="0" style="font-size: 0px; word-break: break-word;">
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" style="border-collapse: collapse; border-spacing: 0px; width: 100%;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td border="0">
|
||||
<div class="full">
|
||||
<div style="margin: 0px auto; max-width: 600px;">
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="width: 600px;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="direction: ltr; width: 600px; font-size: 0px; padding-bottom: 0px; text-align: center; vertical-align: top; background-image: url(""); background-repeat: no-repeat; background-size: 100px; background-position: 10% 50%;">
|
||||
<div style="font-size: 13px; text-align: left; direction: ltr; display: inline-block; vertical-align: top; width: 100%;">
|
||||
<table border="0" cellpadding="0" cellspacing="0" role="presentation" width="100%" style="vertical-align: top;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td align="left" style="font-size: 0px; padding: 0px 20px; word-break: break-word;">
|
||||
<div class="text" style="margin: 0px; text-align: left; color: rgb(102, 102, 102); font-size: 12px;">
|
||||
<div>
|
||||
<p style="line-height: 20px; margin: 0px;">{RequestURL}</p>
|
||||
</div>
|
||||
</div></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="mj-column-per-25" style="width: 100%; max-width: 100%; font-size: 13px; text-align: left; direction: ltr; display: inline-block; vertical-align: top;">
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" style="vertical-align: top;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td align="center" border="0" style="font-size: 0px; word-break: break-word;">
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" style="border-collapse: collapse; border-spacing: 0px; width: 100%;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td border="0">
|
||||
<div class="full">
|
||||
<div style="margin: 0px auto; max-width: 600px;">
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="width: 600px;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="direction: ltr; width: 600px; font-size: 0px; padding-bottom: 0px; text-align: center; vertical-align: top; background-image: url(""); background-repeat: no-repeat; background-size: 100px; background-position: 10% 50%;">
|
||||
<div style="font-size: 13px; text-align: left; direction: ltr; display: inline-block; vertical-align: top; width: 100%;">
|
||||
<table border="0" cellpadding="0" cellspacing="0" role="presentation" width="100%" style="vertical-align: top;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td align="left" style="font-size: 0px; padding: 10px 20px; word-break: break-word;">
|
||||
<div class="text" style="margin: 0px; text-align: left; color: rgb(102, 102, 102); font-size: 12px;">
|
||||
<div>
|
||||
<p style="line-height: 20px; margin: 0px;">请求 UA:</p>
|
||||
</div>
|
||||
</div></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="mj-column-per-25" style="width: 100%; max-width: 100%; font-size: 13px; text-align: left; direction: ltr; display: inline-block; vertical-align: top;">
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" style="vertical-align: top;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td align="center" border="0" style="font-size: 0px; word-break: break-word;">
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" style="border-collapse: collapse; border-spacing: 0px; width: 100%;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td border="0">
|
||||
<div class="full">
|
||||
<div style="margin: 0px auto; max-width: 600px;">
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="width: 600px;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="direction: ltr; width: 600px; font-size: 0px; padding-bottom: 0px; text-align: center; vertical-align: top; background-image: url(""); background-repeat: no-repeat; background-size: 100px; background-position: 10% 50%;">
|
||||
<div style="font-size: 13px; text-align: left; direction: ltr; display: inline-block; vertical-align: top; width: 100%;">
|
||||
<table border="0" cellpadding="0" cellspacing="0" role="presentation" width="100%" style="vertical-align: top;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td align="left" style="font-size: 0px; padding: 0px 20px; word-break: break-word;">
|
||||
<div class="text" style="margin: 0px; text-align: left; color: rgb(102, 102, 102); font-size: 12px;">
|
||||
<div>
|
||||
<p style="line-height: 20px; margin: 0px;">{RequestUA}</p>
|
||||
</div>
|
||||
</div></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="mj-column-per-25" style="width: 100%; max-width: 100%; font-size: 13px; text-align: left; direction: ltr; display: inline-block; vertical-align: top;">
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" style="vertical-align: top;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td align="center" border="0" style="font-size: 0px; word-break: break-word;">
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" style="border-collapse: collapse; border-spacing: 0px; width: 100%;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td border="0">
|
||||
<div class="full">
|
||||
<div style="margin: 0px auto; max-width: 600px;">
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="width: 600px;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="direction: ltr; width: 600px; font-size: 0px; padding-bottom: 0px; text-align: center; vertical-align: top; background-image: url(""); background-repeat: no-repeat; background-size: 100px; background-position: 10% 50%;">
|
||||
<div style="font-size: 13px; text-align: left; direction: ltr; display: inline-block; vertical-align: top; width: 100%;">
|
||||
<table border="0" cellpadding="0" cellspacing="0" role="presentation" width="100%" style="vertical-align: top;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td align="left" style="font-size: 0px; padding: 10px 20px; word-break: break-word;">
|
||||
<div class="text" style="margin: 0px; text-align: left; color: rgb(102, 102, 102); font-size: 12px;">
|
||||
<div>
|
||||
<p style="line-height: 20px; margin: 0px;">请求 IP:</p>
|
||||
</div>
|
||||
</div></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="mj-column-per-25" style="width: 100%; max-width: 100%; font-size: 13px; text-align: left; direction: ltr; display: inline-block; vertical-align: top;">
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" style="vertical-align: top;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td align="center" border="0" style="font-size: 0px; word-break: break-word;">
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" style="border-collapse: collapse; border-spacing: 0px; width: 100%;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td border="0">
|
||||
<div class="full">
|
||||
<div style="margin: 0px auto; max-width: 600px;">
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="width: 600px;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="direction: ltr; width: 600px; font-size: 0px; padding-bottom: 0px; text-align: center; vertical-align: top; background-image: url(""); background-repeat: no-repeat; background-size: 100px; background-position: 10% 50%;">
|
||||
<div style="font-size: 13px; text-align: left; direction: ltr; display: inline-block; vertical-align: top; width: 100%;">
|
||||
<table border="0" cellpadding="0" cellspacing="0" role="presentation" width="100%" style="vertical-align: top;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td align="left" style="font-size: 0px; padding: 0px 20px; word-break: break-word;">
|
||||
<div class="text" style="margin: 0px; text-align: left; color: rgb(102, 102, 102); font-size: 12px;">
|
||||
<div>
|
||||
<p style="line-height: 20px; margin: 0px;">{RequestIP}</p>
|
||||
</div>
|
||||
</div></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="mj-column-per-25" style="width: 100%; max-width: 100%; font-size: 13px; text-align: left; direction: ltr; display: inline-block; vertical-align: top;">
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" style="vertical-align: top;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td align="center" border="0" style="font-size: 0px; word-break: break-word;">
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" style="border-collapse: collapse; border-spacing: 0px; width: 100%;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td border="0">
|
||||
<div class="full">
|
||||
<div style="margin: 0px auto; max-width: 600px;">
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="width: 600px;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="direction: ltr; width: 600px; font-size: 0px; padding-bottom: 0px; text-align: center; vertical-align: top; background-image: url(""); background-repeat: no-repeat; background-size: 100px; background-position: 10% 50%;">
|
||||
<div style="font-size: 13px; text-align: left; direction: ltr; display: inline-block; vertical-align: top; width: 100%;">
|
||||
<table border="0" cellpadding="0" cellspacing="0" role="presentation" width="100%" style="vertical-align: top;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td align="left" style="font-size: 0px; padding: 10px 20px; word-break: break-word;">
|
||||
<div class="text" style="margin: 0px; text-align: left; color: rgb(102, 102, 102); font-size: 12px;">
|
||||
<div>
|
||||
<p style="line-height: 20px; margin: 0px;">DebugStack:</p>
|
||||
</div>
|
||||
</div></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="mj-column-per-25" style="width: 100%; max-width: 100%; font-size: 13px; text-align: left; direction: ltr; display: inline-block; vertical-align: top;">
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" style="vertical-align: top;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td align="center" border="0" style="font-size: 0px; word-break: break-word;">
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" style="border-collapse: collapse; border-spacing: 0px; width: 100%;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td border="0">
|
||||
<div class="full">
|
||||
<div style="margin: 0px auto; max-width: 600px;">
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="width: 600px;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="direction: ltr; width: 600px; font-size: 0px; padding-bottom: 0px; text-align: center; vertical-align: top; background-image: url(""); background-repeat: no-repeat; background-size: 100px; background-position: 10% 50%;">
|
||||
<div style="font-size: 13px; text-align: left; direction: ltr; display: inline-block; vertical-align: top; width: 100%;">
|
||||
<table border="0" cellpadding="0" cellspacing="0" role="presentation" width="100%" style="vertical-align: top;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td align="left" style="font-size: 0px; padding: 0px 20px; word-break: break-word;">
|
||||
<div class="text" style="margin: 0px; text-align: left; color: rgb(102, 102, 102); font-size: 12px;">
|
||||
<div>
|
||||
<p style="line-height: 20px; margin: 0px;">{DebugStack}</p>
|
||||
</div>
|
||||
</div></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div></a></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="full">
|
||||
<div style="margin: 0px auto; max-width: 600px;">
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="width: 600px;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="direction: ltr; font-size: 0px; padding-top: 0px; text-align: center; vertical-align: top;">
|
||||
<div class="outlook-group-fix" style="font-size: 13px; text-align: left; direction: ltr; display: inline-block; vertical-align: top; width: 100%;">
|
||||
<table border="0" cellpadding="0" cellspacing="0" role="presentation" width="100%" style="vertical-align: top;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td align="center" vertical-align="middle" style="padding-top: 15px; width: 600px; background-image: url(""); background-size: 100px; background-position: 10% 50%; background-repeat: no-repeat;"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div style="margin: 0px auto; max-width: 600px;">
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" style="background-color: rgb(65, 207, 88); background-image: url(""); background-repeat: no-repeat; background-size: 100px; background-position: 1% 50%;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="direction: ltr; font-size: 0px; text-align: center; vertical-align: top; width: 600px;">
|
||||
<table border="0" cellpadding="0" cellspacing="0">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="width: 100%; max-width: 100%; min-height: 1px; font-size: 13px; text-align: left; direction: ltr; vertical-align: top; padding: 0px;">
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" style="vertical-align: top;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td align="center" style="font-size: 0px; word-break: break-word;">
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" style="border-collapse: collapse; border-spacing: 0px; width: 100%;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<div class="full">
|
||||
<div style="margin: 0px auto; max-width: 600px;">
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="width: 600px;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="direction: ltr; width: 600px; font-size: 0px; padding-bottom: 0px; text-align: center; vertical-align: top; background-image: url(""); background-repeat: no-repeat; background-size: 100px; background-position: 10% 50%;">
|
||||
<div style="font-size: 13px; text-align: left; direction: ltr; display: inline-block; vertical-align: top; width: 100%;">
|
||||
<table border="0" cellpadding="0" cellspacing="0" role="presentation" width="100%" style="vertical-align: top;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td align="left" style="font-size: 0px; padding: 20px 10px; word-break: break-word;">
|
||||
<div class="text" style="margin: 0px; text-align: left; color: rgb(255, 255, 255); font-size: 12px;">
|
||||
<div>
|
||||
<p style="line-height: 20px; margin: 0px;">请注意,该邮件地址不接收回复邮件。</p>
|
||||
</div>
|
||||
</div></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>`
|
||||
@@ -1,40 +0,0 @@
|
||||
package jaeger
|
||||
|
||||
import (
|
||||
"github.com/xinliangnote/go-gin-api/internal/api/config"
|
||||
"github.com/xinliangnote/go-gin-api/internal/api/util/jaeger_trace"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/opentracing/opentracing-go"
|
||||
"github.com/opentracing/opentracing-go/ext"
|
||||
)
|
||||
|
||||
func SetUp() gin.HandlerFunc {
|
||||
|
||||
return func(c *gin.Context) {
|
||||
if config.JaegerOpen == 1 {
|
||||
|
||||
var parentSpan opentracing.Span
|
||||
|
||||
tracer, closer := jaeger_trace.NewJaegerTracer("LXL-TEST", config.JaegerHostPort)
|
||||
defer closer.Close()
|
||||
|
||||
spCtx, err := opentracing.GlobalTracer().Extract(opentracing.HTTPHeaders, opentracing.HTTPHeadersCarrier(c.Request.Header))
|
||||
if err != nil {
|
||||
parentSpan = tracer.StartSpan(c.Request.URL.Path)
|
||||
defer parentSpan.Finish()
|
||||
} else {
|
||||
parentSpan = opentracing.StartSpan(
|
||||
c.Request.URL.Path,
|
||||
opentracing.ChildOf(spCtx),
|
||||
opentracing.Tag{Key: string(ext.Component), Value: "HTTP"},
|
||||
ext.SpanKindRPCServer,
|
||||
)
|
||||
defer parentSpan.Finish()
|
||||
}
|
||||
c.Set("Tracer", tracer)
|
||||
c.Set("ParentSpanContext", parentSpan.Context())
|
||||
}
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
@@ -1,124 +0,0 @@
|
||||
package sign_aes
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/xinliangnote/go-gin-api/internal/api/config"
|
||||
"github.com/xinliangnote/go-gin-api/internal/api/util/response"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/xinliangnote/go-util/aes"
|
||||
timeUtil "github.com/xinliangnote/go-util/time"
|
||||
)
|
||||
|
||||
var AppSecret string
|
||||
|
||||
// AES 对称加密
|
||||
func SetUp() gin.HandlerFunc {
|
||||
|
||||
return func(c *gin.Context) {
|
||||
utilGin := response.Gin{Ctx: c}
|
||||
|
||||
sign, err := verifySign(c)
|
||||
|
||||
if sign != nil {
|
||||
utilGin.Response(-1, "Debug Sign", sign)
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
utilGin.Response(-1, err.Error(), sign)
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
|
||||
// 验证签名
|
||||
func verifySign(c *gin.Context) (map[string]string, error) {
|
||||
_ = c.Request.ParseForm()
|
||||
req := c.Request.Form
|
||||
debug := strings.Join(c.Request.Form["debug"], "")
|
||||
ak := strings.Join(c.Request.Form["ak"], "")
|
||||
sn := strings.Join(c.Request.Form["sn"], "")
|
||||
ts := strings.Join(c.Request.Form["ts"], "")
|
||||
|
||||
// 验证来源
|
||||
value, ok := config.ApiAuthConfig[ak]
|
||||
if ok {
|
||||
AppSecret = value["aes"]
|
||||
} else {
|
||||
return nil, errors.New("ak Error")
|
||||
}
|
||||
|
||||
if debug == "1" {
|
||||
currentUnix := timeUtil.GetCurrentUnix()
|
||||
req.Set("ts", strconv.FormatInt(currentUnix, 10))
|
||||
|
||||
sn, err := createSign(req)
|
||||
if err != nil {
|
||||
return nil, errors.New("sn Exception")
|
||||
}
|
||||
|
||||
res := map[string]string{
|
||||
"ts": strconv.FormatInt(currentUnix, 10),
|
||||
"sn": sn,
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// 验证过期时间
|
||||
timestamp := time.Now().Unix()
|
||||
exp, _ := strconv.ParseInt(config.AppSignExpiry, 10, 64)
|
||||
tsInt, _ := strconv.ParseInt(ts, 10, 64)
|
||||
if tsInt > timestamp || timestamp-tsInt >= exp {
|
||||
return nil, errors.New("ts Error")
|
||||
}
|
||||
|
||||
// 验证签名
|
||||
if sn == "" {
|
||||
return nil, errors.New("sn Error")
|
||||
}
|
||||
|
||||
decryptStr, decryptErr := aes.Decrypt(sn, []byte(AppSecret), AppSecret)
|
||||
if decryptErr != nil {
|
||||
return nil, errors.New(decryptErr.Error())
|
||||
}
|
||||
if decryptStr != createEncryptStr(req) {
|
||||
return nil, errors.New("sn Error")
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// 创建签名
|
||||
func createSign(params url.Values) (string, error) {
|
||||
return aes.Encrypt(createEncryptStr(params), []byte(AppSecret), AppSecret)
|
||||
}
|
||||
|
||||
func createEncryptStr(params url.Values) string {
|
||||
var key []string
|
||||
var str = ""
|
||||
for k := range params {
|
||||
if k != "sn" && k != "debug" {
|
||||
key = append(key, k)
|
||||
}
|
||||
}
|
||||
sort.Strings(key)
|
||||
for i := 0; i < len(key); i++ {
|
||||
if i == 0 {
|
||||
str = fmt.Sprintf("%v=%v", key[i], params.Get(key[i]))
|
||||
} else {
|
||||
str = str + fmt.Sprintf("&%v=%v", key[i], params.Get(key[i]))
|
||||
}
|
||||
}
|
||||
return str
|
||||
}
|
||||
@@ -1,112 +0,0 @@
|
||||
package sign_md5
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/xinliangnote/go-gin-api/internal/api/config"
|
||||
"github.com/xinliangnote/go-gin-api/internal/api/util/response"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/xinliangnote/go-util/md5"
|
||||
timeUtil "github.com/xinliangnote/go-util/time"
|
||||
)
|
||||
|
||||
var AppSecret string
|
||||
|
||||
// MD5 组合加密
|
||||
func SetUp() gin.HandlerFunc {
|
||||
|
||||
return func(c *gin.Context) {
|
||||
utilGin := response.Gin{Ctx: c}
|
||||
|
||||
sign, err := verifySign(c)
|
||||
|
||||
if sign != nil {
|
||||
utilGin.Response(-1, "Debug Sign", sign)
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
utilGin.Response(-1, err.Error(), sign)
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
|
||||
// 验证签名
|
||||
func verifySign(c *gin.Context) (map[string]string, error) {
|
||||
_ = c.Request.ParseForm()
|
||||
req := c.Request.Form
|
||||
debug := strings.Join(c.Request.Form["debug"], "")
|
||||
ak := strings.Join(c.Request.Form["ak"], "")
|
||||
sn := strings.Join(c.Request.Form["sn"], "")
|
||||
ts := strings.Join(c.Request.Form["ts"], "")
|
||||
|
||||
// 验证来源
|
||||
value, ok := config.ApiAuthConfig[ak]
|
||||
if ok {
|
||||
AppSecret = value["md5"]
|
||||
} else {
|
||||
return nil, errors.New("ak Error")
|
||||
}
|
||||
|
||||
if debug == "1" {
|
||||
currentUnix := timeUtil.GetCurrentUnix()
|
||||
req.Set("ts", strconv.FormatInt(currentUnix, 10))
|
||||
res := map[string]string{
|
||||
"ts": strconv.FormatInt(currentUnix, 10),
|
||||
"sn": createSign(req),
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// 验证过期时间
|
||||
timestamp := time.Now().Unix()
|
||||
exp, _ := strconv.ParseInt(config.AppSignExpiry, 10, 64)
|
||||
tsInt, _ := strconv.ParseInt(ts, 10, 64)
|
||||
if tsInt > timestamp || timestamp-tsInt >= exp {
|
||||
return nil, errors.New("ts Error")
|
||||
}
|
||||
|
||||
// 验证签名
|
||||
if sn == "" || sn != createSign(req) {
|
||||
return nil, errors.New("sn Error")
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// 创建签名
|
||||
func createSign(params url.Values) string {
|
||||
// 自定义 MD5 组合
|
||||
return md5.MD5(AppSecret + createEncryptStr(params) + AppSecret)
|
||||
}
|
||||
|
||||
func createEncryptStr(params url.Values) string {
|
||||
var key []string
|
||||
var str = ""
|
||||
for k := range params {
|
||||
if k != "sn" && k != "debug" {
|
||||
key = append(key, k)
|
||||
}
|
||||
}
|
||||
sort.Strings(key)
|
||||
for i := 0; i < len(key); i++ {
|
||||
if i == 0 {
|
||||
str = fmt.Sprintf("%v=%v", key[i], params.Get(key[i]))
|
||||
} else {
|
||||
str = str + fmt.Sprintf("&%v=%v", key[i], params.Get(key[i]))
|
||||
}
|
||||
}
|
||||
return str
|
||||
}
|
||||
@@ -1,124 +0,0 @@
|
||||
package sign_rsa
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/xinliangnote/go-gin-api/internal/api/config"
|
||||
"github.com/xinliangnote/go-gin-api/internal/api/util/response"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/xinliangnote/go-util/rsa"
|
||||
timeUtil "github.com/xinliangnote/go-util/time"
|
||||
)
|
||||
|
||||
var AppSecret string
|
||||
|
||||
// RSA 非对称加密
|
||||
func SetUp() gin.HandlerFunc {
|
||||
|
||||
return func(c *gin.Context) {
|
||||
utilGin := response.Gin{Ctx: c}
|
||||
|
||||
sign, err := verifySign(c)
|
||||
|
||||
if sign != nil {
|
||||
utilGin.Response(-1, "Debug Sign", sign)
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
utilGin.Response(-1, err.Error(), sign)
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
|
||||
// 验证签名
|
||||
func verifySign(c *gin.Context) (map[string]string, error) {
|
||||
_ = c.Request.ParseForm()
|
||||
req := c.Request.Form
|
||||
debug := strings.Join(c.Request.Form["debug"], "")
|
||||
ak := strings.Join(c.Request.Form["ak"], "")
|
||||
sn := strings.Join(c.Request.Form["sn"], "")
|
||||
ts := strings.Join(c.Request.Form["ts"], "")
|
||||
|
||||
// 验证来源
|
||||
value, ok := config.ApiAuthConfig[ak]
|
||||
if ok {
|
||||
AppSecret = value["rsa"]
|
||||
} else {
|
||||
return nil, errors.New("ak Error")
|
||||
}
|
||||
|
||||
if debug == "1" {
|
||||
currentUnix := timeUtil.GetCurrentUnix()
|
||||
req.Set("ts", strconv.FormatInt(currentUnix, 10))
|
||||
|
||||
sn, err := createSign(req)
|
||||
if err != nil {
|
||||
return nil, errors.New("sn Exception")
|
||||
}
|
||||
|
||||
res := map[string]string{
|
||||
"ts": strconv.FormatInt(currentUnix, 10),
|
||||
"sn": sn,
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// 验证过期时间
|
||||
timestamp := time.Now().Unix()
|
||||
exp, _ := strconv.ParseInt(config.AppSignExpiry, 10, 64)
|
||||
tsInt, _ := strconv.ParseInt(ts, 10, 64)
|
||||
if tsInt > timestamp || timestamp-tsInt >= exp {
|
||||
return nil, errors.New("ts Error")
|
||||
}
|
||||
|
||||
// 验证签名
|
||||
if sn == "" {
|
||||
return nil, errors.New("sn Error")
|
||||
}
|
||||
|
||||
decryptStr, decryptErr := rsa.PrivateDecrypt(sn, config.AppRsaPrivateFile)
|
||||
if decryptErr != nil {
|
||||
return nil, errors.New(decryptErr.Error())
|
||||
}
|
||||
if decryptStr != createEncryptStr(req) {
|
||||
return nil, errors.New("sn Error")
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// 创建签名
|
||||
func createSign(params url.Values) (string, error) {
|
||||
return rsa.PublicEncrypt(createEncryptStr(params), AppSecret)
|
||||
}
|
||||
|
||||
func createEncryptStr(params url.Values) string {
|
||||
var key []string
|
||||
var str = ""
|
||||
for k := range params {
|
||||
if k != "sn" && k != "debug" {
|
||||
key = append(key, k)
|
||||
}
|
||||
}
|
||||
sort.Strings(key)
|
||||
for i := 0; i < len(key); i++ {
|
||||
if i == 0 {
|
||||
str = fmt.Sprintf("%v=%v", key[i], params.Get(key[i]))
|
||||
} else {
|
||||
str = str + fmt.Sprintf("&%v=%v", key[i], params.Get(key[i]))
|
||||
}
|
||||
}
|
||||
return str
|
||||
}
|
||||
@@ -1,77 +0,0 @@
|
||||
package grpc_client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/xinliangnote/go-gin-api/internal/api/config"
|
||||
"github.com/xinliangnote/go-gin-api/internal/api/util/grpc_log"
|
||||
"github.com/xinliangnote/go-gin-api/internal/api/util/jaeger_trace"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
grpc_middeware "github.com/grpc-ecosystem/go-grpc-middleware"
|
||||
"github.com/opentracing/opentracing-go"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
func CreateServiceListenConn(c *gin.Context) *grpc.ClientConn {
|
||||
return createGrpcConn("127.0.0.1:9901", c)
|
||||
}
|
||||
|
||||
func CreateServiceSpeakConn(c *gin.Context) *grpc.ClientConn {
|
||||
return createGrpcConn("127.0.0.1:9902", c)
|
||||
}
|
||||
|
||||
func CreateServiceReadConn(c *gin.Context) *grpc.ClientConn {
|
||||
return createGrpcConn("127.0.0.1:9903", c)
|
||||
}
|
||||
|
||||
func CreateServiceWriteConn(c *gin.Context) *grpc.ClientConn {
|
||||
return createGrpcConn("127.0.0.1:9904", c)
|
||||
}
|
||||
|
||||
func createGrpcConn(serviceAddress string, c *gin.Context) *grpc.ClientConn {
|
||||
|
||||
var conn *grpc.ClientConn
|
||||
var err error
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond*500)
|
||||
defer cancel()
|
||||
|
||||
if config.JaegerOpen == 1 {
|
||||
|
||||
tracer, _ := c.Get("Tracer")
|
||||
parentSpanContext, _ := c.Get("ParentSpanContext")
|
||||
|
||||
conn, err = grpc.DialContext(
|
||||
ctx,
|
||||
serviceAddress,
|
||||
grpc.WithInsecure(),
|
||||
grpc.WithBlock(),
|
||||
grpc.WithUnaryInterceptor(
|
||||
grpc_middeware.ChainUnaryClient(
|
||||
jaeger_trace.ClientInterceptor(tracer.(opentracing.Tracer), parentSpanContext.(opentracing.SpanContext)),
|
||||
grpc_log.ClientInterceptor(),
|
||||
),
|
||||
),
|
||||
)
|
||||
} else {
|
||||
conn, err = grpc.DialContext(
|
||||
ctx,
|
||||
serviceAddress,
|
||||
grpc.WithInsecure(),
|
||||
grpc.WithBlock(),
|
||||
grpc.WithUnaryInterceptor(
|
||||
grpc_middeware.ChainUnaryClient(
|
||||
grpc_log.ClientInterceptor(),
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
fmt.Println(serviceAddress, "grpc conn err:", err)
|
||||
}
|
||||
return conn
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
package grpc_log
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/xinliangnote/go-gin-api/internal/api/config"
|
||||
|
||||
"github.com/xinliangnote/go-util/json"
|
||||
"github.com/xinliangnote/go-util/time"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
var grpcChannel = make(chan string, 100)
|
||||
|
||||
func ClientInterceptor() grpc.UnaryClientInterceptor {
|
||||
|
||||
go handleGrpcChannel()
|
||||
|
||||
return func(ctx context.Context, method string,
|
||||
req, reply interface{}, cc *grpc.ClientConn,
|
||||
invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {
|
||||
|
||||
// 开始时间
|
||||
startTime := time.GetCurrentMilliUnix()
|
||||
|
||||
err := invoker(ctx, method, req, reply, cc, opts...)
|
||||
|
||||
// 结束时间
|
||||
endTime := time.GetCurrentMilliUnix()
|
||||
|
||||
// 日志格式
|
||||
grpcLogMap := make(map[string]interface{})
|
||||
|
||||
grpcLogMap["request_time"] = startTime
|
||||
grpcLogMap["request_data"] = req
|
||||
grpcLogMap["request_method"] = method
|
||||
|
||||
grpcLogMap["response_data"] = reply
|
||||
grpcLogMap["response_error"] = err
|
||||
|
||||
grpcLogMap["cost_time"] = fmt.Sprintf("%vms", endTime-startTime)
|
||||
|
||||
grpcLogJson, _ := json.Encode(grpcLogMap)
|
||||
|
||||
grpcChannel <- grpcLogJson
|
||||
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
func handleGrpcChannel() {
|
||||
if f, err := os.OpenFile(config.AppGrpcLogName, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0666); err != nil {
|
||||
log.Println(err)
|
||||
} else {
|
||||
for accessLog := range grpcChannel {
|
||||
_, _ = f.WriteString(accessLog + "\n")
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -1,98 +0,0 @@
|
||||
package jaeger_trace
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"github.com/opentracing/opentracing-go"
|
||||
"github.com/opentracing/opentracing-go/ext"
|
||||
"github.com/opentracing/opentracing-go/log"
|
||||
"github.com/uber/jaeger-client-go"
|
||||
jaegerConfig "github.com/uber/jaeger-client-go/config"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/metadata"
|
||||
)
|
||||
|
||||
func NewJaegerTracer(serviceName string, jaegerHostPort string) (opentracing.Tracer, io.Closer) {
|
||||
|
||||
cfg := &jaegerConfig.Configuration{
|
||||
Sampler: &jaegerConfig.SamplerConfig{
|
||||
Type: "const", //固定采样
|
||||
Param: 1, //1=全采样、0=不采样
|
||||
},
|
||||
|
||||
Reporter: &jaegerConfig.ReporterConfig{
|
||||
LogSpans: true,
|
||||
LocalAgentHostPort: jaegerHostPort,
|
||||
},
|
||||
|
||||
ServiceName: serviceName,
|
||||
}
|
||||
|
||||
tracer, closer, err := cfg.NewTracer(jaegerConfig.Logger(jaeger.StdLogger))
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("ERROR: cannot init Jaeger: %v\n", err))
|
||||
}
|
||||
opentracing.SetGlobalTracer(tracer)
|
||||
return tracer, closer
|
||||
}
|
||||
|
||||
type MDReaderWriter struct {
|
||||
metadata.MD
|
||||
}
|
||||
|
||||
// ForeachKey implements ForeachKey of opentracing.TextMapReader
|
||||
func (c MDReaderWriter) ForeachKey(handler func(key, val string) error) error {
|
||||
for k, vs := range c.MD {
|
||||
for _, v := range vs {
|
||||
if err := handler(k, v); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Set implements Set() of opentracing.TextMapWriter
|
||||
func (c MDReaderWriter) Set(key, val string) {
|
||||
key = strings.ToLower(key)
|
||||
c.MD[key] = append(c.MD[key], val)
|
||||
}
|
||||
|
||||
// ClientInterceptor grpc client
|
||||
func ClientInterceptor(tracer opentracing.Tracer, spanContext opentracing.SpanContext) grpc.UnaryClientInterceptor {
|
||||
return func(ctx context.Context, method string,
|
||||
req, reply interface{}, cc *grpc.ClientConn,
|
||||
invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {
|
||||
|
||||
span := opentracing.StartSpan(
|
||||
"call gRPC",
|
||||
opentracing.ChildOf(spanContext),
|
||||
opentracing.Tag{Key: string(ext.Component), Value: "gRPC"},
|
||||
ext.SpanKindRPCClient,
|
||||
)
|
||||
|
||||
defer span.Finish()
|
||||
|
||||
md, ok := metadata.FromOutgoingContext(ctx)
|
||||
if !ok {
|
||||
md = metadata.New(nil)
|
||||
} else {
|
||||
md = md.Copy()
|
||||
}
|
||||
|
||||
err := tracer.Inject(span.Context(), opentracing.TextMap, MDReaderWriter{md})
|
||||
if err != nil {
|
||||
span.LogFields(log.String("inject-error", err.Error()))
|
||||
}
|
||||
|
||||
newCtx := metadata.NewOutgoingContext(ctx, md)
|
||||
err = invoker(newCtx, method, req, reply, cc, opts...)
|
||||
if err != nil {
|
||||
span.LogFields(log.String("call-error", err.Error()))
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
package request
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/xinliangnote/go-gin-api/internal/api/config"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/opentracing/opentracing-go"
|
||||
"github.com/opentracing/opentracing-go/ext"
|
||||
)
|
||||
|
||||
func HttpGet(url string, c *gin.Context) (string, error) {
|
||||
|
||||
tr := &http.Transport{
|
||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
||||
}
|
||||
|
||||
client := &http.Client{
|
||||
Timeout: time.Second * 5, //默认5秒超时时间
|
||||
Transport: tr,
|
||||
}
|
||||
|
||||
req, err := http.NewRequest("GET", url, nil)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if config.JaegerOpen == 1 {
|
||||
|
||||
tracer, _ := c.Get("Tracer")
|
||||
parentSpanContext, _ := c.Get("ParentSpanContext")
|
||||
|
||||
span := opentracing.StartSpan(
|
||||
"call Http Get",
|
||||
opentracing.ChildOf(parentSpanContext.(opentracing.SpanContext)),
|
||||
opentracing.Tag{Key: string(ext.Component), Value: "HTTP"},
|
||||
ext.SpanKindRPCClient,
|
||||
)
|
||||
|
||||
span.Finish()
|
||||
|
||||
injectErr := tracer.(opentracing.Tracer).Inject(span.Context(), opentracing.HTTPHeaders, opentracing.HTTPHeadersCarrier(req.Header))
|
||||
if injectErr != nil {
|
||||
log.Fatalf("%s: Couldn't inject headers", err)
|
||||
}
|
||||
}
|
||||
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
content, err := ioutil.ReadAll(resp.Body)
|
||||
defer resp.Body.Close()
|
||||
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(content), err
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
package response
|
||||
|
||||
import "github.com/gin-gonic/gin"
|
||||
|
||||
type Gin struct {
|
||||
Ctx *gin.Context
|
||||
}
|
||||
|
||||
type Response struct {
|
||||
Code int `json:"code"`
|
||||
Message string `json:"msg"`
|
||||
Data interface{} `json:"data"`
|
||||
}
|
||||
|
||||
func (g *Gin) Response(code int, msg string, data interface{}) {
|
||||
g.Ctx.JSON(200, Response{
|
||||
Code: code,
|
||||
Message: msg,
|
||||
Data: data,
|
||||
})
|
||||
return
|
||||
}
|
||||
@@ -9,10 +9,17 @@ import (
|
||||
var config = new(Config)
|
||||
|
||||
type Config struct {
|
||||
DB struct {
|
||||
Mail struct {
|
||||
Host string `mapstructure:"host"`
|
||||
Port int `mapstructure:"port"`
|
||||
} `mapstructure:"db"`
|
||||
User string `mapstructure:"user"`
|
||||
Pass string `mapstructure:"pass"`
|
||||
To string `mapstructure:"to"`
|
||||
} `mapstructure:"mail"`
|
||||
|
||||
JWT struct {
|
||||
Secret string `mapstructure:"secret"`
|
||||
} `mapstructure:"jwt"`
|
||||
|
||||
Aes struct {
|
||||
Key string `mapstructure:"key"`
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/errno"
|
||||
@@ -20,10 +21,14 @@ type HandlerFunc func(c Context)
|
||||
type Journal = journal.T
|
||||
|
||||
const (
|
||||
_JournalName = "_journal_"
|
||||
_LoggerName = "_logger_"
|
||||
_BodyName = "_body_"
|
||||
_PayloadName = "_payload_"
|
||||
_Alias = "_alias_"
|
||||
_JournalName = "_journal_"
|
||||
_LoggerName = "_logger_"
|
||||
_BodyName = "_body_"
|
||||
_PayloadName = "_payload_"
|
||||
_UserID = "_user_id_"
|
||||
_UserName = "_user_name_"
|
||||
_AbortErrorName = "_abort_error_"
|
||||
)
|
||||
|
||||
var contextPool = &sync.Pool{
|
||||
@@ -87,14 +92,22 @@ type Context interface {
|
||||
GetHeader(key string) string
|
||||
SetHeader(key, value string)
|
||||
|
||||
UserID() int
|
||||
setUserID(userID int)
|
||||
|
||||
UserName() string
|
||||
setUserName(userName string)
|
||||
|
||||
AbortWithError(err errno.Error)
|
||||
abortError() errno.Error
|
||||
|
||||
Alias() string
|
||||
setAlias(path string)
|
||||
|
||||
RawData() []byte
|
||||
|
||||
Method() string
|
||||
|
||||
Host() string
|
||||
|
||||
Path() string
|
||||
|
||||
URI() string
|
||||
}
|
||||
|
||||
@@ -208,6 +221,59 @@ func (c *context) SetHeader(key, value string) {
|
||||
c.ctx.Header(key, value)
|
||||
}
|
||||
|
||||
func (c *context) UserID() int {
|
||||
val, ok := c.ctx.Get(_UserID)
|
||||
if !ok {
|
||||
return 0
|
||||
}
|
||||
|
||||
return val.(int)
|
||||
}
|
||||
|
||||
func (c *context) setUserID(userID int) {
|
||||
c.ctx.Set(_UserID, userID)
|
||||
}
|
||||
|
||||
func (c *context) UserName() string {
|
||||
val, ok := c.ctx.Get(_UserName)
|
||||
if !ok {
|
||||
return ""
|
||||
}
|
||||
|
||||
return val.(string)
|
||||
}
|
||||
|
||||
func (c *context) setUserName(userName string) {
|
||||
c.ctx.Set(_UserName, userName)
|
||||
}
|
||||
|
||||
func (c *context) AbortWithError(err errno.Error) {
|
||||
if err != nil {
|
||||
c.ctx.AbortWithStatus(http.StatusInternalServerError)
|
||||
c.ctx.Set(_AbortErrorName, err)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *context) abortError() errno.Error {
|
||||
err, _ := c.ctx.Get(_AbortErrorName)
|
||||
return err.(errno.Error)
|
||||
}
|
||||
|
||||
func (c *context) Alias() string {
|
||||
path, ok := c.ctx.Get(_Alias)
|
||||
if !ok {
|
||||
return ""
|
||||
}
|
||||
|
||||
return path.(string)
|
||||
}
|
||||
|
||||
func (c *context) setAlias(path string) {
|
||||
if path = strings.TrimSpace(path); path != "" {
|
||||
c.ctx.Set(_Alias, path)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *context) RawData() []byte {
|
||||
body, ok := c.ctx.Get(_BodyName)
|
||||
if !ok {
|
||||
|
||||
@@ -19,6 +19,7 @@ import (
|
||||
cors "github.com/rs/cors/wrapper/gin"
|
||||
ginSwagger "github.com/swaggo/gin-swagger"
|
||||
"github.com/swaggo/gin-swagger/swaggerFiles"
|
||||
"go.uber.org/multierr"
|
||||
"go.uber.org/zap"
|
||||
"golang.org/x/time/rate"
|
||||
)
|
||||
@@ -41,6 +42,7 @@ type option struct {
|
||||
disableSwagger bool
|
||||
disablePrometheus bool
|
||||
panicNotify OnPanicNotify
|
||||
recordMetrics RecordMetrics
|
||||
enableCors bool
|
||||
enableRate bool
|
||||
}
|
||||
@@ -48,6 +50,10 @@ type option struct {
|
||||
// OnPanicNotify 发生panic时通知用
|
||||
type OnPanicNotify func(ctx Context, err interface{}, stackInfo string)
|
||||
|
||||
// RecordMetrics 记录prometheus指标用
|
||||
// 如果使用AliasForRecordMetrics配置了别名,uri将被替换为别名。
|
||||
type RecordMetrics func(method, uri string, success bool, httpCode, businessCode int, costSeconds float64)
|
||||
|
||||
// WithDisablePProf 禁用 pprof
|
||||
func WithDisablePProf() Option {
|
||||
return func(opt *option) {
|
||||
@@ -73,6 +79,14 @@ func WithDisableproPrometheus() Option {
|
||||
func WithPanicNotify(notify OnPanicNotify) Option {
|
||||
return func(opt *option) {
|
||||
opt.panicNotify = notify
|
||||
fmt.Println(color.Green("* [register panic notify]"))
|
||||
}
|
||||
}
|
||||
|
||||
// WithRecordMetrics 设置记录prometheus记录指标回调
|
||||
func WithRecordMetrics(record RecordMetrics) Option {
|
||||
return func(opt *option) {
|
||||
opt.recordMetrics = record
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,12 +94,14 @@ func WithPanicNotify(notify OnPanicNotify) Option {
|
||||
func WithEnableCors() Option {
|
||||
return func(opt *option) {
|
||||
opt.enableCors = true
|
||||
fmt.Println(color.Green("* [register cors]"))
|
||||
}
|
||||
}
|
||||
|
||||
func WithEnableRate() Option {
|
||||
return func(opt *option) {
|
||||
opt.enableRate = true
|
||||
fmt.Println(color.Green("* [register rate]"))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,6 +110,27 @@ func DisableJournal(ctx Context) {
|
||||
ctx.disableJournal()
|
||||
}
|
||||
|
||||
// AliasForRecordMetrics 对请求uri起个别名,用于prometheus记录指标。
|
||||
// 如:Get /user/:username 这样的uri,因为username会有非常多的情况,这样记录prometheus指标会非常的不有好。
|
||||
func AliasForRecordMetrics(path string) HandlerFunc {
|
||||
return func(ctx Context) {
|
||||
ctx.setAlias(path)
|
||||
}
|
||||
}
|
||||
|
||||
// WrapAuthHandler 用来处理 Auth 的入口,在之后的handler中只需 ctx.UserID() ctx.UserName() 即可。
|
||||
func WrapAuthHandler(handler func(Context) (userID int, userName string, err errno.Error)) HandlerFunc {
|
||||
return func(ctx Context) {
|
||||
userID, userName, err := handler(ctx)
|
||||
if err != nil {
|
||||
ctx.AbortWithError(err)
|
||||
return
|
||||
}
|
||||
ctx.setUserID(userID)
|
||||
ctx.setUserName(userName)
|
||||
}
|
||||
}
|
||||
|
||||
// RouterGroup 包装gin的RouterGroup
|
||||
type RouterGroup interface {
|
||||
Group(string, ...HandlerFunc) RouterGroup
|
||||
@@ -199,8 +236,6 @@ func New(logger *zap.Logger, options ...Option) (Mux, error) {
|
||||
|
||||
gin.SetMode(gin.ReleaseMode)
|
||||
gin.DisableBindValidation()
|
||||
engine := gin.New()
|
||||
|
||||
mux := &mux{
|
||||
engine: gin.New(),
|
||||
}
|
||||
@@ -232,15 +267,18 @@ func New(logger *zap.Logger, options ...Option) (Mux, error) {
|
||||
}
|
||||
|
||||
if !opt.disablePProf {
|
||||
pprof.Register(engine) // register pprof to gin
|
||||
pprof.Register(mux.engine) // register pprof to gin
|
||||
fmt.Println(color.Green("* [register pprof]"))
|
||||
}
|
||||
|
||||
if !opt.disableSwagger {
|
||||
mux.engine.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler)) // register swagger
|
||||
fmt.Println(color.Green("* [register swagger]"))
|
||||
}
|
||||
|
||||
if !opt.disablePrometheus {
|
||||
mux.engine.GET("/metrics", gin.WrapH(promhttp.Handler())) // register prometheus
|
||||
fmt.Println(color.Green("* [register prometheus]"))
|
||||
}
|
||||
|
||||
if opt.enableCors {
|
||||
@@ -303,18 +341,48 @@ func New(logger *zap.Logger, options ...Option) (Mux, error) {
|
||||
return
|
||||
}
|
||||
|
||||
response := context.GetPayload()
|
||||
if x := context.Journal(); x != nil {
|
||||
context.SetHeader(journal.JournalHeader, x.ID())
|
||||
response.WithID(x.ID())
|
||||
var (
|
||||
response errno.Error
|
||||
abortErr error
|
||||
)
|
||||
|
||||
if ctx.IsAborted() {
|
||||
for i := range ctx.Errors { // gin error
|
||||
multierr.AppendInto(&abortErr, ctx.Errors[i])
|
||||
}
|
||||
|
||||
if err := context.abortError(); err != nil { // customer err
|
||||
multierr.AppendInto(&abortErr, errors.New(err.GetMsg()))
|
||||
response = err
|
||||
}
|
||||
} else {
|
||||
response.WithID("")
|
||||
response = context.GetPayload()
|
||||
}
|
||||
|
||||
if response != nil {
|
||||
if x := context.Journal(); x != nil {
|
||||
context.SetHeader(journal.JournalHeader, x.ID())
|
||||
response.WithID(x.ID())
|
||||
} else {
|
||||
response.WithID("")
|
||||
}
|
||||
ctx.JSON(http.StatusOK, response)
|
||||
}
|
||||
|
||||
if opt.recordMetrics != nil {
|
||||
uri := context.URI()
|
||||
if alias := context.Alias(); alias != "" {
|
||||
uri = alias
|
||||
}
|
||||
|
||||
businessCode := 0
|
||||
if response != nil {
|
||||
businessCode = response.GetCode()
|
||||
}
|
||||
|
||||
opt.recordMetrics(context.Method(), uri, !ctx.IsAborted() && ctx.Writer.Status() == http.StatusOK, ctx.Writer.Status(), businessCode, time.Since(ts).Seconds())
|
||||
}
|
||||
|
||||
var j *journal.Journal
|
||||
if x := context.Journal(); x != nil {
|
||||
j = x.(*journal.Journal)
|
||||
|
||||
@@ -13,6 +13,10 @@ type Error interface {
|
||||
WithData(data interface{}) Error
|
||||
// WithID 设置当前请求的唯一ID
|
||||
WithID(id string) Error
|
||||
// GetCode 获取 Code
|
||||
GetCode() int
|
||||
// GetMsg 获取 Msg
|
||||
GetMsg() string
|
||||
// ToString 返回 JSON 格式的错误详情
|
||||
ToString() string
|
||||
}
|
||||
@@ -44,6 +48,14 @@ func (e *err) WithID(id string) Error {
|
||||
return e
|
||||
}
|
||||
|
||||
func (e *err) GetCode() int {
|
||||
return e.Code
|
||||
}
|
||||
|
||||
func (e *err) GetMsg() string {
|
||||
return e.Msg
|
||||
}
|
||||
|
||||
// ToString 返回 JSON 格式的错误详情
|
||||
func (e *err) ToString() string {
|
||||
err := &struct {
|
||||
|
||||
6
internal/pkg/metrics/metrics.go
Normal file
6
internal/pkg/metrics/metrics.go
Normal file
@@ -0,0 +1,6 @@
|
||||
package metrics
|
||||
|
||||
// RecordMetrics 记录指标
|
||||
func RecordMetrics(method, uri string, success bool, httpCode, businessCode int, costSeconds float64) {
|
||||
//fmt.Printf(">>>>>>>Metrics\nmethod:%s\nuri:%s\nsuccess:%t\nhttp code:%d\nbusiness code:%d\ncost seconds:%.9f\n<<<<<<<\n", method, uri, success, httpCode, businessCode, costSeconds)
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
package notify
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
|
||||
"github.com/xinliangnote/go-gin-api/pkg/color"
|
||||
)
|
||||
|
||||
// Email
|
||||
func Email(ctx core.Context, err interface{}, stackInfo string) {
|
||||
|
||||
buf := bytes.NewBuffer(nil)
|
||||
buf.WriteString(fmt.Sprintf("URI: %s %s%s <br/>", ctx.Method(), ctx.Host(), ctx.URI()))
|
||||
buf.WriteString(fmt.Sprintf("JournalID: %s <br/>", ctx.Journal().ID()))
|
||||
buf.WriteString(fmt.Sprintf("ErrorInfo: %+v <br/>", err))
|
||||
buf.WriteString(fmt.Sprintf("StackInfo: <br/>"))
|
||||
|
||||
for _, v := range strings.Split(stackInfo, "\n") {
|
||||
buf.WriteString(v)
|
||||
buf.WriteString(" <br/>")
|
||||
}
|
||||
|
||||
content := buf.String()
|
||||
|
||||
fmt.Println(color.Red(content))
|
||||
}
|
||||
39
internal/pkg/notify/mail/mail.go
Normal file
39
internal/pkg/notify/mail/mail.go
Normal file
@@ -0,0 +1,39 @@
|
||||
package mail
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"gopkg.in/gomail.v2"
|
||||
)
|
||||
|
||||
type Options struct {
|
||||
MailHost string
|
||||
MailPort int
|
||||
MailUser string // 发件人
|
||||
MailPass string // 发件人密码
|
||||
MailTo string // 收件人 多个用,分割
|
||||
Subject string // 邮件主题
|
||||
Body string // 邮件内容
|
||||
}
|
||||
|
||||
func Send(o *Options) error {
|
||||
|
||||
m := gomail.NewMessage()
|
||||
|
||||
//设置发件人
|
||||
m.SetHeader("From", o.MailUser)
|
||||
|
||||
//设置发送给多个用户
|
||||
mailArrTo := strings.Split(o.MailTo, ",")
|
||||
m.SetHeader("To", mailArrTo...)
|
||||
|
||||
//设置邮件主题
|
||||
m.SetHeader("Subject", o.Subject)
|
||||
|
||||
//设置邮件正文
|
||||
m.SetBody("text/html", o.Body)
|
||||
|
||||
d := gomail.NewDialer(o.MailHost, o.MailPort, o.MailUser, o.MailPass)
|
||||
|
||||
return d.DialAndSend(m)
|
||||
}
|
||||
23
internal/pkg/notify/mail/mail_test.go
Normal file
23
internal/pkg/notify/mail/mail_test.go
Normal file
@@ -0,0 +1,23 @@
|
||||
package mail
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestSend(t *testing.T) {
|
||||
options := &Options{
|
||||
MailHost: "smtp.163.com",
|
||||
MailPort: 465,
|
||||
MailUser: "xxx@163.com",
|
||||
MailPass: "", //密码或授权码
|
||||
MailTo: "",
|
||||
Subject: "subject",
|
||||
Body: "body",
|
||||
}
|
||||
err := Send(options)
|
||||
if err != nil {
|
||||
t.Error("Mail Send error", err)
|
||||
return
|
||||
}
|
||||
t.Log("success")
|
||||
}
|
||||
44
internal/pkg/notify/mail/template.go
Normal file
44
internal/pkg/notify/mail/template.go
Normal file
@@ -0,0 +1,44 @@
|
||||
package mail
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"time"
|
||||
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/notify/mail/templates"
|
||||
)
|
||||
|
||||
// NewPanicHTMLEmail 发送系统异常邮件 html
|
||||
func NewPanicHTMLEmail(method, host, uri, id string, msg interface{}, stack string) (subject string, body string, err error) {
|
||||
mailData := &struct {
|
||||
URL string
|
||||
ID string
|
||||
Msg string
|
||||
Stack string
|
||||
Year int
|
||||
}{
|
||||
URL: fmt.Sprintf("%s %s%s", method, host, uri),
|
||||
ID: id,
|
||||
Msg: fmt.Sprintf("%+v", msg),
|
||||
Stack: stack,
|
||||
Year: time.Now().Year(),
|
||||
}
|
||||
|
||||
mailTplContent, err := getEmailHTMLContent(templates.PanicMail, mailData)
|
||||
return fmt.Sprintf("[系统异常]-%s", uri), mailTplContent, err
|
||||
}
|
||||
|
||||
// getEmailHTMLContent 获取邮件模板
|
||||
func getEmailHTMLContent(mailTpl string, mailData interface{}) (string, error) {
|
||||
tpl, err := template.New("email tpl").Parse(mailTpl)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
buffer := new(bytes.Buffer)
|
||||
err = tpl.Execute(buffer, mailData)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return buffer.String(), nil
|
||||
}
|
||||
119
internal/pkg/notify/mail/templates/panic-mail.go
Normal file
119
internal/pkg/notify/mail/templates/panic-mail.go
Normal file
@@ -0,0 +1,119 @@
|
||||
package templates
|
||||
|
||||
const PanicMail = `
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
|
||||
|
||||
<style type="text/css" rel="stylesheet" media="all">
|
||||
/* Media Queries */
|
||||
@media only screen and (max-width: 500px) {
|
||||
.button {
|
||||
width: 100% !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
|
||||
<body style="margin: 0; padding: 0; width: 100%; background-color: #F2F4F6;">
|
||||
<table width="100%" cellpadding="0" cellspacing="0">
|
||||
<tr>
|
||||
<td style="width: 100%; margin: 0; padding: 0; background-color: #F2F4F6;" align="center">
|
||||
<table width="100%" cellpadding="0" cellspacing="0">
|
||||
<!-- Logo -->
|
||||
<tr>
|
||||
<td style="padding: 25px 0; text-align: center;">
|
||||
系统异常
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<!-- Email Body -->
|
||||
<tr>
|
||||
<td style="width: 100%; margin: 0; padding: 0; border-top: 1px solid #EDEFF2; border-bottom: 1px solid #EDEFF2; background-color: #FFF;"
|
||||
width="100%">
|
||||
<table style="width: auto; max-width: 750px; margin: 0 auto; padding: 0;" align="center"
|
||||
width="750" cellpadding="0" cellspacing="0">
|
||||
<tr>
|
||||
<td style="font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif; padding: 35px;">
|
||||
<!-- Greeting -->
|
||||
<h1 style="margin-top: 0; color: #2F3133; font-size: 19px; font-weight: bold; text-align: left;">
|
||||
Hello!
|
||||
</h1>
|
||||
|
||||
<!-- Intro -->
|
||||
<p style="margin-top: 0; color: #74787E; line-height: 1.5em;">
|
||||
您收到此电子邮件,请紧急安排处理。
|
||||
</p>
|
||||
|
||||
<!-- Action Button -->
|
||||
<table style="width: 100%; margin: 30px auto; padding: 0;"
|
||||
width="100%" cellpadding="0" cellspacing="0">
|
||||
<tr style="margin-top: 0; color: #74787E; line-height: 1.5em;">
|
||||
<td style="width: 10%;">
|
||||
ID:
|
||||
</td>
|
||||
<td style="width: 90%">
|
||||
{{.ID}}
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr style="margin-top: 0; color: #74787E; font-size: 16px; line-height: 1.5em;">
|
||||
<td style="width: 10%;">
|
||||
URL:
|
||||
</td>
|
||||
<td style="width: 90%">
|
||||
{{.URL}}
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr style="margin-top: 0; color: #74787E; font-size: 16px; line-height: 1.5em;">
|
||||
<td style="width: 10%;">
|
||||
Error:
|
||||
</td>
|
||||
<td style="width: 90%">
|
||||
{{.Msg}}
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr style="margin-top: 0; color: #74787E; font-size: 16px; line-height: 1.5em;">
|
||||
<td style="width: 10%;">
|
||||
Stack:
|
||||
</td>
|
||||
<td style="width: 90%;">
|
||||
{{.Stack}}}
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<!-- Footer -->
|
||||
<tr>
|
||||
<td>
|
||||
<table style="width: auto; max-width: 570px; margin: 0 auto; padding: 0; text-align: center;"
|
||||
align="center" width="750" cellpadding="0" cellspacing="0">
|
||||
<tr>
|
||||
<td style="font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif; color: #AEAEAE; padding: 35px; text-align: center;">
|
||||
<p style="margin-top: 0; color: #74787E; font-size: 12px; line-height: 1.5em;">
|
||||
© {{.Year}}
|
||||
All rights reserved.
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
||||
`
|
||||
38
internal/pkg/notify/notify.go
Normal file
38
internal/pkg/notify/notify.go
Normal file
@@ -0,0 +1,38 @@
|
||||
package notify
|
||||
|
||||
import (
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/configs"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/notify/mail"
|
||||
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
// OnPanicNotify
|
||||
func OnPanicNotify(ctx core.Context, err interface{}, stackInfo string) {
|
||||
cfg := configs.Get().Mail
|
||||
if cfg.Host == "" || cfg.Port == 0 || cfg.User == "" || cfg.Pass == "" || cfg.To == "" {
|
||||
ctx.Logger().Error("Mail config error")
|
||||
return
|
||||
}
|
||||
|
||||
subject, body, htmlErr := mail.NewPanicHTMLEmail(ctx.Method(), ctx.Host(), ctx.URI(), ctx.Journal().ID(), err, stackInfo)
|
||||
if htmlErr != nil {
|
||||
ctx.Logger().Error("NewPanicHTMLEmail error", zap.Error(htmlErr))
|
||||
return
|
||||
}
|
||||
options := &mail.Options{
|
||||
MailHost: cfg.Host,
|
||||
MailPort: cfg.Port,
|
||||
MailUser: cfg.User,
|
||||
MailPass: cfg.Pass,
|
||||
MailTo: cfg.To,
|
||||
Subject: subject,
|
||||
Body: body,
|
||||
}
|
||||
sendErr := mail.Send(options)
|
||||
if sendErr != nil {
|
||||
ctx.Logger().Error("Mail Send error", zap.Error(sendErr))
|
||||
}
|
||||
return
|
||||
}
|
||||
53
internal/pkg/token/token.go
Normal file
53
internal/pkg/token/token.go
Normal file
@@ -0,0 +1,53 @@
|
||||
package token
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/dgrijalva/jwt-go"
|
||||
)
|
||||
|
||||
//var secret = configs.Get().JWT.Secret
|
||||
var secret = "i1ydX9RtHyuJTrw7frcu"
|
||||
|
||||
type claims struct {
|
||||
UserID int
|
||||
UserName string
|
||||
jwt.StandardClaims
|
||||
}
|
||||
|
||||
func Sign(userId int, userName string) (tokenString string, err error) {
|
||||
// The token content.
|
||||
// iss: (Issuer)签发者
|
||||
// iat: (Issued At)签发时间,用Unix时间戳表示
|
||||
// exp: (Expiration Time)过期时间,用Unix时间戳表示
|
||||
// aud: (Audience)接收该JWT的一方
|
||||
// sub: (Subject)该JWT的主题
|
||||
// nbf: (Not Before)不要早于这个时间
|
||||
// jti: (JWT ID)用于标识JWT的唯一ID
|
||||
claims := claims{
|
||||
userId,
|
||||
userName,
|
||||
jwt.StandardClaims{
|
||||
NotBefore: time.Now().Unix(),
|
||||
IssuedAt: time.Now().Unix(),
|
||||
ExpiresAt: time.Now().Add(24 * time.Hour).Unix(),
|
||||
Issuer: "go-gin-api",
|
||||
},
|
||||
}
|
||||
tokenString, err = jwt.NewWithClaims(jwt.SigningMethodHS256, claims).SignedString([]byte(secret))
|
||||
return
|
||||
}
|
||||
|
||||
func Parse(tokenString string) (*claims, error) {
|
||||
tokenClaims, err := jwt.ParseWithClaims(tokenString, &claims{}, func(token *jwt.Token) (interface{}, error) {
|
||||
return []byte(secret), nil
|
||||
})
|
||||
|
||||
if tokenClaims != nil {
|
||||
if claims, ok := tokenClaims.Claims.(*claims); ok && tokenClaims.Valid {
|
||||
return claims, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, err
|
||||
}
|
||||
26
internal/pkg/token/token_test.go
Normal file
26
internal/pkg/token/token_test.go
Normal file
@@ -0,0 +1,26 @@
|
||||
package token
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
// 执行 Test 时,先将 token.secret 设置值
|
||||
|
||||
func TestSign(t *testing.T) {
|
||||
tokenString, err := Sign(123456789, "xinliangnote")
|
||||
if err != nil {
|
||||
t.Error("sign error", err)
|
||||
return
|
||||
}
|
||||
t.Log(tokenString)
|
||||
}
|
||||
|
||||
func TestParse(t *testing.T) {
|
||||
tokenString := "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEyMzQ1Njc4OSwidXNlcm5hbWUiOiJ4aW5saWFuZyIsImV4cCI6MTYwOTQ2NzcwNCwiaWF0IjoxNjA5MzgxMzA0LCJpc3MiOiJnby1naW4tYXBpIiwibmJmIjoxNjA5MzgxMzA0fQ.hccv8F713WpKcwiSldBrFLZz_2SZzOTPedPi-8ps7M4"
|
||||
user, err := Parse(tokenString)
|
||||
if err != nil {
|
||||
t.Error("parse error", err)
|
||||
return
|
||||
}
|
||||
t.Log(user)
|
||||
}
|
||||
29
internal/router/middleware/auth.go
Normal file
29
internal/router/middleware/auth.go
Normal file
@@ -0,0 +1,29 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/errno"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/token"
|
||||
)
|
||||
|
||||
func AuthHandler(ctx core.Context) (userId int, userName string, err errno.Error) {
|
||||
auth := ctx.GetHeader("Authorization")
|
||||
if auth == "" {
|
||||
err = errno.ErrSignParam
|
||||
return
|
||||
}
|
||||
|
||||
claims, errParse := token.Parse(auth)
|
||||
if errParse != nil {
|
||||
err = errno.ErrSignParam
|
||||
return
|
||||
}
|
||||
|
||||
userId = claims.UserID
|
||||
if userId <= 0 {
|
||||
err = errno.ErrSignParam
|
||||
return
|
||||
}
|
||||
userName = claims.UserName
|
||||
return
|
||||
}
|
||||
@@ -3,7 +3,9 @@ package router
|
||||
import (
|
||||
"github.com/xinliangnote/go-gin-api/internal/api/controller/demo"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/metrics"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/notify"
|
||||
"github.com/xinliangnote/go-gin-api/internal/router/middleware"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"go.uber.org/zap"
|
||||
@@ -18,24 +20,27 @@ func NewHTTPMux(logger *zap.Logger) (core.Mux, error) {
|
||||
mux, err := core.New(logger,
|
||||
core.WithEnableCors(),
|
||||
core.WithEnableRate(),
|
||||
core.WithPanicNotify(notify.Email),
|
||||
core.WithPanicNotify(notify.OnPanicNotify),
|
||||
core.WithRecordMetrics(metrics.RecordMetrics),
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
//设置路由中间件
|
||||
//engine.Use(exception.SetUp(), jaeger.SetUp())
|
||||
|
||||
demoHandler := demo.NewDemo(logger)
|
||||
|
||||
d := mux.Group("/demo")
|
||||
u := mux.Group("/user")
|
||||
{
|
||||
d.GET("user/:name", demoHandler.User())
|
||||
u.POST("/login", demoHandler.Login())
|
||||
}
|
||||
|
||||
d := mux.Group("/demo", core.WrapAuthHandler(middleware.AuthHandler)) //使用 auth 验证
|
||||
{
|
||||
d.GET("user/:name", demoHandler.User(), core.DisableJournal)
|
||||
|
||||
// 模拟数据
|
||||
d.GET("get/:name", demoHandler.Get(), core.DisableJournal)
|
||||
d.GET("get/:name", demoHandler.Get())
|
||||
d.POST("post", demoHandler.Post(), core.DisableJournal)
|
||||
|
||||
// 测试加密性能
|
||||
@@ -44,8 +49,5 @@ func NewHTTPMux(logger *zap.Logger) (core.Mux, error) {
|
||||
d.GET("/md5/test", demoHandler.MD5Test())
|
||||
}
|
||||
|
||||
// 测试链路追踪
|
||||
//mux.GET("/jaeger_test", jaeger_conn.JaegerTest)
|
||||
|
||||
return mux, nil
|
||||
}
|
||||
Reference in New Issue
Block a user