first commit
This commit is contained in:
commit
79c642932c
|
@ -0,0 +1,62 @@
|
|||
# toml配置文件
|
||||
# Wiki:https://github.com/toml-lang/toml
|
||||
ServiceName = "snow"
|
||||
Debug = true
|
||||
Env = "local" # local-本地 develop-开发 beta-预发布 production-线上
|
||||
PrometheusCollectEnable = true
|
||||
SkyWalkingOapServer = "127.0.0.1:11800"
|
||||
|
||||
[Log]
|
||||
Handler = "file"
|
||||
Dir = "./logs"
|
||||
Level = "info"
|
||||
|
||||
[Db]
|
||||
Driver = "mysql"
|
||||
|
||||
[Db.Option]
|
||||
MaxConns = 128
|
||||
MaxIdle = 32
|
||||
IdleTimeout = 180 # second
|
||||
Charset = "utf8mb4"
|
||||
ConnectTimeout = 3 # second
|
||||
|
||||
[Db.Master]
|
||||
Host = "127.0.0.1"
|
||||
Port = 3306
|
||||
User = "root"
|
||||
Password = "123456"
|
||||
DBName = "test"
|
||||
|
||||
[[Db.Slaves]] # 支持多个从库
|
||||
Host = "127.0.0.1"
|
||||
Port = 3306
|
||||
User = "root"
|
||||
Password = "123456"
|
||||
DBName = "test"
|
||||
|
||||
[Api]
|
||||
Host = "0.0.0.0"
|
||||
Port = 8080
|
||||
|
||||
[Cache]
|
||||
Driver = "redis"
|
||||
|
||||
[Redis.Master]
|
||||
Host = "127.0.0.1"
|
||||
Port = 6379
|
||||
#Password = ""
|
||||
#DB = 0
|
||||
|
||||
#[Redis.Option]
|
||||
#MaxIdle = 64
|
||||
#MaxConns = 256
|
||||
#IdleTimeout = 180 # second
|
||||
#ConnectTimeout = 1
|
||||
#ReadTimeout = 1
|
||||
#WriteTimeout = 1
|
||||
|
||||
[AliMns]
|
||||
Url = ""
|
||||
AccessKeyId = ""
|
||||
AccessKeySecret = ""
|
|
@ -0,0 +1,4 @@
|
|||
/.idea
|
||||
/vendor
|
||||
/.env
|
||||
!/.env.example
|
|
@ -0,0 +1,74 @@
|
|||
## Snow
|
||||
Snow是一套简单易用的Go语言业务框架,整体逻辑设计简洁,支持HTTP服务、队列调度和任务调度等常用业务场景模式。
|
||||
|
||||
## Quick start
|
||||
|
||||
### Build
|
||||
sh build/shell/build.sh
|
||||
|
||||
### Run
|
||||
```shell
|
||||
1. build/bin/snow -a api #启动Api服务
|
||||
2. build/bin/snow -a cron #启动Cron定时任务服务
|
||||
3. build/bin/snow -a job #启动队列调度服务
|
||||
4. build/bin/snow -a command -m test #执行名称为test的脚本任务
|
||||
```
|
||||
|
||||
## Documents
|
||||
|
||||
- [项目地址](https://github.com/qit-team/snow)
|
||||
- [中文文档](https://github.com/qit-team/snow/wiki)
|
||||
- [changelog](https://github.com/qit-team/snow/blob/master/CHANGLOG.md)
|
||||
- [xorm](http://gobook.io/read/github.com/go-xorm/manual-zh-CN/)
|
||||
|
||||
##
|
||||
<!--
|
||||
|
||||
// validator的示例
|
||||
// HandleTestValidator godoc
|
||||
// @Summary HandleTestValidator的示例
|
||||
// @Description HandleTestValidator的示例
|
||||
// @Tags snow
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param testValidator body entities.TestValidatorRequest true "example of validator"
|
||||
// @Success 200 {array} entities.TestValidatorRequest
|
||||
// @Failure 400 {object} controllers.HTTPError
|
||||
// @Failure 404 {object} controllers.HTTPError
|
||||
// @Failure 500 {object} controllers.HTTPError
|
||||
// @Router /test_validator [post]
|
||||
func HandleTestValidator(c *gin.Context) {
|
||||
request := new(entities.TestValidatorRequest)
|
||||
err := GenRequest(c, request)
|
||||
if err != nil {
|
||||
Error(c, errorcode.ParamError)
|
||||
return
|
||||
}
|
||||
|
||||
Success(c, request)
|
||||
return
|
||||
}
|
||||
|
||||
// Address houses a users address information
|
||||
type Address struct {
|
||||
Street string `json:"street" validate:"required" example:"huandaodonglu"`
|
||||
City string `json:"city" validate:"required" example:"xiamen"`
|
||||
Planet string `json:"planet" validate:"required" example:"snow"`
|
||||
Phone string `json:"phone" validate:"required" example:"snow"`
|
||||
}
|
||||
|
||||
// 请求数据结构
|
||||
type TestValidatorRequest struct {
|
||||
//tips,因为组件required不管是没传值或者传 0 or "" 都通过不了,但是如果用指针类型,那么0就是0,而nil无法通过校验
|
||||
Id *int64 `json:"id" validate:"required" example:"1"`
|
||||
Age int `json:"age" validate:"required,gte=0,lte=130" example:"20"`
|
||||
Name *string `json:"name" validate:"required" example:"snow"`
|
||||
Email string `json:"email" validate:"required,email" example:"snow@github.com"`
|
||||
Url string `json:"url" validate:"required" example:"github.com/qit-team/snow"`
|
||||
Mobile string `json:"mobile" validate:"required" example:"snow"`
|
||||
RangeNum int `json:"range_num" validate:"max=10,min=1" example:"3"`
|
||||
TestNum *int `json:"test_num" validate:"required,oneof=5 7 9" example:"7"`
|
||||
Content *string `json:"content" example:"snow"`
|
||||
Addresses []*Address `json:"addresses" validate:"required,dive,required" `
|
||||
}
|
||||
-->
|
|
@ -0,0 +1,34 @@
|
|||
package bannerlistcache
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"qteam/app/caches"
|
||||
|
||||
"github.com/qit-team/snow-core/cache"
|
||||
)
|
||||
|
||||
const (
|
||||
prefix = caches.BannerList //缓存key的前缀
|
||||
)
|
||||
|
||||
var (
|
||||
instance *bannerListCache
|
||||
once sync.Once
|
||||
)
|
||||
|
||||
type bannerListCache struct {
|
||||
cache.BaseCache
|
||||
}
|
||||
|
||||
//单例模式
|
||||
func GetInstance() *bannerListCache {
|
||||
once.Do(func() {
|
||||
instance = new(bannerListCache)
|
||||
instance.Prefix = prefix
|
||||
//instance.DiName = redis.SingletonMain //设置缓存依赖的实例别名
|
||||
//instance.DriverType = cache.DriverTypeRedis //设置缓存驱动的类型,默认redis
|
||||
//instance.SeTTL(86400) 设置默认缓存时间 默认86400
|
||||
})
|
||||
return instance
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
package bannerlistcache
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"qteam/config"
|
||||
|
||||
"github.com/qit-team/snow-core/cache"
|
||||
_ "github.com/qit-team/snow-core/cache/rediscache"
|
||||
"github.com/qit-team/snow-core/redis"
|
||||
)
|
||||
|
||||
func init() {
|
||||
//加载配置文件
|
||||
conf, err := config.Load("../../../.env")
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
|
||||
//注册redis类
|
||||
err = redis.Pr.Register(cache.DefaultDiName, conf.Redis)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_GetMulti(t *testing.T) {
|
||||
ctx := context.TODO()
|
||||
cache := GetInstance()
|
||||
res, _ := cache.Set(ctx, "1000", "a")
|
||||
if res != true {
|
||||
t.Errorf("set key:%s is error", "1000")
|
||||
}
|
||||
|
||||
keys := []string{"1000", "-2000", "9999"}
|
||||
cacheList, err := cache.GetMulti(ctx, keys...)
|
||||
if err != nil {
|
||||
t.Errorf("getMulti error:%s", err.Error())
|
||||
}
|
||||
fmt.Println(cacheList)
|
||||
i := 0
|
||||
for k, v := range cacheList {
|
||||
i++
|
||||
if k == "1000" {
|
||||
if v != "a" {
|
||||
t.Errorf("value of key:%s is error %v", k, v)
|
||||
}
|
||||
} else {
|
||||
if v != "" {
|
||||
t.Errorf("value of key:%s is error %v", k, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
if i != len(keys) {
|
||||
t.Errorf("count of cache key is error: %d", i)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
package caches
|
||||
|
||||
//缓存前缀key,不同的业务使用不同的前缀,避免了业务之间的重用冲突
|
||||
const (
|
||||
Cookie = "ck:"
|
||||
Copy = "cp:"
|
||||
BannerList = "bl:"
|
||||
)
|
|
@ -0,0 +1,9 @@
|
|||
package console
|
||||
|
||||
import (
|
||||
"github.com/qit-team/snow-core/command"
|
||||
)
|
||||
|
||||
func RegisterCommand(c *command.Command) {
|
||||
c.AddFunc("test", test)
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
package console
|
||||
|
||||
import (
|
||||
"github.com/robfig/cron"
|
||||
)
|
||||
|
||||
/**
|
||||
* 配置执行计划
|
||||
* @wiki https://godoc.org/github.com/robfig/cron
|
||||
*/
|
||||
func RegisterSchedule(c *cron.Cron) {
|
||||
//c.AddFunc("0 30 * * * *", test)
|
||||
//c.AddFunc("@hourly", test)
|
||||
c.AddFunc("@every 10s", test)
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
package console
|
||||
|
||||
import "fmt"
|
||||
|
||||
func test() {
|
||||
fmt.Println("run test")
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
package common
|
||||
|
||||
const (
|
||||
TOKEN_PRE = "player_token_"
|
||||
TOKEN_Admin = "Admin_token_"
|
||||
ADMIN_V1 = "/admin/api/v1"
|
||||
)
|
|
@ -0,0 +1,5 @@
|
|||
package common
|
||||
|
||||
const (
|
||||
Event_USER_LOG_IN = "user_login"
|
||||
)
|
|
@ -0,0 +1,52 @@
|
|||
package errorcode
|
||||
|
||||
const (
|
||||
//成功
|
||||
Success = 200
|
||||
|
||||
//参数错误
|
||||
ParamError = 400
|
||||
|
||||
//未经授权
|
||||
NotAuth = 401
|
||||
|
||||
//请求被禁止
|
||||
Forbidden = 403
|
||||
|
||||
//找不到页面
|
||||
NotFound = 404
|
||||
|
||||
//系统错误
|
||||
SystemError = 500
|
||||
|
||||
//未登录
|
||||
NotLogin = 1000
|
||||
)
|
||||
|
||||
var MsgEN = map[int]string{
|
||||
Success: "success",
|
||||
ParamError: "param error",
|
||||
NotAuth: "not authorized",
|
||||
Forbidden: "forbidden",
|
||||
NotFound: "not found",
|
||||
SystemError: "system error",
|
||||
}
|
||||
|
||||
var MsgZH = map[int]string{
|
||||
Success: "请求成功",
|
||||
ParamError: "参数错误",
|
||||
NotFound: "数据不存在",
|
||||
NotAuth: "未经授权",
|
||||
NotLogin: "未登录",
|
||||
}
|
||||
var MsgMap map[string]map[int]string = map[string]map[int]string{"en": MsgZH}
|
||||
|
||||
func GetMsg(code int, local string) string {
|
||||
if local == "" {
|
||||
local = "en"
|
||||
}
|
||||
if msg, ok := MsgMap[local][code]; ok {
|
||||
return msg
|
||||
}
|
||||
return ""
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
package logtype
|
||||
|
||||
const (
|
||||
Message = "message"
|
||||
GoPanic = "go.panic"
|
||||
HTTP = "http"
|
||||
)
|
|
@ -0,0 +1,177 @@
|
|||
package controllers
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"github.com/go-playground/locales/zh"
|
||||
ut "github.com/go-playground/universal-translator"
|
||||
"github.com/qit-team/snow-core/redis"
|
||||
"gopkg.in/go-playground/validator.v9"
|
||||
zh_translations "gopkg.in/go-playground/validator.v9/translations/zh"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"qteam/app/utils"
|
||||
"qteam/config"
|
||||
|
||||
"qteam/app/constants/errorcode"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
/**
|
||||
* 成功时返回
|
||||
*/
|
||||
func Success(c *gin.Context, data interface{}, message string) {
|
||||
if message == "" {
|
||||
message = errorcode.GetMsg(errorcode.Success, c.GetHeader("local"))
|
||||
}
|
||||
if config.GetConf().Env == "production" {
|
||||
c.String(http.StatusOK, EncriptJson(gin.H{
|
||||
"code": errorcode.Success,
|
||||
"message": message,
|
||||
"data": data,
|
||||
}))
|
||||
} else {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": errorcode.Success,
|
||||
"message": message,
|
||||
"data": data,
|
||||
})
|
||||
}
|
||||
|
||||
c.Abort()
|
||||
}
|
||||
func EncriptJson(h gin.H) string {
|
||||
var data, err = json.Marshal(h)
|
||||
if err != nil {
|
||||
utils.Log(nil, "encriptJso", err)
|
||||
}
|
||||
rs, err := utils.Des3Encrypt(data, config.GetConf().AppKey)
|
||||
res := base64.StdEncoding.EncodeToString(rs)
|
||||
return res
|
||||
}
|
||||
|
||||
/**
|
||||
* 失败时返回
|
||||
*/
|
||||
func Error(c *gin.Context, code int, msg ...string) {
|
||||
message := ""
|
||||
if len(msg) > 0 {
|
||||
message = msg[0]
|
||||
} else {
|
||||
message = errorcode.GetMsg(code, "")
|
||||
}
|
||||
if config.GetConf().Env == "production" {
|
||||
c.String(http.StatusOK, EncriptJson(gin.H{
|
||||
"code": code,
|
||||
"message": message,
|
||||
"data": make(map[string]string),
|
||||
}))
|
||||
} else {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": code,
|
||||
"message": message,
|
||||
"data": make(map[string]string),
|
||||
})
|
||||
}
|
||||
|
||||
c.Abort()
|
||||
}
|
||||
|
||||
func Error404(c *gin.Context) {
|
||||
Error(c, errorcode.NotFound, "路由不存在")
|
||||
}
|
||||
|
||||
func Error500(c *gin.Context) {
|
||||
Error(c, errorcode.SystemError)
|
||||
}
|
||||
|
||||
type HTTPError struct {
|
||||
Code int `json:"code" example:"400"`
|
||||
Message string `json:"message" example:"status bad request"`
|
||||
}
|
||||
|
||||
/**
|
||||
* 将请求的body转换为request数据结构
|
||||
* @param c
|
||||
* @param request 传入request数据结构的指针 如 new(TestRequest)
|
||||
*/
|
||||
func GenRequest(c *gin.Context, request interface{}) (msgs []string, err error) {
|
||||
if c.Request.Method == "GET" || c.Request.Method == "DELETE" {
|
||||
err = c.ShouldBindQuery(request)
|
||||
} else {
|
||||
err = c.ShouldBindJSON(request)
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
validate := validator.New()
|
||||
zh_ch := zh.New()
|
||||
|
||||
uni := ut.New(zh_ch)
|
||||
trans, _ := uni.GetTranslator("zh")
|
||||
//验证器注册翻译器
|
||||
zh_translations.RegisterDefaultTranslations(validate, trans)
|
||||
errValidate := validate.Struct(request)
|
||||
|
||||
if errValidate != nil {
|
||||
for _, v := range errValidate.(validator.ValidationErrors) {
|
||||
msgs = append(msgs, v.Translate(trans))
|
||||
}
|
||||
err = errors.New(errorcode.GetMsg(errorcode.ParamError, ""))
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// 重复读取body
|
||||
func ReadBody(c *gin.Context) (body []byte, err error) {
|
||||
body, err = ioutil.ReadAll(c.Request.Body)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
c.Request.Body = ioutil.NopCloser(bytes.NewBuffer(body))
|
||||
return
|
||||
}
|
||||
|
||||
func GetRequest(c *gin.Context) interface{} {
|
||||
request, _ := c.Get("request")
|
||||
return request
|
||||
}
|
||||
|
||||
func HandRes(c *gin.Context, data interface{}, err error) {
|
||||
if err == nil {
|
||||
Success(c, data, "")
|
||||
} else {
|
||||
Error(c, errorcode.SystemError, err.Error())
|
||||
}
|
||||
}
|
||||
func HandCodeRes(c *gin.Context, data interface{}, code int) {
|
||||
if utils.IsNil(data) {
|
||||
data = struct{}{}
|
||||
}
|
||||
if code == errorcode.Success {
|
||||
Success(c, data, errorcode.GetMsg(code, c.GetHeader("local")))
|
||||
} else {
|
||||
Error(c, code, errorcode.GetMsg(code, c.GetHeader("local")))
|
||||
}
|
||||
}
|
||||
func GetPlayerId(c *gin.Context) string {
|
||||
playerId, _ := c.Get("playerId")
|
||||
if playerId == nil {
|
||||
return ""
|
||||
}
|
||||
return playerId.(string)
|
||||
}
|
||||
|
||||
func Frequence(key string) bool {
|
||||
if rs := redis.GetRedis().Exists(context.Background(), utils.GetRealKey(key)); rs.String() != "" {
|
||||
return false
|
||||
} else {
|
||||
redis.GetRedis().SetEX(context.Background(), utils.GetRealKey(key), 1, 5)
|
||||
return true
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
package domains
|
||||
|
||||
type Filter struct {
|
||||
Page int `json:"page"`
|
||||
PageSize int `json:"page_size"`
|
||||
}
|
||||
|
||||
type ProductFilter struct {
|
||||
Page int `json:"page" form:"page"`
|
||||
PageSize int `json:"page_size" form:"page_size"`
|
||||
ProductName string `json:"product_name" form:"product_name"`
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
package domains
|
||||
|
||||
import "time"
|
||||
|
||||
type Product struct {
|
||||
Id int
|
||||
ProductName string
|
||||
ProductType int
|
||||
PacketRule int
|
||||
ProductKind int
|
||||
Amount string
|
||||
CouponType int
|
||||
IsSuperposition int
|
||||
TemplateId int
|
||||
Status int
|
||||
IsNeedBill int
|
||||
CreateTime time.Time
|
||||
SupplierProductId int64
|
||||
SupplierProductName string
|
||||
|
||||
Creator string
|
||||
UpdateTime time.Time
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
package entities
|
||||
|
||||
type IdRequest struct {
|
||||
Id int64 `json:"id"`
|
||||
}
|
||||
|
||||
type PageRequest struct {
|
||||
Page int64 `json:"page"`
|
||||
PageSize int64 `json:"pageSize"`
|
||||
}
|
||||
|
||||
type PageRsp struct {
|
||||
Total int64 `json:"total"`
|
||||
Data []interface{} `json:"data"`
|
||||
}
|
|
@ -0,0 +1,77 @@
|
|||
package metric
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"qteam/app/utils/metric"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
)
|
||||
|
||||
const (
|
||||
HOST = "host"
|
||||
PATH = "path" // 路径
|
||||
METHOD = "method" // 方法
|
||||
CODE = "code" // 错误码
|
||||
|
||||
// metric
|
||||
ALL_REQ_TOTAL_COUNT = "all_req_total_count" // 所有URL总请求数
|
||||
ALL_REQ_COST_TIME = "all_req_cost_time" // 所有URL请求耗时
|
||||
|
||||
REQ_TOTAL_COUNT = "req_total_count" // 每个URL总请求数
|
||||
REQ_COST_TIME = "req_cost_time" // 每个URL请求耗时
|
||||
)
|
||||
|
||||
func init() {
|
||||
metric.RegisterCollector(reqTotalCounter, reqCostTimeObserver, allReqTotalCounter, allReqCostTimeObserver)
|
||||
}
|
||||
|
||||
var (
|
||||
reqTotalCounter = prometheus.NewCounterVec(prometheus.CounterOpts{
|
||||
Name: REQ_TOTAL_COUNT,
|
||||
}, []string{PATH, METHOD})
|
||||
|
||||
reqCostTimeObserver = prometheus.NewHistogramVec(prometheus.HistogramOpts{
|
||||
Name: REQ_COST_TIME,
|
||||
Buckets: []float64{
|
||||
100,
|
||||
200,
|
||||
500,
|
||||
1000,
|
||||
3000,
|
||||
5000,
|
||||
},
|
||||
}, []string{PATH, METHOD})
|
||||
|
||||
allReqTotalCounter = prometheus.NewCounterVec(prometheus.CounterOpts{
|
||||
Name: ALL_REQ_TOTAL_COUNT,
|
||||
}, []string{HOST})
|
||||
|
||||
allReqCostTimeObserver = prometheus.NewHistogramVec(prometheus.HistogramOpts{
|
||||
Name: ALL_REQ_COST_TIME,
|
||||
Buckets: []float64{
|
||||
100,
|
||||
200,
|
||||
500,
|
||||
1000,
|
||||
3000,
|
||||
5000,
|
||||
},
|
||||
}, []string{HOST})
|
||||
)
|
||||
|
||||
func AddReqCount(req *http.Request) {
|
||||
reqTotalCounter.WithLabelValues(req.URL.Path, req.Method).Inc()
|
||||
}
|
||||
|
||||
func CollectReqCostTime(req *http.Request, ms int64) {
|
||||
reqCostTimeObserver.WithLabelValues(req.URL.Path, req.Method).Observe(float64(ms))
|
||||
}
|
||||
|
||||
func AddAllReqCount(req *http.Request) {
|
||||
allReqTotalCounter.WithLabelValues(req.Host).Inc()
|
||||
}
|
||||
|
||||
func CollectAllReqCostTime(req *http.Request, ms int64) {
|
||||
allReqCostTimeObserver.WithLabelValues(req.Host).Observe(float64(ms))
|
||||
}
|
|
@ -0,0 +1,105 @@
|
|||
package middlewares
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/qit-team/snow-core/redis"
|
||||
"qteam/app/constants/common"
|
||||
"qteam/app/constants/errorcode"
|
||||
"qteam/app/http/controllers"
|
||||
"qteam/app/http/requestmapping"
|
||||
"qteam/app/utils"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func Auth() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
c.ClientIP()
|
||||
var tokens = strings.SplitN(c.GetHeader("Authorization"), " ", 2)
|
||||
if len(tokens) != 2 || tokens[0] != "Bearer" {
|
||||
controllers.HandCodeRes(c, nil, errorcode.NotLogin)
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
// 验证token
|
||||
token, claims, err := utils.ParseToken(tokens[1])
|
||||
if err != nil || !token.Valid {
|
||||
controllers.HandCodeRes(c, nil, errorcode.NotAuth)
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
if err == nil {
|
||||
c.Set("userId", claims.Id)
|
||||
c.Set("phone", claims.Phone)
|
||||
c.Next()
|
||||
return
|
||||
} else {
|
||||
controllers.HandCodeRes(c, nil, errorcode.NotAuth)
|
||||
c.Abort()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func Cors() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
c.Writer.Header().Set("Access-Control-Allow-Origin", "*")
|
||||
c.Writer.Header().Set("Access-Control-Allow-Credentials", "true")
|
||||
c.Writer.Header().Set("Access-Control-Allow-Headers", "Content-Type, Content-Length, platform,Accept-Encoding, X-CSRF-Token, Authorization, accept, origin, Cache-Control,token, X-Requested-With")
|
||||
c.Writer.Header().Set("Access-Control-Allow-Methods", "POST, OPTIONS, GET, PUT, DELETE")
|
||||
|
||||
if c.Request.Method == "OPTIONS" {
|
||||
c.AbortWithStatus(204)
|
||||
return
|
||||
}
|
||||
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
|
||||
func AdminAuth() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
var token = c.GetHeader("token")
|
||||
//将token放入redis
|
||||
var playerId, err = redis.GetRedis().Get(context.Background(), utils.GetRealKey(common.TOKEN_Admin+token)).Result()
|
||||
if rs, errRedis := redis.GetRedis().SIsMember(context.Background(), "disabled_uids", playerId).Result(); errRedis == nil && rs {
|
||||
err = errors.New(errorcode.GetMsg(errorcode.NotFound, ""))
|
||||
redis.GetRedis().SRem(context.Background(), "disabled_uids", playerId)
|
||||
}
|
||||
if err == nil {
|
||||
c.Set("playerId", playerId)
|
||||
c.Next()
|
||||
return
|
||||
} else {
|
||||
controllers.HandCodeRes(c, nil, errorcode.Forbidden)
|
||||
c.Abort()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func ValidateRequest() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
var path = c.FullPath()
|
||||
var handler func() interface{}
|
||||
if strings.Index(path, "admin") >= 0 {
|
||||
handler = requestmapping.BackendRequestMap[path]
|
||||
} else {
|
||||
handler = requestmapping.FrontRequestMap[path]
|
||||
}
|
||||
if handler == nil {
|
||||
utils.Log(c, "path", path)
|
||||
controllers.HandCodeRes(c, nil, errorcode.NotFound)
|
||||
} else {
|
||||
v := handler()
|
||||
msg, err := controllers.GenRequest(c, v)
|
||||
if err != nil {
|
||||
utils.Log(c, "path", path)
|
||||
controllers.Error(c, errorcode.ParamError, msg...)
|
||||
} else {
|
||||
c.Set("request", v)
|
||||
c.Next()
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
package middlewares
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"qteam/app/http/metric"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func CollectMetric() gin.HandlerFunc {
|
||||
return func(ctx *gin.Context) {
|
||||
start := time.Now()
|
||||
ctx.Next()
|
||||
dur := time.Now().Sub(start).Milliseconds()
|
||||
|
||||
metric.AddAllReqCount(ctx.Request)
|
||||
metric.CollectAllReqCostTime(ctx.Request, dur)
|
||||
metric.AddReqCount(ctx.Request)
|
||||
metric.CollectReqCostTime(ctx.Request, dur)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
package middlewares
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
syslog "log"
|
||||
"net/http/httputil"
|
||||
"runtime/debug"
|
||||
|
||||
"qteam/app/constants/logtype"
|
||||
"qteam/config"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/qit-team/snow-core/log/logger"
|
||||
)
|
||||
|
||||
func ServerRecovery() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
httpRequest, _ := httputil.DumpRequest(c.Request, false)
|
||||
msg := map[string]interface{}{
|
||||
"error": err,
|
||||
"request": string(httpRequest),
|
||||
"stack": string(debug.Stack()),
|
||||
}
|
||||
msgJson, _ := json.Marshal(msg)
|
||||
logger.GetLogger().Error(string(msgJson), logtype.GoPanic, c)
|
||||
|
||||
if config.IsDebug() {
|
||||
//本地开发 debug 模式开启时输出错误信息到shell
|
||||
syslog.Println(err)
|
||||
}
|
||||
|
||||
c.JSON(500, gin.H{
|
||||
"code": 500,
|
||||
"msg": "system error",
|
||||
"request_uri": c.Request.URL.Path,
|
||||
"data": make(map[string]string),
|
||||
})
|
||||
}
|
||||
}()
|
||||
|
||||
//before request
|
||||
|
||||
c.Next()
|
||||
|
||||
//after request
|
||||
}
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
package middlewares
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"qteam/app/http/trace"
|
||||
"qteam/app/utils"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
const (
|
||||
componentIDGOHttpServer = 5004
|
||||
)
|
||||
|
||||
func Trace() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
tracer, err := trace.Tracer()
|
||||
if err != nil {
|
||||
utils.Log(c, "trace err", err)
|
||||
}
|
||||
span := tracer.StartSpan("base trace")
|
||||
|
||||
// 可以自定义tag
|
||||
span.SetName(c.Request.RequestURI)
|
||||
span.Tag("methd", c.Request.Method)
|
||||
c.Request = c.Request.WithContext(c)
|
||||
c.Next()
|
||||
code := c.Writer.Status()
|
||||
span.Tag("status", strconv.Itoa(code))
|
||||
span.Finish()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
package requestmapping
|
||||
|
||||
var BackendRequestMap = map[string]func() interface{}{
|
||||
|
||||
//common.ADMIN_V1 + "/product/create": func() interface{} {
|
||||
// return new(backend.ProductCreateRequest)
|
||||
//},
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
package requestmapping
|
||||
|
||||
var FrontRequestMap = map[string]func() interface{}{
|
||||
//"/v1/login": func() interface{} {
|
||||
// return new(front.LoginRequest)
|
||||
//},
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
package routes
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/qit-team/snow-core/http/middleware"
|
||||
"qteam/app/http/controllers"
|
||||
"qteam/app/http/middlewares"
|
||||
"qteam/app/http/trace"
|
||||
"qteam/app/utils"
|
||||
"qteam/config"
|
||||
)
|
||||
|
||||
func RegisterAdminRoute(router *gin.Engine) {
|
||||
router.Use(middlewares.ServerRecovery(), middleware.GenRequestId, middleware.GenContextKit, middleware.AccessLog())
|
||||
router.NoRoute(controllers.Error404)
|
||||
if len(config.GetConf().SkyWalkingOapServer) > 0 && config.IsEnvEqual(config.ProdEnv) {
|
||||
err := trace.InitTracer(config.GetConf().ServiceName, config.GetConf().SkyWalkingOapServer)
|
||||
if err != nil {
|
||||
utils.Log(nil, "InitTracer", err.Error())
|
||||
} else {
|
||||
router.Use(middlewares.Trace())
|
||||
}
|
||||
}
|
||||
|
||||
//v1 := router.Group("/admin/api/v1")
|
||||
//{
|
||||
//
|
||||
//}
|
||||
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
package routes
|
||||
|
||||
/**
|
||||
* 配置路由
|
||||
*/
|
||||
import (
|
||||
"qteam/app/http/controllers"
|
||||
"qteam/app/http/middlewares"
|
||||
"qteam/app/http/trace"
|
||||
"qteam/app/utils/metric"
|
||||
"qteam/config"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/qit-team/snow-core/http/middleware"
|
||||
"github.com/qit-team/snow-core/log/logger"
|
||||
"github.com/swaggo/gin-swagger"
|
||||
"github.com/swaggo/gin-swagger/swaggerFiles"
|
||||
)
|
||||
|
||||
// api路由配置
|
||||
func RegisterRoute(router *gin.Engine) {
|
||||
//middleware: 服务错误处理 => 生成请求id => access log
|
||||
router.Use(middlewares.ServerRecovery(), middleware.GenRequestId, middleware.GenContextKit, middleware.AccessLog())
|
||||
|
||||
if config.GetConf().PrometheusCollectEnable && config.IsEnvEqual(config.ProdEnv) {
|
||||
router.Use(middlewares.CollectMetric())
|
||||
metric.Init(metric.EnableRuntime(), metric.EnableProcess())
|
||||
metricHandler := metric.Handler()
|
||||
router.GET("/metrics", func(ctx *gin.Context) {
|
||||
metricHandler.ServeHTTP(ctx.Writer, ctx.Request)
|
||||
})
|
||||
}
|
||||
|
||||
if len(config.GetConf().SkyWalkingOapServer) > 0 && config.IsEnvEqual(config.ProdEnv) {
|
||||
err := trace.InitTracer(config.GetConf().ServiceName, config.GetConf().SkyWalkingOapServer)
|
||||
if err != nil {
|
||||
logger.Error(nil, "InitTracer", err.Error())
|
||||
} else {
|
||||
router.Use(middlewares.Trace())
|
||||
}
|
||||
}
|
||||
|
||||
router.Use(middlewares.Cors())
|
||||
router.NoRoute(controllers.Error404)
|
||||
|
||||
//api版本
|
||||
//v1 := router.Group("/v1", middlewares.ValidateRequest())
|
||||
//{
|
||||
//
|
||||
//}
|
||||
|
||||
router.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
|
||||
|
||||
//router.GET("/hello", controllers.HelloHandler)
|
||||
//router.GET("/create", controllers.HelloCreateHandler)
|
||||
//router.GET("/update", controllers.UpdateHandler)
|
||||
//router.GET("/delete", controllers.DeleteHandler)
|
||||
//router.GET("/query", controllers.QueryHandler)
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
package trace
|
||||
|
||||
import (
|
||||
"github.com/openzipkin/zipkin-go"
|
||||
zkHttp "github.com/openzipkin/zipkin-go/reporter/http"
|
||||
"log"
|
||||
"qteam/config"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var (
|
||||
tracer *zipkin.Tracer
|
||||
lock sync.Mutex
|
||||
)
|
||||
|
||||
func Tracer() (*zipkin.Tracer, error) {
|
||||
if tracer == nil {
|
||||
// 有err, 不适合用sync.Once做单例
|
||||
lock.Lock()
|
||||
defer lock.Unlock()
|
||||
if tracer == nil {
|
||||
err := InitTracer(config.GetConf().ServiceName, config.GetConf().SkyWalkingOapServer)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
return tracer, nil
|
||||
}
|
||||
|
||||
func InitTracer(serviceName, skyWalkingOapServer string) error {
|
||||
zipkinReporter := zkHttp.NewReporter(skyWalkingOapServer)
|
||||
|
||||
// create our local service endpoint
|
||||
endpoint, err := zipkin.NewEndpoint(serviceName, "192.168.110.65:8081")
|
||||
if err != nil {
|
||||
log.Fatalf("unable to create local endpoint: %+v\n", err)
|
||||
}
|
||||
sampler := zipkin.NewModuloSampler(1)
|
||||
// Initialize the tracer.
|
||||
nativeTracer, err := zipkin.NewTracer(
|
||||
zipkinReporter,
|
||||
zipkin.WithLocalEndpoint(endpoint),
|
||||
zipkin.WithSampler(sampler),
|
||||
)
|
||||
// initialize our tracer
|
||||
//nativeTracer, err := zipkin.NewTracer(zipkinReporter, zipkin.WithLocalEndpoint(endpoint))
|
||||
if err != nil {
|
||||
log.Fatalf("unable to create tracer: %+v\n", err)
|
||||
}
|
||||
|
||||
// use zipkin-go-opentracing to wrap our tracer
|
||||
tracer = nativeTracer
|
||||
|
||||
return err
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
package basejob
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
|
||||
"github.com/qit-team/work"
|
||||
)
|
||||
|
||||
var (
|
||||
jb *work.Job
|
||||
register func(job *work.Job)
|
||||
mu sync.RWMutex
|
||||
)
|
||||
|
||||
func SetJob(job *work.Job) {
|
||||
if jb == nil {
|
||||
jb = job
|
||||
}
|
||||
}
|
||||
|
||||
func SetJobRegister(r func(*work.Job)) {
|
||||
register = r
|
||||
}
|
||||
|
||||
func GetJob() *work.Job {
|
||||
if jb == nil {
|
||||
if register != nil {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
jb = work.New()
|
||||
register(jb)
|
||||
} else {
|
||||
panic("job register is nil")
|
||||
}
|
||||
}
|
||||
return jb
|
||||
}
|
||||
|
||||
/**
|
||||
* 消息入队 -- 原始message
|
||||
*/
|
||||
func Enqueue(ctx context.Context, topic string, message string, args ...interface{}) (isOk bool, err error) {
|
||||
return GetJob().Enqueue(ctx, topic, message, args...)
|
||||
}
|
||||
|
||||
/**
|
||||
* 消息入队 -- Task数据结构
|
||||
*/
|
||||
func EnqueueWithTask(ctx context.Context, topic string, task work.Task, args ...interface{}) (isOk bool, err error) {
|
||||
return GetJob().EnqueueWithTask(ctx, topic, task, args...)
|
||||
}
|
||||
|
||||
/**
|
||||
* 消息批量入队 -- 原始message
|
||||
*/
|
||||
func BatchEnqueue(ctx context.Context, topic string, messages []string, args ...interface{}) (isOk bool, err error) {
|
||||
return GetJob().BatchEnqueue(ctx, topic, messages, args...)
|
||||
}
|
||||
|
||||
/**
|
||||
* 消息批量入队 -- Task数据结构
|
||||
*/
|
||||
func BatchEnqueueWithTask(ctx context.Context, topic string, tasks []work.Task, args ...interface{}) (isOk bool, err error) {
|
||||
return GetJob().BatchEnqueueWithTask(ctx, topic, tasks, args...)
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
package jobs
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"qteam/app/jobs/basejob"
|
||||
"qteam/config"
|
||||
|
||||
"github.com/qit-team/snow-core/log/logger"
|
||||
"github.com/qit-team/snow-core/queue"
|
||||
"github.com/qit-team/snow-core/redis"
|
||||
"github.com/qit-team/work"
|
||||
)
|
||||
|
||||
/**
|
||||
* 配置队列任务
|
||||
*/
|
||||
func RegisterWorker(job *work.Job) {
|
||||
basejob.SetJob(job)
|
||||
|
||||
//设置worker的任务投递回调函数
|
||||
job.AddFunc("topic-test", test)
|
||||
//设置worker的任务投递回调函数,和并发数
|
||||
job.AddFunc("topic-test1", test, 2)
|
||||
//使用worker结构进行注册
|
||||
job.AddWorker("topic-test2", &work.Worker{Call: work.MyWorkerFunc(test), MaxConcurrency: 1})
|
||||
|
||||
RegisterQueueDriver(job)
|
||||
SetOptions(job)
|
||||
}
|
||||
|
||||
/**
|
||||
* 给topic注册对应的队列服务
|
||||
*/
|
||||
func RegisterQueueDriver(job *work.Job) {
|
||||
//设置队列服务,需要实现work.Queue接口的方法
|
||||
q := queue.GetQueue(redis.SingletonMain, queue.DriverTypeRedis)
|
||||
//针对topic设置相关的queue
|
||||
job.AddQueue(q, "topic-test1", "topic-test2")
|
||||
//设置默认的queue, 没有设置过的topic会使用默认的queue
|
||||
job.AddQueue(q)
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置配置参数
|
||||
*/
|
||||
func SetOptions(job *work.Job) {
|
||||
//设置logger,需要实现work.Logger接口的方法
|
||||
job.SetLogger(logger.GetLogger())
|
||||
|
||||
//设置启用的topic,未设置表示启用全部注册过topic
|
||||
if config.GetOptions().Queue != "" {
|
||||
topics := strings.Split(config.GetOptions().Queue, ",")
|
||||
job.SetEnableTopics(topics...)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
package jobs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/qit-team/work"
|
||||
)
|
||||
|
||||
func test(task work.Task) (work.TaskResult) {
|
||||
time.Sleep(time.Millisecond * 5)
|
||||
s, err := work.JsonEncode(task)
|
||||
if err != nil {
|
||||
//work.StateFailed 不会进行ack确认
|
||||
//work.StateFailedWithAck 会进行actk确认
|
||||
//return work.TaskResult{Id: task.Id, State: work.StateFailed}
|
||||
return work.TaskResult{Id: task.Id, State: work.StateFailedWithAck}
|
||||
} else {
|
||||
//work.StateSucceed 会进行ack确认
|
||||
fmt.Println("do task", s)
|
||||
return work.TaskResult{Id: task.Id, State: work.StateSucceed}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
package mq
|
||||
|
||||
// 单聊
|
||||
func SingleTalk(tag uint64, ch interface{}, msg []byte) {
|
||||
//var data entities.SingTalkReq
|
||||
//err := json.Unmarshal(msg, &data)
|
||||
//utils.Log(nil, "msg", data)
|
||||
//if err == nil {
|
||||
// conn := netool.GetConnManagger().GetConnection(data.Msg.To)
|
||||
// var sendOk = false
|
||||
// if conn != nil && conn.IsActive() {
|
||||
// if utils.WriteMsg(msgid.SINGLE_MSG, data, conn) {
|
||||
// sendOk = true
|
||||
// }
|
||||
// }
|
||||
// if !sendOk {
|
||||
// common.PikaTool.Zadd(utils.GetRealKey(common2.USER_MSG)+data.Msg.To, data, time.Now().Unix())
|
||||
// }
|
||||
//}
|
||||
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
package mq
|
||||
|
||||
import (
|
||||
"qteam/app/utils/mq"
|
||||
)
|
||||
|
||||
func startQunue(name string, method interface{}, mqTp string, tp int, exhange string) {
|
||||
if tp == 1 {
|
||||
go mq.MqManager.GetMqByName(mqTp).Consume(name, method)
|
||||
} else {
|
||||
go mq.MqManager.GetMqByName(mqTp).DelyConsume(name, method)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 队列服务
|
||||
func StartQunueServer() error {
|
||||
StartServer()
|
||||
select {}
|
||||
return nil
|
||||
}
|
||||
|
||||
// 开启队列
|
||||
func StartServer() error {
|
||||
//startWorkers()
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,90 @@
|
|||
package market
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"github.com/pkg/errors"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"qteam/config"
|
||||
)
|
||||
|
||||
type MarketClient struct {
|
||||
cfg config.MarketConfig
|
||||
}
|
||||
|
||||
type MarketSendRequest struct {
|
||||
AppId string `json:"app_id"` //APP ID
|
||||
Sign string `json:"sign"` //签名
|
||||
ReqCode string `json:"req_code"` //固定值:voucher.create
|
||||
MemId string `json:"mem_id"` //商户号
|
||||
ReqSerialNo string `json:"req_serial_no"` //请求唯一流水号 最大32位
|
||||
TimeTamp string `json:"timestamp"` //时间戳 yyyyMMddHHmmss
|
||||
PosId string `json:"pos_id"` //商户方平台号
|
||||
VoucherId string `json:"voucher_id"` //制码批次号
|
||||
VoucherNum int `json:"voucher_num"` //请券数量,默认是 1
|
||||
MobileNo string `json:"mobile_no"` //11 手机号,可传空字符串
|
||||
SendMsg string `json:"send_msg"` //是否发送短信:2- 发送 1-不发送
|
||||
}
|
||||
|
||||
type MarketSenResponse struct {
|
||||
VoucherId string `json:"voucher_id"` //制码批次号
|
||||
VoucherCode string `json:"voucher_code"` //券码
|
||||
ShortUrl string `json:"short_url"` //含二维码、条码的短链接
|
||||
VoucherSdate string `json:"voucher_sdate"` //有效期起
|
||||
VoucherEdate string `json:"voucher_edate"` //有效期止
|
||||
CodeType string `json:"code_type"` //码类型: 00- 代金券 01- 满减券
|
||||
}
|
||||
|
||||
type MarketResponse struct {
|
||||
ErrCode string `json:"errCode"` //00-成功 其他:失败
|
||||
Msg string `json:"msg"` //描 述 (失败时必填)
|
||||
Data MarketSenResponse `json:"data"`
|
||||
}
|
||||
|
||||
func (this *MarketSendRequest) toMap() (resultMap map[string]interface{}) {
|
||||
// Marshal the struct to JSON, ignoring omitempty fields.
|
||||
jsonBytes, err := json.Marshal(this)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
// Unmarshal the JSON into a map to get the final result.
|
||||
err = json.Unmarshal(jsonBytes, &resultMap)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return resultMap
|
||||
}
|
||||
|
||||
func (this *MarketClient) doPost(url string, jsonBytes []byte) (body []byte, err error) {
|
||||
// 创建POST请求
|
||||
url = this.cfg.Host + url
|
||||
req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonBytes))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// 设置Content-Type头
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
|
||||
// 创建HTTP客户端
|
||||
client := &http.Client{}
|
||||
|
||||
// 发送请求并处理响应
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
// 读取响应体
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
err = errors.New("HTTP request failed: " + resp.Status)
|
||||
return
|
||||
}
|
||||
body, err = ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
package market
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"qteam/app/utils/encrypt"
|
||||
"qteam/config"
|
||||
"time"
|
||||
)
|
||||
|
||||
func NewMarketClient(cfg config.MarketConfig) *MarketClient {
|
||||
cfg.Sign = "-----BEGIN RSA PRIVATE KEY-----\n" + cfg.Sign + "\n-----END RSA PRIVATE KEY-----"
|
||||
return &MarketClient{
|
||||
cfg: cfg,
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
MarketSend
|
||||
券码生成接口
|
||||
- 请求地址:/openApi/v1/market/key/send
|
||||
- 说明:发券接口应支持使用同一流水号进行重复请求,即:当调用该接口失败时,可 以使用同一流水号进行再次请求,接口需要根据请求的流水号进行判断,若无该流水 号的券码信息则新生成后返回,若有该流水号的券码信息则直接返回该券码的信息
|
||||
orderNo: 订单号
|
||||
VoucherId: 制码批次号
|
||||
MobileNo: 11 手机号,可传空字符串
|
||||
SendMsg: 是否发送短信:2- 发送 1-不发送
|
||||
*/
|
||||
func (this *MarketClient) MarketSend(orderNo, VoucherId, MobileNo, SendMsg string) (res MarketResponse, err error) {
|
||||
url := "/openApi/v1/market/key/send"
|
||||
request := MarketSendRequest{
|
||||
AppId: this.cfg.AppId,
|
||||
ReqCode: this.cfg.ReqCode,
|
||||
MemId: this.cfg.MemId,
|
||||
PosId: this.cfg.PosId,
|
||||
TimeTamp: time.Now().Format("20060102150405"),
|
||||
VoucherId: VoucherId,
|
||||
ReqSerialNo: orderNo,
|
||||
VoucherNum: 1,
|
||||
MobileNo: MobileNo,
|
||||
SendMsg: SendMsg,
|
||||
}
|
||||
|
||||
request.Sign, err = MakeRsaSign(this.cfg.Sign, request.toMap())
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
|
||||
bytes, err := json.Marshal(request)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
|
||||
data, err := this.doPost(url, bytes)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
err = json.Unmarshal(data, &res)
|
||||
// 加密
|
||||
if len(res.Data.ShortUrl) > 0 {
|
||||
res.Data.ShortUrl = encrypt.AesEncryptCBC([]byte(res.Data.ShortUrl), []byte(this.cfg.SecretKey))
|
||||
}
|
||||
return res, err
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
package market
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/qit-team/snow-core/kernel/server"
|
||||
"os"
|
||||
"qteam/app/utils"
|
||||
"qteam/config"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestMarketSendRequest_Market(t *testing.T) {
|
||||
opts := config.GetOptions()
|
||||
if opts.ShowVersion {
|
||||
fmt.Printf("%s\ncommit %s\nbuilt on %s\n", server.Version, server.BuildCommit, server.BuildDate)
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
//加载配置
|
||||
conf, err := config.Load(opts.ConfFile)
|
||||
if err != nil {
|
||||
utils.Log(nil, "err", err.Error())
|
||||
return
|
||||
}
|
||||
client := NewMarketClient(conf.OpenApiMarketConfig)
|
||||
|
||||
//data, err := client.MarketSend("123456789111", "1717567048171", "", "2")
|
||||
data, err := client.MarketSend("123111", "1717", "", "2")
|
||||
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
t.Log(data)
|
||||
}
|
|
@ -0,0 +1,137 @@
|
|||
package market
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/sha256"
|
||||
"crypto/x509"
|
||||
"encoding/base64"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"fmt"
|
||||
"sort"
|
||||
)
|
||||
|
||||
// getSignString 使用 xx=aa&yy=bb 的字符串拼接
|
||||
func getSignString(data map[string]interface{}) string {
|
||||
keys := make([]string, 0, len(data))
|
||||
for key := range data {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
|
||||
signString := ""
|
||||
separator := ""
|
||||
for _, key := range keys {
|
||||
value := data[key]
|
||||
if key == "sign" || value == nil {
|
||||
continue
|
||||
}
|
||||
signString += fmt.Sprintf("%s%s=%v", separator, key, value)
|
||||
separator = "&"
|
||||
}
|
||||
return signString
|
||||
}
|
||||
|
||||
// VerifyRsaSign 签名验证
|
||||
func VerifyRsaSign(publicKey string, data map[string]interface{}) (map[string]interface{}, error) {
|
||||
// 对 sign nonce timestamp appId 升序排序
|
||||
// 使用 xx=aa&yy=bb 的字符串拼接
|
||||
// 商户的公钥验签 RSA2验签
|
||||
signString := getSignString(data)
|
||||
|
||||
rsaPubKey, err := parseRSAPublicKeyFromPEM([]byte(publicKey))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
signature, err := base64.StdEncoding.DecodeString(data["sign"].(string))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
hashed := sha256.Sum256([]byte(signString))
|
||||
err = rsa.VerifyPKCS1v15(rsaPubKey, crypto.SHA256, hashed[:], signature)
|
||||
if err != nil {
|
||||
return nil, errors.New("签名验证失败")
|
||||
}
|
||||
|
||||
return data, nil
|
||||
}
|
||||
|
||||
// MakeRsaSign 生成签名
|
||||
func MakeRsaSign(privateKey string, data map[string]interface{}) (string, error) {
|
||||
// 对 sign nonce timestamp appId 升序排序
|
||||
// 使用 xx=aa&yy=bb 的字符串拼接
|
||||
// 营销系统生成的私钥生成签名 RSA2加签
|
||||
signString := getSignString(data)
|
||||
|
||||
privKey, err := parseRSAPrivateKeyFromPEM([]byte(privateKey))
|
||||
if err != nil {
|
||||
return "", errors.New("私钥解析失败")
|
||||
}
|
||||
|
||||
hashed := sha256.Sum256([]byte(signString))
|
||||
signature, err := rsa.SignPKCS1v15(rand.Reader, privKey, crypto.SHA256, hashed[:])
|
||||
if err != nil {
|
||||
return "", errors.New("签名失败")
|
||||
}
|
||||
|
||||
return base64.StdEncoding.EncodeToString(signature), nil
|
||||
}
|
||||
|
||||
// ParseRSAPrivateKeyFromPEM 解析私钥
|
||||
func parseRSAPrivateKeyFromPEM(key []byte) (*rsa.PrivateKey, error) {
|
||||
var err error
|
||||
|
||||
// Parse PEM block
|
||||
var block *pem.Block
|
||||
if block, _ = pem.Decode(key); block == nil {
|
||||
return nil, errors.New("私钥解析失败: 无效的PEM格式")
|
||||
}
|
||||
|
||||
var parsedKey interface{}
|
||||
if parsedKey, err = x509.ParsePKCS1PrivateKey(block.Bytes); err != nil {
|
||||
if parsedKey, err = x509.ParsePKCS8PrivateKey(block.Bytes); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
var pkey *rsa.PrivateKey
|
||||
var ok bool
|
||||
if pkey, ok = parsedKey.(*rsa.PrivateKey); !ok {
|
||||
return nil, errors.New("密钥不是有效的RSA私钥")
|
||||
}
|
||||
|
||||
return pkey, nil
|
||||
}
|
||||
|
||||
// parseRSAPublicKeyFromPEM parses a PEM encoded PKCS1 or PKCS8 public key
|
||||
func parseRSAPublicKeyFromPEM(key []byte) (*rsa.PublicKey, error) {
|
||||
var err error
|
||||
|
||||
// Parse PEM block
|
||||
var block *pem.Block
|
||||
if block, _ = pem.Decode(key); block == nil {
|
||||
return nil, errors.New("公钥解析失败: 无效的PEM格式")
|
||||
}
|
||||
|
||||
// Parse the key
|
||||
var parsedKey interface{}
|
||||
if parsedKey, err = x509.ParsePKIXPublicKey(block.Bytes); err != nil {
|
||||
if cert, err := x509.ParseCertificate(block.Bytes); err == nil {
|
||||
parsedKey = cert.PublicKey
|
||||
} else {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
var pkey *rsa.PublicKey
|
||||
var ok bool
|
||||
if pkey, ok = parsedKey.(*rsa.PublicKey); !ok {
|
||||
return nil, errors.New("密钥不是有效的RSA公钥")
|
||||
}
|
||||
|
||||
return pkey, nil
|
||||
}
|
|
@ -0,0 +1,164 @@
|
|||
package openapiService
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/aes"
|
||||
"encoding/base64"
|
||||
"gitee.com/chengdu_blue_brothers/openapi-go-sdk/api"
|
||||
"gitee.com/chengdu_blue_brothers/openapi-go-sdk/notify"
|
||||
"net/http"
|
||||
"qteam/app/models/orderdetailsmodel"
|
||||
"qteam/app/models/ordersmodel"
|
||||
"qteam/app/models/usercouponmodel"
|
||||
"qteam/app/utils"
|
||||
"qteam/config"
|
||||
"time"
|
||||
)
|
||||
|
||||
func CreatClient() (client *api.Client, err error) {
|
||||
merchantId := config.GetConf().OpenApi.MerchantId
|
||||
secretKey := config.GetConf().OpenApi.SecretKey
|
||||
isProd := config.GetConf().OpenApi.IsProd
|
||||
timeout := 10 * time.Second // 请求超时时间
|
||||
|
||||
client, err = api.NewClient(merchantId, secretKey, isProd, timeout)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return client, nil
|
||||
}
|
||||
|
||||
type RechargeOrderReq struct {
|
||||
OutTradeNo string
|
||||
ProductId int
|
||||
RechargeAccount string
|
||||
AccountType int
|
||||
Number int
|
||||
}
|
||||
|
||||
func RechargeOrder(rechargeOrderReq RechargeOrderReq) (result *api.RechargeOrderResp, err error) {
|
||||
req := &api.RechargeOrderReq{
|
||||
OutTradeNo: rechargeOrderReq.OutTradeNo,
|
||||
ProductId: rechargeOrderReq.ProductId,
|
||||
RechargeAccount: rechargeOrderReq.RechargeAccount,
|
||||
AccountType: 0,
|
||||
Number: 1,
|
||||
NotifyUrl: config.GetConf().OpenApi.NotifyUrl,
|
||||
}
|
||||
client, err := CreatClient()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result, err = client.RechargeOrder(context.Background(), req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if result.Code != api.CodeCreateOrderSuccess {
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func ReCardOrder(CardOrderReq api.CardOrderReq) (result *api.CardOrderResp, err error) {
|
||||
req := &api.CardOrderReq{
|
||||
OutTradeNo: CardOrderReq.OutTradeNo,
|
||||
ProductId: CardOrderReq.ProductId,
|
||||
AccountType: 0,
|
||||
Number: 1,
|
||||
NotifyUrl: config.GetConf().OpenApi.NotifyUrl,
|
||||
}
|
||||
client, err := CreatClient()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result, err = client.CardOrder(context.Background(), req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if result.Code != api.CodeCreateOrderSuccess {
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func DecryptCard(encCode string) (decode string, err error) {
|
||||
defer func() error {
|
||||
if r := recover(); r != nil {
|
||||
utils.Log(nil, "解密错误", err)
|
||||
|
||||
}
|
||||
return err
|
||||
}()
|
||||
client, err := CreatClient()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
decryptedCode, err := decryptAES(encCode, client.GetSecretKey())
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return decryptedCode, nil
|
||||
}
|
||||
|
||||
// AES 解密
|
||||
func decryptAES(encryptedData string, secretKey string) (string, error) {
|
||||
// 第一步:对加密的卡密做base64 decode
|
||||
encryptedBytes, err := base64.StdEncoding.DecodeString(encryptedData)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// 第二步:使用aes-256-ecb解密
|
||||
cipher, _ := aes.NewCipher([]byte(secretKey))
|
||||
decrypted := make([]byte, len(encryptedBytes))
|
||||
size := 16
|
||||
for bs, be := 0, size; bs < len(encryptedBytes); bs, be = bs+size, be+size {
|
||||
cipher.Decrypt(decrypted[bs:be], encryptedBytes[bs:be])
|
||||
}
|
||||
paddingSize := int(decrypted[len(decrypted)-1])
|
||||
return string(decrypted[0 : len(decrypted)-paddingSize]), nil
|
||||
}
|
||||
|
||||
func ParseAndVerify(request *http.Request) (req *notify.OrderReq, err error) {
|
||||
// 第一步:初使化client实例
|
||||
merchantId := config.GetConf().OpenApi.MerchantId
|
||||
secretKey := config.GetConf().OpenApi.SecretKey
|
||||
client := notify.NewNotify(merchantId, secretKey)
|
||||
|
||||
// 第二步:验签并解析结果
|
||||
req, err = client.ParseAndVerify(request)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// NotifyOperation /**
|
||||
func NotifyOperation(order ordersmodel.Orders, req *notify.OrderReq) (err error) {
|
||||
var session = ordersmodel.GetInstance().GetDb().NewSession()
|
||||
session.Begin()
|
||||
updateOrder := ordersmodel.Orders{Status: 3}
|
||||
_, err = session.Where("Id = ?", order.Id).Update(&updateOrder)
|
||||
if err != nil {
|
||||
session.Rollback()
|
||||
return
|
||||
}
|
||||
//卡密
|
||||
if req.CardCode != "" {
|
||||
//订单详情
|
||||
updateOrderDetail := orderdetailsmodel.OrderDetails{Url: req.CardCode.Value()}
|
||||
_, err = session.Where("OrderId = ?", order.Id).Update(&updateOrderDetail)
|
||||
if err != nil {
|
||||
session.Rollback()
|
||||
return
|
||||
}
|
||||
userCouponDetail := usercouponmodel.UserCoupon{OrderInfo: req.CardCode.Value()}
|
||||
_, err = session.Where("OrderId = ?", order.Id).Update(&userCouponDetail)
|
||||
if err != nil {
|
||||
session.Rollback()
|
||||
return
|
||||
}
|
||||
}
|
||||
session.Commit()
|
||||
return
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
package utils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/forgoer/openssl"
|
||||
)
|
||||
|
||||
func Des3Encrypt(src []byte, key string) ([]byte, error) {
|
||||
dst, err := openssl.Des3ECBEncrypt(src, []byte(key), openssl.PKCS7_PADDING)
|
||||
return dst, err
|
||||
}
|
||||
|
||||
func Des3ECBDecrypt(data []byte, key string) ([]byte, error) {
|
||||
dst, err := openssl.Des3ECBDecrypt(data, []byte(key), openssl.PKCS7_PADDING)
|
||||
fmt.Println(string(dst))
|
||||
return dst, err
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
package encrypt
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const lettersString = "0123456789abcdefghijkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ"
|
||||
|
||||
// 字符串长度
|
||||
const number = 16
|
||||
|
||||
/*
|
||||
16位码,前15位随机字符串,最后一位通过前15位字符串计算校验生成
|
||||
*/
|
||||
|
||||
func LotteryEncryptEncode() string {
|
||||
b := make([]byte, number)
|
||||
var sum byte
|
||||
for i := 0; i < number-1; i++ {
|
||||
b[i] = lettersString[rand.Int63()%int64(len(lettersString))]
|
||||
sum += b[i]
|
||||
}
|
||||
b[number-1] = lettersString[sum%byte(len(lettersString))]
|
||||
return *(*string)(unsafe.Pointer(&b))
|
||||
}
|
||||
|
||||
func LotteryEncryptDecode(str string) bool {
|
||||
var sum byte
|
||||
for i := 0; i < len(str)-1; i++ {
|
||||
sum += str[i]
|
||||
}
|
||||
if lettersString[sum%byte(len(lettersString))] != str[len(str)-1] {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
package encrypt
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestLotteryEncryptEncode(t *testing.T) {
|
||||
code := LotteryEncryptEncode()
|
||||
t.Log(code)
|
||||
}
|
||||
|
||||
func TestLotteryEncryptDecode(t *testing.T) {
|
||||
code := LotteryEncryptEncode()
|
||||
t.Log(code)
|
||||
result := LotteryEncryptDecode(code)
|
||||
t.Log(result)
|
||||
}
|
|
@ -0,0 +1,104 @@
|
|||
package httpclient
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"qteam/app/utils"
|
||||
|
||||
"github.com/valyala/fasthttp"
|
||||
"time"
|
||||
)
|
||||
|
||||
func FastHttpPost(url string, header map[string]string, body []byte, timeout int) ([]byte, error) {
|
||||
req := fasthttp.AcquireRequest()
|
||||
defer fasthttp.ReleaseRequest(req) // 用完需要释放资源
|
||||
// 默认是application/x-www-form-urlencoded
|
||||
req.Header.SetMethod("POST")
|
||||
for k, v := range header {
|
||||
req.Header.Set(k, v)
|
||||
}
|
||||
req.SetRequestURI(url)
|
||||
req.SetBody(body)
|
||||
resp := fasthttp.AcquireResponse()
|
||||
defer fasthttp.ReleaseResponse(resp) // 用完需要释放资源
|
||||
var err error
|
||||
if timeout > 0 {
|
||||
if err = fasthttp.Do(req, resp); err != nil {
|
||||
utils.Log(nil, "http请求失败", err, url)
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
if err := fasthttp.DoTimeout(req, resp, time.Duration(timeout)*time.Second); err != nil {
|
||||
utils.Log(nil, "http请求失败", err, url)
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
b := resp.Body()
|
||||
//fmt.Println(string(b),"http请求")
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func FastHttpPostForm(url string, header map[string]string, body map[string]string, timeout int) ([]byte, error) {
|
||||
req := fasthttp.AcquireRequest()
|
||||
defer fasthttp.ReleaseRequest(req) // 用完需要释放资源
|
||||
// 默认是application/x-www-form-urlencoded
|
||||
req.Header.SetMethod("POST")
|
||||
for k, v := range header {
|
||||
req.Header.Set(k, v)
|
||||
}
|
||||
req.SetRequestURI(url)
|
||||
args := &fasthttp.Args{}
|
||||
for k, v := range body {
|
||||
args.Add(k, v)
|
||||
}
|
||||
req.SetBody(args.QueryString())
|
||||
resp := fasthttp.AcquireResponse()
|
||||
defer fasthttp.ReleaseResponse(resp) // 用完需要释放资源
|
||||
var err error
|
||||
if timeout == 0 {
|
||||
if err = fasthttp.Do(req, resp); err != nil {
|
||||
utils.Log(nil, "http请求失败", err, url)
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
if err := fasthttp.DoTimeout(req, resp, time.Duration(timeout)*time.Second); err != nil {
|
||||
utils.Log(nil, "http请求失败", err, url)
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
b := resp.Body()
|
||||
return b, nil
|
||||
}
|
||||
func FastHttpGet(url string, header map[string]string, body map[string]string, timeout int) ([]byte, error) {
|
||||
req := fasthttp.AcquireRequest()
|
||||
defer fasthttp.ReleaseRequest(req) // 用完需要释放资源
|
||||
// 默认是application/x-www-form-urlencoded
|
||||
req.Header.SetMethod("GET")
|
||||
for k, v := range header {
|
||||
req.Header.Set(k, v)
|
||||
}
|
||||
if len(body) > 0 {
|
||||
url += "?"
|
||||
for k, v := range body {
|
||||
url += k + "=" + v + "&"
|
||||
}
|
||||
url = url[0 : len(url)-1]
|
||||
}
|
||||
fmt.Println(url)
|
||||
req.SetRequestURI(url)
|
||||
resp := fasthttp.AcquireResponse()
|
||||
defer fasthttp.ReleaseResponse(resp) // 用完需要释放资源
|
||||
var err error
|
||||
if timeout == 0 {
|
||||
if err = fasthttp.Do(req, resp); err != nil {
|
||||
utils.Log(nil, "http请求失败", err, url)
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
if err := fasthttp.DoTimeout(req, resp, time.Duration(timeout)*time.Second); err != nil {
|
||||
utils.Log(nil, "http请求失败", err, url)
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
b := resp.Body()
|
||||
return b, nil
|
||||
}
|
|
@ -0,0 +1,177 @@
|
|||
package metric
|
||||
|
||||
// prometheus metric:unique identifier: name and optional key-value pairs called labels
|
||||
// 1. name regexp: [a-zA-Z_:][a-zA-Z0-9_:]*
|
||||
// 2. label name regexp: [a-zA-Z_][a-zA-Z0-9_]*
|
||||
// 3. Label names beginning with __ are reserved for internal use.
|
||||
// 4. Label values may contain any Unicode characters.
|
||||
// 5. notation: <metric name>{<label name>=<label value>, ...}
|
||||
// for example: api_http_requests_total{method="POST", handler="/messages"}
|
||||
// A label with an empty label value is considered equivalent to a label that does not exist.
|
||||
|
||||
// each sample consists of :
|
||||
// - a float64 value
|
||||
// - a millisecond-precision timestamp
|
||||
|
||||
// metric type:
|
||||
// - Counter
|
||||
// A cumulative metric that represents a single monotonically increasing counter whose value can only increase or be reset to zero on restart.
|
||||
// - Gauge
|
||||
// A gauge is a metric that represents a single numerical value that can arbitrarily go up and down.
|
||||
// - Histogram
|
||||
// A histogram samples observations (usually things like request durations or response sizes) and counts them in configurable buckets. It also provides a sum of all observed values.
|
||||
// - Summary
|
||||
// Similar to a histogram, a summary samples observations (usually things like request durations and response sizes). While it also provides a total count of observations and a sum of all observed values, it calculates configurable quantiles over a sliding time window.
|
||||
// metric:
|
||||
// Counter:
|
||||
// - req_total_count
|
||||
// - req_failed_count
|
||||
// Gauge:
|
||||
// - heap_inuse_size
|
||||
// - heap_total_size
|
||||
// - heap_object_num
|
||||
// - goroutine_num
|
||||
// Histogram:
|
||||
// - req_cost_time
|
||||
// Summary:
|
||||
// - req_cost_time
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"sync"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||
)
|
||||
|
||||
const (
|
||||
//ENV = "env"
|
||||
APP = "snow"
|
||||
VER = "ver"
|
||||
)
|
||||
|
||||
var (
|
||||
collectors = []prometheus.Collector{}
|
||||
)
|
||||
|
||||
func RegisterCollector(c ...prometheus.Collector) {
|
||||
collectors = append(collectors, c...)
|
||||
}
|
||||
|
||||
type Options struct {
|
||||
labels map[string]string
|
||||
processEnable bool
|
||||
runtimeEnable bool
|
||||
}
|
||||
|
||||
type Option func(opt *Options)
|
||||
|
||||
// 添加App和Ver label
|
||||
func AppVer(app, ver string) Option {
|
||||
return func(opt *Options) {
|
||||
if app != "" {
|
||||
opt.labels[APP] = app
|
||||
}
|
||||
if ver != "" {
|
||||
opt.labels[VER] = ver
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 添加额外label
|
||||
func WithLabel(key, val string) Option {
|
||||
return func(opt *Options) {
|
||||
if key != "" && val != "" {
|
||||
opt.labels[key] = val
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 收集进程信息
|
||||
func EnableProcess() Option {
|
||||
return func(opt *Options) {
|
||||
opt.processEnable = true
|
||||
}
|
||||
}
|
||||
|
||||
func EnableRuntime() Option {
|
||||
return func(opt *Options) {
|
||||
opt.runtimeEnable = true
|
||||
}
|
||||
}
|
||||
|
||||
type Reporter struct {
|
||||
opts Options
|
||||
collectors []prometheus.Collector
|
||||
// registerer
|
||||
registerer prometheus.Registerer
|
||||
gatherer prometheus.Gatherer
|
||||
}
|
||||
|
||||
var (
|
||||
once sync.Once
|
||||
reporter Reporter
|
||||
)
|
||||
|
||||
func Init(opts ...Option) {
|
||||
_opts := Options{
|
||||
labels: map[string]string{},
|
||||
}
|
||||
for _, opt := range opts {
|
||||
opt(&_opts)
|
||||
}
|
||||
|
||||
once.Do(func() {
|
||||
cs := collectors
|
||||
if _opts.processEnable {
|
||||
cs = append(cs, prometheus.NewProcessCollector(prometheus.ProcessCollectorOpts{}))
|
||||
}
|
||||
|
||||
if _opts.runtimeEnable {
|
||||
cs = append(cs, prometheus.NewGoCollector())
|
||||
}
|
||||
|
||||
reporter = Reporter{
|
||||
opts: _opts,
|
||||
collectors: cs,
|
||||
}
|
||||
|
||||
registry := prometheus.NewRegistry()
|
||||
|
||||
reporter.registerer = prometheus.WrapRegistererWith(reporter.opts.labels, registry)
|
||||
reporter.gatherer = registry
|
||||
|
||||
reporter.registerer.MustRegister(reporter.collectors...)
|
||||
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func (p *Reporter) newCounterVec(metric string, labels []string) *prometheus.CounterVec {
|
||||
counterVec := prometheus.NewCounterVec(prometheus.CounterOpts{
|
||||
Name: metric,
|
||||
}, labels)
|
||||
|
||||
return counterVec
|
||||
}
|
||||
|
||||
func (p *Reporter) newGaugeVec(metric string, labels []string) *prometheus.GaugeVec {
|
||||
gaugeVec := prometheus.NewGaugeVec(prometheus.GaugeOpts{
|
||||
Name: metric,
|
||||
}, labels)
|
||||
return gaugeVec
|
||||
}
|
||||
|
||||
func (p *Reporter) newHistogramVec(metric string, labels []string, buckets []float64) *prometheus.HistogramVec {
|
||||
histogramVec := prometheus.NewHistogramVec(prometheus.HistogramOpts{
|
||||
Name: metric,
|
||||
Buckets: buckets,
|
||||
}, labels)
|
||||
return histogramVec
|
||||
}
|
||||
|
||||
func Handler() http.Handler {
|
||||
return promhttp.InstrumentMetricHandler(
|
||||
reporter.registerer, promhttp.HandlerFor(reporter.gatherer, promhttp.HandlerOpts{}),
|
||||
)
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
package mq
|
||||
|
||||
import (
|
||||
common3 "qteam/app/constants/common"
|
||||
mq "qteam/app/utils/mq/mqs"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var (
|
||||
MqManager = CMqManager{}
|
||||
once sync.Once
|
||||
)
|
||||
|
||||
type Imq interface {
|
||||
Produce(name string, log interface{}, delayTime int, args ...interface{}) error
|
||||
Consume(name string, hand interface{})
|
||||
DelyConsume(name string, hand interface{})
|
||||
}
|
||||
|
||||
type CMqManager struct {
|
||||
mqs map[string]Imq
|
||||
}
|
||||
|
||||
func (this *CMqManager) InitMq() {
|
||||
this.mqs = make(map[string]Imq)
|
||||
//this.mqs[common.MQ_RABBIT] = RabbitMq{}
|
||||
//this.mqs[common.MQ_NSQ] = NsqMq{}
|
||||
this.mqs[common3.MQ_NATS] = mq.NatsMq{}
|
||||
this.mqs[common3.MQ_KFK] = mq.KafkaMq{}
|
||||
}
|
||||
func (this *CMqManager) GetMqByName(name string) Imq {
|
||||
once.Do(func() {
|
||||
this.InitMq()
|
||||
})
|
||||
return this.mqs[name]
|
||||
}
|
|
@ -0,0 +1,89 @@
|
|||
package mq
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/Shopify/sarama"
|
||||
"github.com/qit-team/snow-core/redis"
|
||||
"qteam/app/utils"
|
||||
"qteam/config"
|
||||
|
||||
"strconv"
|
||||
"sync"
|
||||
)
|
||||
|
||||
type KafkaMq struct {
|
||||
}
|
||||
|
||||
// 同步
|
||||
func (n KafkaMq) Produce(name string, log interface{}, delayTime int, args ...interface{}) error {
|
||||
kafConfig := sarama.NewConfig()
|
||||
kafConfig.Producer.RequiredAcks = sarama.WaitForAll // 发送完数据需要leader和follow都确认
|
||||
kafConfig.Producer.Partitioner = sarama.NewRandomPartitioner // 新选出一个partition
|
||||
kafConfig.Producer.Return.Successes = true // 成功交付的消息将在success channel返回
|
||||
|
||||
// 构造一个消息
|
||||
msg := &sarama.ProducerMessage{}
|
||||
msg.Topic = name
|
||||
var data, _ = json.Marshal(log)
|
||||
msg.Value = sarama.StringEncoder(string(data))
|
||||
// 连接kafka
|
||||
client, err := sarama.NewSyncProducer(config.GetConf().KafkaUrl, kafConfig)
|
||||
if err != nil {
|
||||
fmt.Println("producer closed, err:", err)
|
||||
return nil
|
||||
}
|
||||
defer client.Close()
|
||||
// 发送消息
|
||||
pid, offset, err := client.SendMessage(msg)
|
||||
if err != nil {
|
||||
utils.Log(nil, "send msg failed, err:", err, pid, offset)
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n KafkaMq) Consume(name string, hand interface{}) {
|
||||
consumer, err := sarama.NewConsumer(config.GetConf().KafkaUrl, nil)
|
||||
if err != nil {
|
||||
utils.Log(nil, "kafka comsume", err.Error())
|
||||
return
|
||||
}
|
||||
partitionList, err := consumer.Partitions(name) // 根据topic取到所有的分区
|
||||
if err != nil {
|
||||
utils.Log(nil, "kafka comsume", err.Error())
|
||||
return
|
||||
}
|
||||
//utils.Log(nil,"kafka comsume",name,partitionList)
|
||||
for partition := range partitionList { // 遍历所有的分区
|
||||
// 针对每个分区创建一个对应的分区消费者
|
||||
var offsetReDis, _ = redis.GetRedis().Incr(context.Background(), "kafka_consume:"+strconv.Itoa(int(partition))).Result() //保证多消费者不重复消费
|
||||
var offset int64 = sarama.OffsetNewest
|
||||
if offsetReDis > 0 {
|
||||
//offset = int64(offsetReDis)
|
||||
}
|
||||
pc, err := consumer.ConsumePartition(name, int32(partition), offset)
|
||||
//utils.Log(nil,"partion",int32(partition))
|
||||
if err != nil {
|
||||
fmt.Printf("failed to start consumer for partition %d,err:%v\n", partition, err)
|
||||
return
|
||||
}
|
||||
defer pc.AsyncClose()
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(1)
|
||||
// 异步从每个分区消费信息
|
||||
go func(sarama.PartitionConsumer) {
|
||||
for msg := range pc.Messages() {
|
||||
defer wg.Done()
|
||||
var handler = hand.(func(tag uint64, ch interface{}, msg []byte))
|
||||
handler(0, nil, msg.Value)
|
||||
//utils.Log(nil,"hand msg",string(msg.Value),msg.Offset)
|
||||
}
|
||||
}(pc)
|
||||
wg.Wait()
|
||||
}
|
||||
}
|
||||
func (n KafkaMq) DelyConsume(name string, hand interface{}) {
|
||||
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
package mq
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/nats-io/nats.go"
|
||||
_ "github.com/nats-io/nats.go"
|
||||
"github.com/streadway/amqp"
|
||||
"qteam/app/utils"
|
||||
"qteam/config"
|
||||
)
|
||||
|
||||
type NatsMq struct {
|
||||
Address string
|
||||
nc *nats.Conn
|
||||
}
|
||||
|
||||
func (n NatsMq) Produce(name string, log interface{}, delayTime int, args ...interface{}) error {
|
||||
name = config.GetConf().ServiceName + "_" + name
|
||||
fmt.Println("nats produce", name)
|
||||
nc, _ := nats.Connect(n.Address)
|
||||
defer nc.Close()
|
||||
var content, err = json.Marshal(log)
|
||||
nc.Publish(name, content)
|
||||
return err
|
||||
}
|
||||
|
||||
func (n NatsMq) Consume(name string, hand interface{}) {
|
||||
if n.nc == nil || n.nc.IsClosed() == true {
|
||||
if n.nc != nil {
|
||||
n.nc.Close()
|
||||
}
|
||||
n.nc, _ = nats.Connect(n.Address)
|
||||
}
|
||||
nc := n.nc
|
||||
fmt.Println("nats comsume", name)
|
||||
//defer nc.Close()
|
||||
_, err := nc.Subscribe(name, func(m *nats.Msg) {
|
||||
utils.Log(nil, "Received a message: %s", string(m.Data))
|
||||
var handler = hand.(func(tag uint64, ch *amqp.Channel, msg []byte))
|
||||
handler(0, nil, m.Data)
|
||||
})
|
||||
fmt.Println("ttt", err)
|
||||
}
|
||||
|
||||
func (n NatsMq) DelyConsume(name string, hand interface{}) {
|
||||
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
package utils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/nacos-group/nacos-sdk-go/v2/clients"
|
||||
"github.com/nacos-group/nacos-sdk-go/v2/clients/naming_client"
|
||||
"github.com/nacos-group/nacos-sdk-go/v2/common/constant"
|
||||
"github.com/nacos-group/nacos-sdk-go/v2/vo"
|
||||
"qteam/config"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var (
|
||||
client naming_client.INamingClient
|
||||
once sync.Once
|
||||
)
|
||||
|
||||
func GetNaocosClient() naming_client.INamingClient {
|
||||
once.Do(func() {
|
||||
//注册nacos
|
||||
fmt.Println(config.GetConf().Nacos.Port)
|
||||
sc := []constant.ServerConfig{
|
||||
*constant.NewServerConfig(config.GetConf().Nacos.Url, uint64(config.GetConf().Nacos.Port), constant.WithContextPath("/nacos")),
|
||||
}
|
||||
|
||||
//create ClientConfig
|
||||
cc := *constant.NewClientConfig(
|
||||
constant.WithNamespaceId(""),
|
||||
constant.WithTimeoutMs(5000),
|
||||
constant.WithNotLoadCacheAtStart(true),
|
||||
constant.WithLogDir("/tmp/nacos/log"),
|
||||
constant.WithCacheDir("/tmp/nacos/cache"),
|
||||
constant.WithLogLevel("debug"),
|
||||
)
|
||||
var err error
|
||||
// create naming client
|
||||
client, err = clients.NewNamingClient(
|
||||
vo.NacosClientParam{
|
||||
ClientConfig: &cc,
|
||||
ServerConfigs: sc,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
})
|
||||
return client
|
||||
}
|
|
@ -0,0 +1,160 @@
|
|||
package sm2
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"github.com/tjfoc/gmsm/sm2"
|
||||
"math/big"
|
||||
"qteam/config"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// 生成公钥、私钥
|
||||
func GenerateSM2Key() (PublicKey string, PrivateKey string, err error) {
|
||||
// 生成私钥、公钥
|
||||
privKey, err := sm2.GenerateKey(rand.Reader)
|
||||
if err != nil {
|
||||
fmt.Println("生成密钥对失败:", err)
|
||||
return "", "", err
|
||||
}
|
||||
return PublicKeyToString(&privKey.PublicKey), PrivateKeyToString(privKey), nil
|
||||
}
|
||||
|
||||
// PublicKeyToString 公钥sm2.PublicKey转字符串(与java中org.bouncycastle.crypto生成的公私钥完全互通使用)
|
||||
func PublicKeyToString(publicKey *sm2.PublicKey) string {
|
||||
xBytes := publicKey.X.Bytes()
|
||||
yBytes := publicKey.Y.Bytes()
|
||||
|
||||
// 确保坐标字节切片长度相同
|
||||
byteLen := len(xBytes)
|
||||
if len(yBytes) > byteLen {
|
||||
byteLen = len(yBytes)
|
||||
}
|
||||
|
||||
// 为坐标补齐前导零
|
||||
xBytes = append(make([]byte, byteLen-len(xBytes)), xBytes...)
|
||||
yBytes = append(make([]byte, byteLen-len(yBytes)), yBytes...)
|
||||
|
||||
// 添加 "04" 前缀
|
||||
publicKeyBytes := append([]byte{0x04}, append(xBytes, yBytes...)...)
|
||||
|
||||
return strings.ToUpper(hex.EncodeToString(publicKeyBytes))
|
||||
}
|
||||
|
||||
// PrivateKeyToString 私钥sm2.PrivateKey 转字符串(与java中org.bouncycastle.crypto生成的公私钥完全互通使用)
|
||||
func PrivateKeyToString(privateKey *sm2.PrivateKey) string {
|
||||
return strings.ToUpper(hex.EncodeToString(privateKey.D.Bytes()))
|
||||
}
|
||||
|
||||
func SM2Decrypt(cipherText string) (string, error) {
|
||||
if cipherText == "" {
|
||||
return "", nil
|
||||
}
|
||||
decodedBytes, err := base64.StdEncoding.DecodeString(cipherText)
|
||||
if err != nil {
|
||||
fmt.Println("解码错误:", err)
|
||||
return "", nil
|
||||
}
|
||||
decrypt, err := decryptLoc(config.GetConf().Sm2.PublicKey, config.GetConf().Sm2.PrivateKey, string(decodedBytes))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return decrypt, nil
|
||||
}
|
||||
|
||||
func SM2Encrypt(cipherText string) (string, error) {
|
||||
if cipherText == "" {
|
||||
return "", nil
|
||||
}
|
||||
decrypt, err := encryptLoc(config.GetConf().Sm2.PublicKey, cipherText)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return decrypt, nil
|
||||
}
|
||||
|
||||
func encryptLoc(publicKeyStr, data string) (string, error) {
|
||||
publicKeyObj, err := StringToPublicKey(publicKeyStr)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
decrypt, err := sm2.Encrypt(publicKeyObj, []byte(data), rand.Reader, sm2.C1C2C3)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
resultStr := hex.EncodeToString(decrypt)
|
||||
return base64.StdEncoding.EncodeToString([]byte(resultStr)), nil
|
||||
}
|
||||
|
||||
func decryptLoc(publicKeyStr, privateKeyStr, cipherText string) (string, error) {
|
||||
publicKeyObj, err := StringToPublicKey(publicKeyStr)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
privateKeyObj, err := StringToPrivateKey(privateKeyStr, publicKeyObj)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
decodeString, err := hex.DecodeString(cipherText)
|
||||
decrypt, err := sm2.Decrypt(privateKeyObj, decodeString, sm2.C1C2C3)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
resultStr := string(decrypt)
|
||||
fmt.Println("解密后的字符串:", resultStr)
|
||||
return resultStr, nil
|
||||
}
|
||||
|
||||
// StringToPrivateKey 私钥还原为 sm2.PrivateKey对象(与java中org.bouncycastle.crypto生成的公私钥完全互通使用)
|
||||
func StringToPrivateKey(privateKeyStr string, publicKey *sm2.PublicKey) (*sm2.PrivateKey, error) {
|
||||
privateKeyBytes, err := hex.DecodeString(privateKeyStr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 将字节切片转换为大整数
|
||||
d := new(big.Int).SetBytes(privateKeyBytes)
|
||||
|
||||
// 创建 sm2.PrivateKey 对象
|
||||
privateKey := &sm2.PrivateKey{
|
||||
PublicKey: *publicKey,
|
||||
D: d,
|
||||
}
|
||||
|
||||
return privateKey, nil
|
||||
}
|
||||
|
||||
// StringToPublicKey 公钥字符串还原为 sm2.PublicKey 对象(与java中org.bouncycastle.crypto生成的公私钥完全互通使用)
|
||||
func StringToPublicKey(publicKeyStr string) (*sm2.PublicKey, error) {
|
||||
publicKeyBytes, err := hex.DecodeString(publicKeyStr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 提取 x 和 y 坐标字节切片
|
||||
curve := sm2.P256Sm2().Params()
|
||||
byteLen := (curve.BitSize + 7) / 8
|
||||
xBytes := publicKeyBytes[1 : byteLen+1]
|
||||
yBytes := publicKeyBytes[byteLen+1 : 2*byteLen+1]
|
||||
|
||||
// 将字节切片转换为大整数
|
||||
x := new(big.Int).SetBytes(xBytes)
|
||||
y := new(big.Int).SetBytes(yBytes)
|
||||
|
||||
// 创建 sm2.PublicKey 对象
|
||||
publicKey := &sm2.PublicKey{
|
||||
Curve: curve,
|
||||
X: x,
|
||||
Y: y,
|
||||
}
|
||||
|
||||
return publicKey, nil
|
||||
}
|
||||
|
||||
// 验证签名
|
||||
func VerSm2Sig(pub *sm2.PublicKey, msg []byte, sign []byte) bool {
|
||||
isok := pub.Verify(msg, sign)
|
||||
return isok
|
||||
}
|
|
@ -0,0 +1,412 @@
|
|||
package utils
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/md5"
|
||||
"crypto/rand"
|
||||
"crypto/sha256"
|
||||
"crypto/sha512"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"github.com/aliyun/aliyun-oss-go-sdk/oss"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/golang-jwt/jwt/v4"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/qit-team/snow-core/redis"
|
||||
"github.com/tjfoc/gmsm/sm2"
|
||||
"github.com/tjfoc/gmsm/x509"
|
||||
mrand "math/rand"
|
||||
"net"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"qteam/app/constants/common"
|
||||
"qteam/config"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
const (
|
||||
CODE62 = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
codeLen = 20
|
||||
)
|
||||
|
||||
func GetHostIp() string {
|
||||
conn, err := net.Dial("udp", "8.8.8.8:53")
|
||||
if err != nil {
|
||||
fmt.Println("get current host ip err: ", err)
|
||||
return ""
|
||||
}
|
||||
addr := conn.LocalAddr().(*net.UDPAddr)
|
||||
ip := strings.Split(addr.String(), ":")[0]
|
||||
return ip
|
||||
}
|
||||
func Log(c *gin.Context, name string, msg ...interface{}) {
|
||||
_, file, line, _ := runtime.Caller(1)
|
||||
timeLayout := "2006-01-01 03:04:05" //转化所需模板
|
||||
var datetime = time.Unix(time.Now().Unix(), 0).Format(timeLayout)
|
||||
fmt.Println(name, msg, file, line, datetime)
|
||||
}
|
||||
|
||||
func GetRealKey(key string) string {
|
||||
return config.GetConf().ServiceName + ":" + key
|
||||
}
|
||||
|
||||
// MD5加密
|
||||
func SToMd5(data string) string {
|
||||
r := md5.Sum([]byte(data))
|
||||
return hex.EncodeToString(r[:])
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 编码 整数 为 base62 字符串
|
||||
*/
|
||||
func Encode(number int64) string {
|
||||
if number == 0 {
|
||||
return "0"
|
||||
}
|
||||
result := make([]byte, 0)
|
||||
for number > 0 {
|
||||
round := number / codeLen
|
||||
remain := number % codeLen
|
||||
result = append(result, CODE62[remain])
|
||||
number = round
|
||||
}
|
||||
return string(result)
|
||||
}
|
||||
|
||||
// 生成用户touken
|
||||
func GeneratorToken(playerName string, playerId int) string {
|
||||
//去生成一个token返回给客户端
|
||||
m5 := SToMd5(playerName + time.Now().String())
|
||||
var pid = int64(playerId)
|
||||
bsCode := Encode(pid)
|
||||
tk := m5 + bsCode
|
||||
//将token放入redis
|
||||
_, err := redis.GetRedis(redis.SingletonMain).SetEX(context.Background(), GetRealKey(common.TOKEN_PRE+tk), playerId, time.Duration(3600)*time.Second).Result()
|
||||
if err != nil {
|
||||
Log(nil, "token", err)
|
||||
}
|
||||
return tk
|
||||
}
|
||||
|
||||
// ExistFile 检查给定的文件是否存在
|
||||
func ExistFile(dirPath string) (exist bool, err error) {
|
||||
// 使用filepath.Abs获取绝对路径,确保我们处理的是实际存在的路径
|
||||
absPath, err := filepath.Abs(dirPath)
|
||||
if err != nil {
|
||||
return exist, fmt.Errorf("failed to get absolute path: %v", err)
|
||||
}
|
||||
|
||||
// 使用os.Stat检查路径是否存在
|
||||
_, err = os.Stat(absPath)
|
||||
if errors.Is(err, os.ErrNotExist) {
|
||||
return exist, nil
|
||||
}
|
||||
|
||||
return true, err
|
||||
}
|
||||
|
||||
// StrToTimeShanghai 字符串转时间
|
||||
func StrToTimeShanghai(strTime string) (t time.Time, err error) {
|
||||
location, _ := time.LoadLocation("Asia/Shanghai")
|
||||
return time.ParseInLocation(time.DateTime, strTime, location)
|
||||
}
|
||||
|
||||
func GenIncrementId(tableName string) (int, error) {
|
||||
var id, err = redis.GetRedis().Incr(context.Background(), GetRealKey(tableName)).Result()
|
||||
|
||||
return int(id), err
|
||||
}
|
||||
|
||||
// CheckOrCreateYmdDirectory 创建ymd目录
|
||||
func CheckOrCreateYmdDirectory(dirPath string) (SavePath string, err error) {
|
||||
// 使用filepath.Abs获取绝对路径,确保我们处理的是实际存在的路径
|
||||
now := time.Now()
|
||||
year := now.Year()
|
||||
month := now.Month()
|
||||
day := now.Day()
|
||||
savePath := filepath.Join("uploads", dirPath, fmt.Sprintf("%d", year), fmt.Sprintf("%d", month), fmt.Sprintf("%d", day))
|
||||
absPath, err := filepath.Abs(savePath)
|
||||
if err != nil {
|
||||
return SavePath, fmt.Errorf("failed to get absolute path: %v", err)
|
||||
}
|
||||
// 使用os.Stat检查路径是否存在
|
||||
info, err := os.Stat(absPath)
|
||||
if err != nil {
|
||||
if !os.IsNotExist(err) {
|
||||
return SavePath, fmt.Errorf("error checking directory: %v", err)
|
||||
}
|
||||
|
||||
// 目录不存在,尝试创建
|
||||
err = os.MkdirAll(absPath, 0755) // 0755是默认权限,可以按需调整
|
||||
if err != nil {
|
||||
return SavePath, fmt.Errorf("failed to create directory: %v", err)
|
||||
}
|
||||
} else if !info.IsDir() {
|
||||
return SavePath, fmt.Errorf("%s exists but it's not a directory", absPath)
|
||||
}
|
||||
SavePath = absPath
|
||||
return SavePath, nil
|
||||
}
|
||||
|
||||
// CheckOrCreateDirectory 检查给定的目录是否存在,如果不存在则创建它
|
||||
func CheckOrCreateDirectory(dirPath string) error {
|
||||
// 使用filepath.Abs获取绝对路径,确保我们处理的是实际存在的路径
|
||||
absPath, err := filepath.Abs(dirPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get absolute path: %v", err)
|
||||
}
|
||||
|
||||
// 使用os.Stat检查路径是否存在
|
||||
info, err := os.Stat(absPath)
|
||||
if err != nil {
|
||||
if !os.IsNotExist(err) {
|
||||
return fmt.Errorf("error checking directory: %v", err)
|
||||
}
|
||||
|
||||
// 目录不存在,尝试创建
|
||||
err = os.MkdirAll(absPath, 0755) // 0755是默认权限,可以按需调整
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create directory: %v", err)
|
||||
}
|
||||
} else if !info.IsDir() {
|
||||
return fmt.Errorf("%s exists but it's not a directory", absPath)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func ToSnakeCase(s string) string {
|
||||
ch, en := splitChineseEnglish(s)
|
||||
fmt.Println(ch, en)
|
||||
re := regexp.MustCompile("([a-z0-9])([A-Z])")
|
||||
snake := re.ReplaceAllString(en, "${1}_${2}")
|
||||
return strings.ToLower(snake) + ch
|
||||
}
|
||||
|
||||
func splitChineseEnglish(input string) (chinese string, english string) {
|
||||
var index = findChineseStartIndex(input)
|
||||
|
||||
return input[index:], input[0:index]
|
||||
}
|
||||
|
||||
func findChineseStartIndex(input string) int {
|
||||
runes := []rune(input)
|
||||
for i, r := range runes {
|
||||
if unicode.Is(unicode.Han, r) {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return -1 // 如果没有找到中文字符,返回-1
|
||||
}
|
||||
|
||||
func DownloadFileFromOss(url, savePath string) error {
|
||||
_, bucket, err := AliOssClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = bucket.GetObjectToFileWithURL(url, savePath)
|
||||
return err
|
||||
}
|
||||
|
||||
func EntityCopy(dst, src interface{}) {
|
||||
dstValue := reflect.ValueOf(dst).Elem()
|
||||
srcValue := reflect.ValueOf(src).Elem()
|
||||
|
||||
for i := 0; i < srcValue.NumField(); i++ {
|
||||
srcField := srcValue.Field(i)
|
||||
|
||||
srcName := srcValue.Type().Field(i).Name
|
||||
dstFieldByName := dstValue.FieldByName(srcName)
|
||||
|
||||
if dstFieldByName.IsValid() {
|
||||
switch dstFieldByName.Kind() {
|
||||
case reflect.Ptr:
|
||||
switch srcField.Kind() {
|
||||
case reflect.Ptr:
|
||||
if srcField.IsNil() {
|
||||
dstFieldByName.Set(reflect.New(dstFieldByName.Type().Elem()))
|
||||
} else {
|
||||
dstFieldByName.Set(srcField)
|
||||
}
|
||||
default:
|
||||
dstFieldByName.Set(srcField.Addr())
|
||||
}
|
||||
default:
|
||||
switch srcField.Kind() {
|
||||
case reflect.Ptr:
|
||||
if srcField.IsNil() {
|
||||
dstFieldByName.Set(reflect.Zero(dstFieldByName.Type()))
|
||||
} else {
|
||||
dstFieldByName.Set(srcField.Elem())
|
||||
}
|
||||
default:
|
||||
if srcField.Type().Name() == "Time" {
|
||||
if (srcField.Interface().(time.Time).Unix()) < 1 {
|
||||
dstFieldByName.Set(reflect.ValueOf(""))
|
||||
} else {
|
||||
dstFieldByName.Set(reflect.ValueOf(srcField.Interface().(time.Time).Format("2006-01-02 15:04:05")))
|
||||
}
|
||||
|
||||
} else {
|
||||
dstFieldByName.Set(srcField)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// AliOssClient 返回Oss客户链接
|
||||
func AliOssClient() (client *oss.Client, Bucket *oss.Bucket, err error) {
|
||||
/*
|
||||
oss 的相关配置信息
|
||||
*/
|
||||
bucketName := config.GetConf().AliOss.BucKet
|
||||
endpoint := config.GetConf().AliOss.EndPoint
|
||||
accessKeyId := config.GetConf().AliOss.AccessKey
|
||||
accessKeySecret := config.GetConf().AliOss.AccessKeySecret
|
||||
//domain := config.GetConf().AliOss.Domain
|
||||
//Dir := config.GetConf().AliOss.Dir
|
||||
|
||||
//创建OSSClient实例
|
||||
client, err = oss.New(endpoint, accessKeyId, accessKeySecret)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// 获取存储空间
|
||||
Bucket, err = client.Bucket(bucketName)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return client, Bucket, nil
|
||||
}
|
||||
func GetSHA256HashCode(message []byte) string {
|
||||
//方法一:
|
||||
//创建一个基于SHA256算法的hash.Hash接口的对象
|
||||
hash := sha256.New()
|
||||
//输入数据
|
||||
hash.Write(message)
|
||||
//计算哈希值
|
||||
bytes := hash.Sum(nil)
|
||||
//将字符串编码为16进制格式,返回字符串
|
||||
hashCode := hex.EncodeToString(bytes)
|
||||
//返回哈希值
|
||||
return hashCode
|
||||
|
||||
//方法二:
|
||||
//bytes2:=sha256.Sum256(message)//计算哈希值,返回一个长度为32的数组
|
||||
//hashcode2:=hex.EncodeToString(bytes2[:])//将数组转换成切片,转换成16进制,返回字符串
|
||||
//return hashcode2
|
||||
}
|
||||
func GetSHA512HashCode(message []byte) string {
|
||||
|
||||
hash := sha512.New()
|
||||
hash.Write(message)
|
||||
bytes := hash.Sum(nil)
|
||||
hashCode := hex.EncodeToString(bytes)
|
||||
return hashCode
|
||||
}
|
||||
|
||||
// SM2Encode sm2公钥加密
|
||||
func SM2Encode(pubKey string, plaintext string, mode int) (string, error) {
|
||||
pubMen, err := x509.ReadPublicKeyFromHex(pubKey)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
msg := []byte(plaintext)
|
||||
ciphertxt, err := sm2.Encrypt(pubMen, msg, rand.Reader, mode)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return hex.EncodeToString(ciphertxt), nil
|
||||
}
|
||||
|
||||
// SM2Decode sm2私钥解密
|
||||
func SM2Decode(privKey string, data string, mode int) (string, error) {
|
||||
priv, err := x509.ReadPrivateKeyFromHex(privKey)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
ciphertext, err := hex.DecodeString(data)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
plaintext, err := sm2.Decrypt(priv, []byte(ciphertext), mode)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(plaintext), nil
|
||||
}
|
||||
|
||||
func GenerateOrderNumber() string {
|
||||
// 生成当前日期部分(例如:20231008)
|
||||
datePart := time.Now().Format("20060102150405")
|
||||
|
||||
// 生成随机数部分(4位随机数)
|
||||
mrand.Seed(time.Now().UnixNano())
|
||||
randomPart := fmt.Sprintf("%04d", mrand.Intn(10000))
|
||||
|
||||
// 添加固定前缀
|
||||
prefix := "SN"
|
||||
|
||||
// 最终的订单号由前缀、日期和随机数部分组成
|
||||
orderNumber := fmt.Sprintf("%s%s%s", prefix, datePart, randomPart)
|
||||
|
||||
return orderNumber
|
||||
}
|
||||
func IsNil(x interface{}) bool {
|
||||
if x == nil {
|
||||
return true
|
||||
}
|
||||
rv := reflect.ValueOf(x)
|
||||
return rv.Kind() == reflect.Ptr && rv.IsNil()
|
||||
}
|
||||
|
||||
type User struct {
|
||||
Id int
|
||||
Phone string
|
||||
}
|
||||
|
||||
func GeneratorJwtToken(user User) string {
|
||||
// 定义一个用于签名的密钥
|
||||
secretKey := []byte(config.GetConf().Jwt.SecretKey)
|
||||
|
||||
// 创建一个MapClaims对象,用于存放自定义的声明信息
|
||||
claims := jwt.MapClaims{
|
||||
"id": user.Id,
|
||||
"phone": user.Phone,
|
||||
"exp": time.Now().Add(time.Hour * 24).Unix(), // 设置过期时间为24小时后
|
||||
}
|
||||
// 使用HS256算法创建一个Token对象
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
||||
|
||||
// 使用密钥对Token进行签名,生成JWT字符串
|
||||
tokenString, err := token.SignedString(secretKey)
|
||||
if err != nil {
|
||||
fmt.Println("Failed to create token:", err)
|
||||
return ""
|
||||
}
|
||||
return tokenString
|
||||
}
|
||||
|
||||
type Claims struct {
|
||||
Id int
|
||||
Phone string
|
||||
jwt.StandardClaims
|
||||
}
|
||||
|
||||
func ParseToken(tokenString string) (*jwt.Token, *Claims, error) {
|
||||
Claims := &Claims{}
|
||||
token, err := jwt.ParseWithClaims(tokenString, Claims, func(token *jwt.Token) (i interface{}, err error) {
|
||||
return []byte(config.GetConf().Jwt.SecretKey), nil
|
||||
})
|
||||
return token, Claims, err
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
package bootstrap
|
||||
|
||||
import (
|
||||
"github.com/qit-team/snow-core/log/accesslogger"
|
||||
"qteam/app/jobs"
|
||||
"qteam/app/jobs/basejob"
|
||||
"qteam/config"
|
||||
|
||||
"github.com/qit-team/snow-core/db"
|
||||
"github.com/qit-team/snow-core/kernel/close"
|
||||
"github.com/qit-team/snow-core/kernel/container"
|
||||
"github.com/qit-team/snow-core/log/logger"
|
||||
"github.com/qit-team/snow-core/redis"
|
||||
)
|
||||
|
||||
// 全局变量
|
||||
var App *container.Container
|
||||
|
||||
/**
|
||||
* 服务引导程序
|
||||
*/
|
||||
func Bootstrap(conf *config.Config) (err error) {
|
||||
//容器
|
||||
App = container.App
|
||||
|
||||
//注册db服务
|
||||
//第一个参数为注入别名,第二个参数为配置,第三个参数可选为是否懒加载
|
||||
err = db.Pr.Register(db.SingletonMain, conf.Db)
|
||||
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
//注册redis服务
|
||||
err = redis.Pr.Register(redis.SingletonMain, conf.Redis)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
//注册mns服务
|
||||
//err = alimns.Pr.Register(alimns.SingletonMain, conf.Mns, true)
|
||||
//if err != nil {
|
||||
// return
|
||||
//}
|
||||
|
||||
//注册日志类服务
|
||||
err = logger.Pr.Register(logger.SingletonMain, conf.Log, true)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
//注册access log服务
|
||||
err = accesslogger.Pr.Register(accesslogger.SingletonMain, conf.Log)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
//注册应用停止时调用的关闭服务
|
||||
close.MultiRegister(db.Pr, redis.Pr)
|
||||
////Register
|
||||
//_, err = utils.GetNaocosClient().RegisterInstance(vo.RegisterInstanceParam{
|
||||
// Ip: utils.GetHostIp(),
|
||||
// Port: uint64(conf.Api.Port),
|
||||
// ServiceName: "snow",
|
||||
// GroupName: "group-d",
|
||||
// ClusterName: "cluster-snow",
|
||||
// Weight: 10,
|
||||
// Enable: true,
|
||||
// Healthy: true,
|
||||
// Ephemeral: true,
|
||||
// Metadata: map[string]string{},
|
||||
//})
|
||||
//
|
||||
//utils.Log(nil, "nacos err", err)
|
||||
|
||||
//注册job register,为了非job模式的消息入队调用
|
||||
basejob.SetJobRegister(jobs.RegisterWorker)
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,2 @@
|
|||
*
|
||||
!.gitignore
|
|
@ -0,0 +1,9 @@
|
|||
#/bin/bash
|
||||
os=$1 #系统linux
|
||||
arch=$2 #架构amd64
|
||||
|
||||
#回到根目录
|
||||
rootPath=$(cd `dirname $0`/../../; pwd)
|
||||
|
||||
#编译
|
||||
GOOS=$os GOARCH=$arch go build -o build/bin/snow main.go
|
|
@ -0,0 +1,125 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"github.com/BurntSushi/toml"
|
||||
"github.com/qit-team/snow-core/config"
|
||||
"os"
|
||||
)
|
||||
|
||||
const (
|
||||
ProdEnv = "production" //线上环境
|
||||
BetaEnv = "beta" //beta环境
|
||||
DevEnv = "develop" //开发环境
|
||||
LocalEnv = "local" //本地环境
|
||||
)
|
||||
|
||||
var srvConf *Config
|
||||
|
||||
// ------------------------配置文件解析
|
||||
type Config struct {
|
||||
ServiceName string `toml:"ServiceName"`
|
||||
Env string `toml:"Env"`
|
||||
Debug bool `toml:"Debug"`
|
||||
PrometheusCollectEnable bool `toml:"PrometheusCollectEnable"`
|
||||
SkyWalkingOapServer string `toml:"SkyWalkingOapServer"`
|
||||
Log config.LogConfig `toml:"Log"`
|
||||
Redis config.RedisConfig `toml:"Redis"`
|
||||
Mns config.MnsConfig `toml:"AliMns"`
|
||||
Db config.DbConfig `toml:"Db"`
|
||||
Api config.ApiConfig `toml:"Api"`
|
||||
Admin config.ApiConfig `toml:"Admin"`
|
||||
Nacos Nacos `toml:"Nacas"`
|
||||
Rpc Rpc `toml:"Rpc"`
|
||||
AppKey string `toml:"AppKey"`
|
||||
Sm2 Sm2 `toml:"Sm2"`
|
||||
OpenApiMarketConfig MarketConfig `toml:"MarketConfig"`
|
||||
OpenApi OpenApi `toml:"OpenApi"`
|
||||
Jwt Jwt `toml:"Jwt"`
|
||||
AliOss AliOss `toml:"AliOss"`
|
||||
}
|
||||
|
||||
type AliOss struct {
|
||||
AccessKey string
|
||||
AccessKeySecret string
|
||||
EndPoint string
|
||||
BucKet string
|
||||
Domain string
|
||||
Dir string
|
||||
}
|
||||
|
||||
type Jwt struct {
|
||||
SecretKey string
|
||||
}
|
||||
|
||||
type OpenApi struct {
|
||||
MerchantId string
|
||||
SecretKey string
|
||||
IsProd bool
|
||||
NotifyUrl string
|
||||
TimeOut int
|
||||
}
|
||||
|
||||
type MarketConfig struct {
|
||||
AppId string `json:"app_id"` //APP ID
|
||||
Sign string `json:"sign"` //签名
|
||||
ReqCode string `json:"req_code"` //固定值:voucher.create
|
||||
MemId string `json:"mem_id"` //商户号
|
||||
PosId string `json:"pos_id"` //商户方平台号
|
||||
Host string `json:"-"`
|
||||
SecretKey string
|
||||
}
|
||||
|
||||
type Sm2 struct {
|
||||
PublicKey string
|
||||
PrivateKey string
|
||||
}
|
||||
|
||||
type Rpc struct {
|
||||
User string
|
||||
}
|
||||
type Nacos struct {
|
||||
Url string
|
||||
Port int64
|
||||
}
|
||||
|
||||
func newConfig() *Config {
|
||||
return new(Config)
|
||||
}
|
||||
|
||||
// ------------------------ 加载配置 ------------------------//
|
||||
func Load(path string) (*Config, error) {
|
||||
_, err := os.Stat(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
conf := newConfig()
|
||||
if _, err := toml.DecodeFile(path, conf); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
srvConf = conf
|
||||
return conf, nil
|
||||
}
|
||||
|
||||
// 当前配置
|
||||
func GetConf() *Config {
|
||||
return srvConf
|
||||
}
|
||||
|
||||
// 是否调试模式
|
||||
func IsDebug() bool {
|
||||
return srvConf.Debug
|
||||
}
|
||||
|
||||
// 当前环境,默认本地开发
|
||||
func GetEnv() string {
|
||||
if srvConf.Env == "" {
|
||||
return LocalEnv
|
||||
}
|
||||
return srvConf.Env
|
||||
}
|
||||
|
||||
// 是否当前环境
|
||||
func IsEnvEqual(env string) bool {
|
||||
return GetEnv() == env
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var options *Options
|
||||
|
||||
// ------------------------启动命令配置
|
||||
type Options struct {
|
||||
ShowVersion bool
|
||||
Cmd string
|
||||
ConfFile string
|
||||
App string
|
||||
PidDir string
|
||||
Queue string
|
||||
Command string
|
||||
}
|
||||
|
||||
func parseOptions() *Options {
|
||||
opts := new(Options)
|
||||
flag.BoolVar(&opts.ShowVersion, "v", false, "show version")
|
||||
flag.StringVar(&opts.App, "a", "admin", "application to run")
|
||||
flag.StringVar(&opts.Cmd, "k", "", "status|stop|restart")
|
||||
flag.StringVar(&opts.ConfFile, "c", ".env", "conf file path")
|
||||
flag.StringVar(&opts.PidDir, "p", "/var/run/", "pid directory")
|
||||
flag.StringVar(&opts.Queue, "queue", "", "topic of queue is enable")
|
||||
flag.StringVar(&opts.Command, "m", "", "command name")
|
||||
flag.Parse()
|
||||
return opts
|
||||
}
|
||||
|
||||
// 获取启动命令配置
|
||||
func GetOptions() *Options {
|
||||
if options == nil {
|
||||
options = parseOptions()
|
||||
}
|
||||
return options
|
||||
}
|
||||
|
||||
// pid进程号的保存路径
|
||||
func (opts *Options) GenPidFile() string {
|
||||
return strings.TrimRight(opts.PidDir, "/") + "/" + opts.App + ".pid"
|
||||
}
|
|
@ -0,0 +1,342 @@
|
|||
// Package docs Code generated by swaggo/swag. DO NOT EDIT
|
||||
package docs
|
||||
|
||||
import "github.com/swaggo/swag"
|
||||
|
||||
const docTemplate = `{
|
||||
"schemes": {{ marshal .Schemes }},
|
||||
"swagger": "2.0",
|
||||
"info": {
|
||||
"description": "{{escape .Description}}",
|
||||
"title": "{{.Title}}",
|
||||
"termsOfService": "http://swagger.io/terms/",
|
||||
"contact": {
|
||||
"name": "API Support",
|
||||
"url": "http://www.swagger.io/support",
|
||||
"email": "support@swagger.io"
|
||||
},
|
||||
"license": {
|
||||
"name": "Apache 2.0",
|
||||
"url": "http://www.apache.org/licenses/LICENSE-2.0.html"
|
||||
},
|
||||
"version": "{{.Version}}"
|
||||
},
|
||||
"host": "{{.Host}}",
|
||||
"basePath": "{{.BasePath}}",
|
||||
"paths": {
|
||||
"/test": {
|
||||
"post": {
|
||||
"description": "request和response的示例",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"snow"
|
||||
],
|
||||
"summary": "request和response的示例",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "test request",
|
||||
"name": "test",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/entities.TestRequest"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/entities.TestResponse"
|
||||
}
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/controllers.HTTPError"
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Not Found",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/controllers.HTTPError"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/controllers.HTTPError"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/test_validator": {
|
||||
"post": {
|
||||
"description": "HandleTestValidator的示例",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"snow"
|
||||
],
|
||||
"summary": "HandleTestValidator的示例",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "example of validator",
|
||||
"name": "testValidator",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/entities.TestValidatorRequest"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/entities.TestValidatorRequest"
|
||||
}
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/controllers.HTTPError"
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Not Found",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/controllers.HTTPError"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/controllers.HTTPError"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"definitions": {
|
||||
"controllers.HTTPError": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"code": {
|
||||
"type": "integer",
|
||||
"example": 400
|
||||
},
|
||||
"message": {
|
||||
"type": "string",
|
||||
"example": "status bad request"
|
||||
}
|
||||
}
|
||||
},
|
||||
"entities.Address": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"city",
|
||||
"phone",
|
||||
"planet",
|
||||
"street"
|
||||
],
|
||||
"properties": {
|
||||
"city": {
|
||||
"type": "string",
|
||||
"example": "xiamen"
|
||||
},
|
||||
"phone": {
|
||||
"type": "string",
|
||||
"example": "snow"
|
||||
},
|
||||
"planet": {
|
||||
"type": "string",
|
||||
"example": "snow"
|
||||
},
|
||||
"street": {
|
||||
"type": "string",
|
||||
"example": "huandaodonglu"
|
||||
}
|
||||
}
|
||||
},
|
||||
"entities.TestRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"example": "snow"
|
||||
},
|
||||
"url": {
|
||||
"type": "string",
|
||||
"example": "github.com/qit-team/snow"
|
||||
}
|
||||
}
|
||||
},
|
||||
"entities.TestResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "integer",
|
||||
"example": 1
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
"example": "snow"
|
||||
},
|
||||
"url": {
|
||||
"type": "string",
|
||||
"example": "github.com/qit-team/snow"
|
||||
}
|
||||
}
|
||||
},
|
||||
"entities.TestValidatorRequest": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"addresses",
|
||||
"age",
|
||||
"email",
|
||||
"id",
|
||||
"mobile",
|
||||
"name",
|
||||
"test_num",
|
||||
"url"
|
||||
],
|
||||
"properties": {
|
||||
"addresses": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/entities.Address"
|
||||
}
|
||||
},
|
||||
"age": {
|
||||
"type": "integer",
|
||||
"maximum": 130,
|
||||
"minimum": 0,
|
||||
"example": 20
|
||||
},
|
||||
"content": {
|
||||
"type": "string",
|
||||
"example": "snow"
|
||||
},
|
||||
"email": {
|
||||
"type": "string",
|
||||
"example": "snow@github.com"
|
||||
},
|
||||
"id": {
|
||||
"description": "tips,因为组件required不管是没传值或者传 0 or \"\" 都通过不了,但是如果用指针类型,那么0就是0,而nil无法通过校验",
|
||||
"type": "integer",
|
||||
"example": 1
|
||||
},
|
||||
"mobile": {
|
||||
"type": "string",
|
||||
"example": "snow"
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
"example": "snow"
|
||||
},
|
||||
"range_num": {
|
||||
"type": "integer",
|
||||
"maximum": 10,
|
||||
"minimum": 1,
|
||||
"example": 3
|
||||
},
|
||||
"test_num": {
|
||||
"type": "integer",
|
||||
"enum": [
|
||||
5,
|
||||
7,
|
||||
9
|
||||
],
|
||||
"example": 7
|
||||
},
|
||||
"url": {
|
||||
"type": "string",
|
||||
"example": "github.com/qit-team/snow"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"securityDefinitions": {
|
||||
"ApiKeyAuth": {
|
||||
"type": "apiKey",
|
||||
"name": "Authorization",
|
||||
"in": "header"
|
||||
},
|
||||
"BasicAuth": {
|
||||
"type": "basic"
|
||||
},
|
||||
"OAuth2AccessCode": {
|
||||
"type": "oauth2",
|
||||
"flow": "accessCode",
|
||||
"authorizationUrl": "https://example.com/oauth/authorize",
|
||||
"tokenUrl": "https://example.com/oauth/token",
|
||||
"scopes": {
|
||||
"admin": "Grants read and write access to administrative information"
|
||||
}
|
||||
},
|
||||
"OAuth2Application": {
|
||||
"type": "oauth2",
|
||||
"flow": "application",
|
||||
"tokenUrl": "https://example.com/oauth/token",
|
||||
"scopes": {
|
||||
"admin": "Grants read and write access to administrative information",
|
||||
"write": "Grants write access"
|
||||
}
|
||||
},
|
||||
"OAuth2Implicit": {
|
||||
"type": "oauth2",
|
||||
"flow": "implicit",
|
||||
"authorizationUrl": "https://example.com/oauth/authorize",
|
||||
"scopes": {
|
||||
"admin": "Grants read and write access to administrative information",
|
||||
"write": "Grants write access"
|
||||
}
|
||||
},
|
||||
"OAuth2Password": {
|
||||
"type": "oauth2",
|
||||
"flow": "password",
|
||||
"tokenUrl": "https://example.com/oauth/token",
|
||||
"scopes": {
|
||||
"admin": "Grants read and write access to administrative information",
|
||||
"read": "Grants read access",
|
||||
"write": "Grants write access"
|
||||
}
|
||||
}
|
||||
}
|
||||
}`
|
||||
|
||||
// SwaggerInfo holds exported Swagger Info so clients can modify it
|
||||
var SwaggerInfo = &swag.Spec{
|
||||
Version: "1.0",
|
||||
Host: "localhost:8080",
|
||||
BasePath: "/",
|
||||
Schemes: []string{},
|
||||
Title: "Swagger Example API",
|
||||
Description: "This is a sample server celler server.",
|
||||
InfoInstanceName: "swagger",
|
||||
SwaggerTemplate: docTemplate,
|
||||
//LeftDelim: "{{",
|
||||
//RightDelim: "}}",
|
||||
}
|
||||
|
||||
func init() {
|
||||
swag.Register(SwaggerInfo.InstanceName(), SwaggerInfo)
|
||||
}
|
|
@ -0,0 +1,318 @@
|
|||
{
|
||||
"swagger": "2.0",
|
||||
"info": {
|
||||
"description": "This is a sample server celler server.",
|
||||
"title": "Swagger Example API",
|
||||
"termsOfService": "http://swagger.io/terms/",
|
||||
"contact": {
|
||||
"name": "API Support",
|
||||
"url": "http://www.swagger.io/support",
|
||||
"email": "support@swagger.io"
|
||||
},
|
||||
"license": {
|
||||
"name": "Apache 2.0",
|
||||
"url": "http://www.apache.org/licenses/LICENSE-2.0.html"
|
||||
},
|
||||
"version": "1.0"
|
||||
},
|
||||
"host": "localhost:8080",
|
||||
"basePath": "/",
|
||||
"paths": {
|
||||
"/test": {
|
||||
"post": {
|
||||
"description": "request和response的示例",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"snow"
|
||||
],
|
||||
"summary": "request和response的示例",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "test request",
|
||||
"name": "test",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/entities.TestRequest"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/entities.TestResponse"
|
||||
}
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/controllers.HTTPError"
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Not Found",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/controllers.HTTPError"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/controllers.HTTPError"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/test_validator": {
|
||||
"post": {
|
||||
"description": "HandleTestValidator的示例",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"snow"
|
||||
],
|
||||
"summary": "HandleTestValidator的示例",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "example of validator",
|
||||
"name": "testValidator",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/entities.TestValidatorRequest"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/entities.TestValidatorRequest"
|
||||
}
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/controllers.HTTPError"
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Not Found",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/controllers.HTTPError"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/controllers.HTTPError"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"definitions": {
|
||||
"controllers.HTTPError": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"code": {
|
||||
"type": "integer",
|
||||
"example": 400
|
||||
},
|
||||
"message": {
|
||||
"type": "string",
|
||||
"example": "status bad request"
|
||||
}
|
||||
}
|
||||
},
|
||||
"entities.Address": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"city",
|
||||
"phone",
|
||||
"planet",
|
||||
"street"
|
||||
],
|
||||
"properties": {
|
||||
"city": {
|
||||
"type": "string",
|
||||
"example": "xiamen"
|
||||
},
|
||||
"phone": {
|
||||
"type": "string",
|
||||
"example": "snow"
|
||||
},
|
||||
"planet": {
|
||||
"type": "string",
|
||||
"example": "snow"
|
||||
},
|
||||
"street": {
|
||||
"type": "string",
|
||||
"example": "huandaodonglu"
|
||||
}
|
||||
}
|
||||
},
|
||||
"entities.TestRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"example": "snow"
|
||||
},
|
||||
"url": {
|
||||
"type": "string",
|
||||
"example": "github.com/qit-team/snow"
|
||||
}
|
||||
}
|
||||
},
|
||||
"entities.TestResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "integer",
|
||||
"example": 1
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
"example": "snow"
|
||||
},
|
||||
"url": {
|
||||
"type": "string",
|
||||
"example": "github.com/qit-team/snow"
|
||||
}
|
||||
}
|
||||
},
|
||||
"entities.TestValidatorRequest": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"addresses",
|
||||
"age",
|
||||
"email",
|
||||
"id",
|
||||
"mobile",
|
||||
"name",
|
||||
"test_num",
|
||||
"url"
|
||||
],
|
||||
"properties": {
|
||||
"addresses": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/entities.Address"
|
||||
}
|
||||
},
|
||||
"age": {
|
||||
"type": "integer",
|
||||
"maximum": 130,
|
||||
"minimum": 0,
|
||||
"example": 20
|
||||
},
|
||||
"content": {
|
||||
"type": "string",
|
||||
"example": "snow"
|
||||
},
|
||||
"email": {
|
||||
"type": "string",
|
||||
"example": "snow@github.com"
|
||||
},
|
||||
"id": {
|
||||
"description": "tips,因为组件required不管是没传值或者传 0 or \"\" 都通过不了,但是如果用指针类型,那么0就是0,而nil无法通过校验",
|
||||
"type": "integer",
|
||||
"example": 1
|
||||
},
|
||||
"mobile": {
|
||||
"type": "string",
|
||||
"example": "snow"
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
"example": "snow"
|
||||
},
|
||||
"range_num": {
|
||||
"type": "integer",
|
||||
"maximum": 10,
|
||||
"minimum": 1,
|
||||
"example": 3
|
||||
},
|
||||
"test_num": {
|
||||
"type": "integer",
|
||||
"enum": [
|
||||
5,
|
||||
7,
|
||||
9
|
||||
],
|
||||
"example": 7
|
||||
},
|
||||
"url": {
|
||||
"type": "string",
|
||||
"example": "github.com/qit-team/snow"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"securityDefinitions": {
|
||||
"ApiKeyAuth": {
|
||||
"type": "apiKey",
|
||||
"name": "Authorization",
|
||||
"in": "header"
|
||||
},
|
||||
"BasicAuth": {
|
||||
"type": "basic"
|
||||
},
|
||||
"OAuth2AccessCode": {
|
||||
"type": "oauth2",
|
||||
"flow": "accessCode",
|
||||
"authorizationUrl": "https://example.com/oauth/authorize",
|
||||
"tokenUrl": "https://example.com/oauth/token",
|
||||
"scopes": {
|
||||
"admin": "Grants read and write access to administrative information"
|
||||
}
|
||||
},
|
||||
"OAuth2Application": {
|
||||
"type": "oauth2",
|
||||
"flow": "application",
|
||||
"tokenUrl": "https://example.com/oauth/token",
|
||||
"scopes": {
|
||||
"admin": "Grants read and write access to administrative information",
|
||||
"write": "Grants write access"
|
||||
}
|
||||
},
|
||||
"OAuth2Implicit": {
|
||||
"type": "oauth2",
|
||||
"flow": "implicit",
|
||||
"authorizationUrl": "https://example.com/oauth/authorize",
|
||||
"scopes": {
|
||||
"admin": "Grants read and write access to administrative information",
|
||||
"write": "Grants write access"
|
||||
}
|
||||
},
|
||||
"OAuth2Password": {
|
||||
"type": "oauth2",
|
||||
"flow": "password",
|
||||
"tokenUrl": "https://example.com/oauth/token",
|
||||
"scopes": {
|
||||
"admin": "Grants read and write access to administrative information",
|
||||
"read": "Grants read access",
|
||||
"write": "Grants write access"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,227 @@
|
|||
basePath: /
|
||||
definitions:
|
||||
controllers.HTTPError:
|
||||
properties:
|
||||
code:
|
||||
example: 400
|
||||
type: integer
|
||||
message:
|
||||
example: status bad request
|
||||
type: string
|
||||
type: object
|
||||
entities.Address:
|
||||
properties:
|
||||
city:
|
||||
example: xiamen
|
||||
type: string
|
||||
phone:
|
||||
example: snow
|
||||
type: string
|
||||
planet:
|
||||
example: snow
|
||||
type: string
|
||||
street:
|
||||
example: huandaodonglu
|
||||
type: string
|
||||
required:
|
||||
- city
|
||||
- phone
|
||||
- planet
|
||||
- street
|
||||
type: object
|
||||
entities.TestRequest:
|
||||
properties:
|
||||
name:
|
||||
example: snow
|
||||
type: string
|
||||
url:
|
||||
example: github.com/qit-team/snow
|
||||
type: string
|
||||
type: object
|
||||
entities.TestResponse:
|
||||
properties:
|
||||
id:
|
||||
example: 1
|
||||
type: integer
|
||||
name:
|
||||
example: snow
|
||||
type: string
|
||||
url:
|
||||
example: github.com/qit-team/snow
|
||||
type: string
|
||||
type: object
|
||||
entities.TestValidatorRequest:
|
||||
properties:
|
||||
addresses:
|
||||
items:
|
||||
$ref: '#/definitions/entities.Address'
|
||||
type: array
|
||||
age:
|
||||
example: 20
|
||||
maximum: 130
|
||||
minimum: 0
|
||||
type: integer
|
||||
content:
|
||||
example: snow
|
||||
type: string
|
||||
email:
|
||||
example: snow@github.com
|
||||
type: string
|
||||
id:
|
||||
description: tips,因为组件required不管是没传值或者传 0 or "" 都通过不了,但是如果用指针类型,那么0就是0,而nil无法通过校验
|
||||
example: 1
|
||||
type: integer
|
||||
mobile:
|
||||
example: snow
|
||||
type: string
|
||||
name:
|
||||
example: snow
|
||||
type: string
|
||||
range_num:
|
||||
example: 3
|
||||
maximum: 10
|
||||
minimum: 1
|
||||
type: integer
|
||||
test_num:
|
||||
enum:
|
||||
- 5
|
||||
- 7
|
||||
- 9
|
||||
example: 7
|
||||
type: integer
|
||||
url:
|
||||
example: github.com/qit-team/snow
|
||||
type: string
|
||||
required:
|
||||
- addresses
|
||||
- age
|
||||
- email
|
||||
- id
|
||||
- mobile
|
||||
- name
|
||||
- test_num
|
||||
- url
|
||||
type: object
|
||||
host: localhost:8080
|
||||
info:
|
||||
contact:
|
||||
email: support@swagger.io
|
||||
name: API Support
|
||||
url: http://www.swagger.io/support
|
||||
description: This is a sample server celler server.
|
||||
license:
|
||||
name: Apache 2.0
|
||||
url: http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
termsOfService: http://swagger.io/terms/
|
||||
title: Swagger Example API
|
||||
version: "1.0"
|
||||
paths:
|
||||
/test:
|
||||
post:
|
||||
consumes:
|
||||
- application/json
|
||||
description: request和response的示例
|
||||
parameters:
|
||||
- description: test request
|
||||
in: body
|
||||
name: test
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/entities.TestRequest'
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
items:
|
||||
$ref: '#/definitions/entities.TestResponse'
|
||||
type: array
|
||||
"400":
|
||||
description: Bad Request
|
||||
schema:
|
||||
$ref: '#/definitions/controllers.HTTPError'
|
||||
"404":
|
||||
description: Not Found
|
||||
schema:
|
||||
$ref: '#/definitions/controllers.HTTPError'
|
||||
"500":
|
||||
description: Internal Server Error
|
||||
schema:
|
||||
$ref: '#/definitions/controllers.HTTPError'
|
||||
summary: request和response的示例
|
||||
tags:
|
||||
- snow
|
||||
/test_validator:
|
||||
post:
|
||||
consumes:
|
||||
- application/json
|
||||
description: HandleTestValidator的示例
|
||||
parameters:
|
||||
- description: example of validator
|
||||
in: body
|
||||
name: testValidator
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/entities.TestValidatorRequest'
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
items:
|
||||
$ref: '#/definitions/entities.TestValidatorRequest'
|
||||
type: array
|
||||
"400":
|
||||
description: Bad Request
|
||||
schema:
|
||||
$ref: '#/definitions/controllers.HTTPError'
|
||||
"404":
|
||||
description: Not Found
|
||||
schema:
|
||||
$ref: '#/definitions/controllers.HTTPError'
|
||||
"500":
|
||||
description: Internal Server Error
|
||||
schema:
|
||||
$ref: '#/definitions/controllers.HTTPError'
|
||||
summary: HandleTestValidator的示例
|
||||
tags:
|
||||
- snow
|
||||
securityDefinitions:
|
||||
ApiKeyAuth:
|
||||
in: header
|
||||
name: Authorization
|
||||
type: apiKey
|
||||
BasicAuth:
|
||||
type: basic
|
||||
OAuth2AccessCode:
|
||||
authorizationUrl: https://example.com/oauth/authorize
|
||||
flow: accessCode
|
||||
scopes:
|
||||
admin: Grants read and write access to administrative information
|
||||
tokenUrl: https://example.com/oauth/token
|
||||
type: oauth2
|
||||
OAuth2Application:
|
||||
flow: application
|
||||
scopes:
|
||||
admin: Grants read and write access to administrative information
|
||||
write: Grants write access
|
||||
tokenUrl: https://example.com/oauth/token
|
||||
type: oauth2
|
||||
OAuth2Implicit:
|
||||
authorizationUrl: https://example.com/oauth/authorize
|
||||
flow: implicit
|
||||
scopes:
|
||||
admin: Grants read and write access to administrative information
|
||||
write: Grants write access
|
||||
type: oauth2
|
||||
OAuth2Password:
|
||||
flow: password
|
||||
scopes:
|
||||
admin: Grants read and write access to administrative information
|
||||
read: Grants read access
|
||||
write: Grants write access
|
||||
tokenUrl: https://example.com/oauth/token
|
||||
type: oauth2
|
||||
swagger: "2.0"
|
|
@ -0,0 +1,34 @@
|
|||
package event
|
||||
|
||||
import "qteam/app/utils"
|
||||
|
||||
type EventHandler interface {
|
||||
Handle(param interface{})
|
||||
}
|
||||
|
||||
type EventManagerFactory struct {
|
||||
handers map[string][]EventHandler
|
||||
}
|
||||
|
||||
func (this *EventManagerFactory) Register(eventName string, handler EventHandler) {
|
||||
if this.handers == nil {
|
||||
this.handers = make(map[string][]EventHandler)
|
||||
}
|
||||
this.handers[eventName] = append(this.handers[eventName], handler)
|
||||
|
||||
}
|
||||
|
||||
func (this *EventManagerFactory) TrigerEvent(eventName string, param interface{}) {
|
||||
defer func() {
|
||||
err := recover()
|
||||
if err != nil {
|
||||
utils.Log(nil, "event err", err)
|
||||
}
|
||||
|
||||
}()
|
||||
|
||||
for _, v := range this.handers[eventName] {
|
||||
v.Handle(param)
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
package event
|
||||
|
||||
import (
|
||||
"qteam/app/constants/common"
|
||||
"qteam/event/observers"
|
||||
)
|
||||
|
||||
/**
|
||||
*事件触发
|
||||
event.EventManger.TrigerEvent(common.Event_USER_LOG_IN, param)
|
||||
*/
|
||||
|
||||
var EventManger *EventManagerFactory
|
||||
|
||||
func init() {
|
||||
EventManger = new(EventManagerFactory)
|
||||
EventManger.Register(common.Event_USER_LOG_IN, &observers.ScoreLogin{})
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
package observers
|
||||
|
||||
type ScoreLogin struct {
|
||||
}
|
||||
|
||||
func (this *ScoreLogin) Handle(param interface{}) {
|
||||
|
||||
}
|
|
@ -0,0 +1,116 @@
|
|||
module qteam
|
||||
|
||||
go 1.21
|
||||
|
||||
require (
|
||||
gitee.com/chengdu_blue_brothers/openapi-go-sdk v0.0.2
|
||||
github.com/BurntSushi/toml v0.4.1
|
||||
github.com/Shopify/sarama v1.19.0
|
||||
github.com/aliyun/aliyun-oss-go-sdk v3.0.2+incompatible
|
||||
github.com/forgoer/openssl v1.6.0
|
||||
github.com/gin-gonic/gin v1.7.7
|
||||
github.com/go-playground/locales v0.14.0
|
||||
github.com/go-playground/universal-translator v0.18.0
|
||||
github.com/go-sql-driver/mysql v1.6.0
|
||||
github.com/golang-jwt/jwt/v4 v4.5.0
|
||||
github.com/nacos-group/nacos-sdk-go/v2 v2.2.5
|
||||
github.com/nats-io/nats.go v1.9.1
|
||||
github.com/openzipkin/zipkin-go v0.2.2
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/prometheus/client_golang v1.12.2
|
||||
github.com/qit-team/snow-core v0.1.28
|
||||
github.com/qit-team/work v0.3.11
|
||||
github.com/robfig/cron v1.2.0
|
||||
github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271
|
||||
github.com/swaggo/gin-swagger v1.3.3
|
||||
github.com/swaggo/swag v1.7.9
|
||||
github.com/tjfoc/gmsm v1.4.1
|
||||
github.com/valyala/fasthttp v1.31.0
|
||||
google.golang.org/grpc v1.56.3
|
||||
google.golang.org/protobuf v1.30.0
|
||||
gopkg.in/go-playground/validator.v9 v9.31.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/KyleBanks/depth v1.2.1 // indirect
|
||||
github.com/PuerkitoBio/purell v1.1.1 // indirect
|
||||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect
|
||||
github.com/alibabacloud-go/debug v0.0.0-20190504072949-9472017b5c68 // indirect
|
||||
github.com/alibabacloud-go/tea v1.1.17 // indirect
|
||||
github.com/alibabacloud-go/tea-utils v1.4.4 // indirect
|
||||
github.com/aliyun/alibaba-cloud-sdk-go v1.61.1800 // indirect
|
||||
github.com/aliyun/alibabacloud-dkms-gcs-go-sdk v0.2.2 // indirect
|
||||
github.com/aliyun/alibabacloud-dkms-transfer-go-sdk v0.1.7 // indirect
|
||||
github.com/andybalholm/brotli v1.0.4 // indirect
|
||||
github.com/apache/rocketmq-client-go/v2 v2.1.0 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/buger/jsonparser v1.1.1 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||
github.com/eapache/go-resiliency v1.1.0 // indirect
|
||||
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21 // indirect
|
||||
github.com/eapache/queue v1.1.0 // indirect
|
||||
github.com/emirpasic/gods v1.12.0 // indirect
|
||||
github.com/gin-contrib/sse v0.1.0 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.19.5 // indirect
|
||||
github.com/go-openapi/jsonreference v0.19.6 // indirect
|
||||
github.com/go-openapi/spec v0.20.4 // indirect
|
||||
github.com/go-openapi/swag v0.19.15 // indirect
|
||||
github.com/go-playground/validator/v10 v10.9.0 // indirect
|
||||
github.com/go-redis/redis/v8 v8.11.4 // indirect
|
||||
github.com/goccy/go-json v0.8.1 // indirect
|
||||
github.com/golang/mock v1.6.0 // indirect
|
||||
github.com/golang/protobuf v1.5.3 // indirect
|
||||
github.com/golang/snappy v0.0.4 // indirect
|
||||
github.com/google/uuid v1.3.0 // indirect
|
||||
github.com/hetiansu5/accesslog v1.0.0 // indirect
|
||||
github.com/hetiansu5/cores v1.0.0 // indirect
|
||||
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/klauspost/compress v1.13.6 // indirect
|
||||
github.com/leodido/go-urn v1.2.1 // indirect
|
||||
github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible // indirect
|
||||
github.com/lestrrat-go/strftime v1.0.5 // indirect
|
||||
github.com/mailru/easyjson v0.7.7 // indirect
|
||||
github.com/mattn/go-isatty v0.0.14 // indirect
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/nats-io/jwt v0.3.2 // indirect
|
||||
github.com/nats-io/nkeys v0.1.3 // indirect
|
||||
github.com/nats-io/nuid v1.0.1 // indirect
|
||||
github.com/pierrec/lz4 v2.0.5+incompatible // indirect
|
||||
github.com/prometheus/client_model v0.2.0 // indirect
|
||||
github.com/prometheus/common v0.32.1 // indirect
|
||||
github.com/prometheus/procfs v0.7.3 // indirect
|
||||
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a // indirect
|
||||
github.com/rifflock/lfshook v0.0.0-20180920164130-b9218ef580f5 // indirect
|
||||
github.com/sirupsen/logrus v1.8.1 // indirect
|
||||
github.com/syndtr/goleveldb v1.0.0 // indirect
|
||||
github.com/tidwall/gjson v1.12.1 // indirect
|
||||
github.com/tidwall/match v1.1.1 // indirect
|
||||
github.com/tidwall/pretty v1.2.0 // indirect
|
||||
github.com/ugorji/go/codec v1.2.6 // indirect
|
||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||
github.com/valyala/fasttemplate v1.2.1 // indirect
|
||||
go.uber.org/atomic v1.9.0 // indirect
|
||||
go.uber.org/multierr v1.6.0 // indirect
|
||||
go.uber.org/zap v1.21.0 // indirect
|
||||
golang.org/x/crypto v0.17.0 // indirect
|
||||
golang.org/x/net v0.17.0 // indirect
|
||||
golang.org/x/sync v0.1.0 // indirect
|
||||
golang.org/x/sys v0.15.0 // indirect
|
||||
golang.org/x/text v0.14.0 // indirect
|
||||
golang.org/x/time v0.1.0 // indirect
|
||||
golang.org/x/tools v0.6.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect
|
||||
gopkg.in/ini.v1 v1.66.2 // indirect
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
stathat.com/c/consistent v1.0.0 // indirect
|
||||
xorm.io/builder v0.3.9 // indirect
|
||||
xorm.io/core v0.7.3 // indirect
|
||||
xorm.io/xorm v1.2.5 // indirect
|
||||
)
|
|
@ -0,0 +1,2 @@
|
|||
*
|
||||
!.gitignore
|
|
@ -0,0 +1,128 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"qteam/app/console"
|
||||
"qteam/app/http/routes"
|
||||
"qteam/app/jobs"
|
||||
"qteam/bootstrap"
|
||||
"qteam/config"
|
||||
_ "qteam/docs"
|
||||
"qteam/rpc"
|
||||
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
_ "github.com/qit-team/snow-core/cache/rediscache"
|
||||
"github.com/qit-team/snow-core/kernel/server"
|
||||
_ "github.com/qit-team/snow-core/queue/redisqueue"
|
||||
)
|
||||
|
||||
// @title Swagger Example API
|
||||
// @version 1.0
|
||||
// @description This is a sample server celler server.
|
||||
// @termsOfService http://swagger.io/terms/
|
||||
|
||||
// @contact.name API Support
|
||||
// @contact.url http://www.swagger.io/support
|
||||
// @contact.email support@swagger.io
|
||||
|
||||
// @license.name Apache 2.0
|
||||
// @license.url http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
|
||||
// @host localhost:8080
|
||||
// @BasePath /
|
||||
|
||||
// @securityDefinitions.basic BasicAuth
|
||||
|
||||
// @securityDefinitions.apikey ApiKeyAuth
|
||||
// @in header
|
||||
// @name Authorization
|
||||
|
||||
// @securitydefinitions.oauth2.application OAuth2Application
|
||||
// @tokenUrl https://example.com/oauth/token
|
||||
// @scope.write Grants write access
|
||||
// @scope.admin Grants read and write access to administrative information
|
||||
|
||||
// @securitydefinitions.oauth2.implicit OAuth2Implicit
|
||||
// @authorizationUrl https://example.com/oauth/authorize
|
||||
// @scope.write Grants write access
|
||||
// @scope.admin Grants read and write access to administrative information
|
||||
|
||||
// @securitydefinitions.oauth2.password OAuth2Password
|
||||
// @tokenUrl https://example.com/oauth/token
|
||||
// @scope.read Grants read access
|
||||
// @scope.write Grants write access
|
||||
// @scope.admin Grants read and write access to administrative information
|
||||
|
||||
// @securitydefinitions.oauth2.accessCode OAuth2AccessCode
|
||||
// @tokenUrl https://example.com/oauth/token
|
||||
// @authorizationUrl https://example.com/oauth/authorize
|
||||
// @scope.admin Grants read and write access to administrative information
|
||||
func main() {
|
||||
//解析启动命令
|
||||
opts := config.GetOptions()
|
||||
if opts.ShowVersion {
|
||||
fmt.Printf("%s\ncommit %s\nbuilt on %s\n", server.Version, server.BuildCommit, server.BuildDate)
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
handleCmd(opts)
|
||||
|
||||
err := startServer(opts)
|
||||
if err != nil {
|
||||
fmt.Printf("server start error, %s\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
// 执行(status|stop|restart)命令
|
||||
func handleCmd(opts *config.Options) {
|
||||
if opts.Cmd != "" {
|
||||
pidFile := opts.GenPidFile()
|
||||
err := server.HandleUserCmd(opts.Cmd, pidFile)
|
||||
if err != nil {
|
||||
fmt.Printf("Handle user command(%s) error, %s\n", opts.Cmd, err)
|
||||
} else {
|
||||
fmt.Printf("Handle user command(%s) succ \n ", opts.Cmd)
|
||||
}
|
||||
os.Exit(0)
|
||||
}
|
||||
}
|
||||
|
||||
func startServer(opts *config.Options) (err error) {
|
||||
//加载配置
|
||||
conf, err := config.Load(opts.ConfFile)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
//引导程序
|
||||
err = bootstrap.Bootstrap(conf)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
pidFile := opts.GenPidFile()
|
||||
//根据启动命令行参数,决定启动哪种服务模式
|
||||
switch opts.App {
|
||||
case "api":
|
||||
err = server.StartHttp(pidFile, conf.Api, routes.RegisterRoute)
|
||||
case "cron":
|
||||
err = server.StartConsole(pidFile, console.RegisterSchedule)
|
||||
case "job":
|
||||
err = server.StartJob(pidFile, jobs.RegisterWorker)
|
||||
case "command":
|
||||
err = server.ExecuteCommand(opts.Command, console.RegisterCommand)
|
||||
case "rpc":
|
||||
err = rpc.StartRpc()
|
||||
case "admin":
|
||||
err = server.StartHttp(pidFile, conf.Admin, routes.RegisterAdminRoute)
|
||||
case "all":
|
||||
go server.StartHttp(pidFile, conf.Api, routes.RegisterRoute)
|
||||
go server.StartHttp(pidFile, conf.Admin, routes.RegisterAdminRoute)
|
||||
select {}
|
||||
default:
|
||||
err = errors.New("no server start")
|
||||
}
|
||||
return
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
package rpc
|
||||
|
||||
import (
|
||||
"github.com/qit-team/snow-core/kernel/server"
|
||||
__ "qteam/rpc/user"
|
||||
)
|
||||
|
||||
func StartRpc() error {
|
||||
go (&__.UserServer{}).StartServer()
|
||||
|
||||
//等待停止信号
|
||||
server.WaitStop()
|
||||
|
||||
return nil
|
||||
|
||||
}
|
|
@ -0,0 +1,290 @@
|
|||
// 文件位置:pbfile/studnet.proto
|
||||
// 指定使用的语法格式,根据自己下载的protoc的版本选择
|
||||
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.33.0
|
||||
// protoc v3.19.5
|
||||
// source: user/user.proto
|
||||
|
||||
// 指定包名
|
||||
|
||||
package __
|
||||
|
||||
import (
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
reflect "reflect"
|
||||
sync "sync"
|
||||
)
|
||||
|
||||
const (
|
||||
// Verify that this generated code is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||
)
|
||||
|
||||
type User struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
|
||||
Age int32 `protobuf:"varint,2,opt,name=age,proto3" json:"age,omitempty"`
|
||||
}
|
||||
|
||||
func (x *User) Reset() {
|
||||
*x = User{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_user_user_proto_msgTypes[0]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *User) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*User) ProtoMessage() {}
|
||||
|
||||
func (x *User) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_user_user_proto_msgTypes[0]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use User.ProtoReflect.Descriptor instead.
|
||||
func (*User) Descriptor() ([]byte, []int) {
|
||||
return file_user_user_proto_rawDescGZIP(), []int{0}
|
||||
}
|
||||
|
||||
func (x *User) GetName() string {
|
||||
if x != nil {
|
||||
return x.Name
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *User) GetAge() int32 {
|
||||
if x != nil {
|
||||
return x.Age
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// 定义请求体结构
|
||||
type UserRequest struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Number int32 `protobuf:"varint,1,opt,name=number,proto3" json:"number,omitempty"` //请求中传入学生的学号,1表示占位符
|
||||
}
|
||||
|
||||
func (x *UserRequest) Reset() {
|
||||
*x = UserRequest{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_user_user_proto_msgTypes[1]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *UserRequest) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*UserRequest) ProtoMessage() {}
|
||||
|
||||
func (x *UserRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_user_user_proto_msgTypes[1]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use UserRequest.ProtoReflect.Descriptor instead.
|
||||
func (*UserRequest) Descriptor() ([]byte, []int) {
|
||||
return file_user_user_proto_rawDescGZIP(), []int{1}
|
||||
}
|
||||
|
||||
func (x *UserRequest) GetNumber() int32 {
|
||||
if x != nil {
|
||||
return x.Number
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// 定义响应体结构
|
||||
type UserResponse struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Student *User `protobuf:"bytes,1,opt,name=student,proto3" json:"student,omitempty"`
|
||||
}
|
||||
|
||||
func (x *UserResponse) Reset() {
|
||||
*x = UserResponse{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_user_user_proto_msgTypes[2]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *UserResponse) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*UserResponse) ProtoMessage() {}
|
||||
|
||||
func (x *UserResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_user_user_proto_msgTypes[2]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use UserResponse.ProtoReflect.Descriptor instead.
|
||||
func (*UserResponse) Descriptor() ([]byte, []int) {
|
||||
return file_user_user_proto_rawDescGZIP(), []int{2}
|
||||
}
|
||||
|
||||
func (x *UserResponse) GetStudent() *User {
|
||||
if x != nil {
|
||||
return x.Student
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var File_user_user_proto protoreflect.FileDescriptor
|
||||
|
||||
var file_user_user_proto_rawDesc = []byte{
|
||||
0x0a, 0x0f, 0x75, 0x73, 0x65, 0x72, 0x2f, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74,
|
||||
0x6f, 0x12, 0x04, 0x75, 0x73, 0x65, 0x72, 0x22, 0x2c, 0x0a, 0x04, 0x55, 0x73, 0x65, 0x72, 0x12,
|
||||
0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e,
|
||||
0x61, 0x6d, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05,
|
||||
0x52, 0x03, 0x61, 0x67, 0x65, 0x22, 0x25, 0x0a, 0x0b, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71,
|
||||
0x75, 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x01,
|
||||
0x20, 0x01, 0x28, 0x05, 0x52, 0x06, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x22, 0x34, 0x0a, 0x0c,
|
||||
0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x24, 0x0a, 0x07,
|
||||
0x73, 0x74, 0x75, 0x64, 0x65, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0a, 0x2e,
|
||||
0x75, 0x73, 0x65, 0x72, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x07, 0x73, 0x74, 0x75, 0x64, 0x65,
|
||||
0x6e, 0x74, 0x32, 0x4a, 0x0a, 0x0b, 0x55, 0x73, 0x65, 0x72, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63,
|
||||
0x65, 0x12, 0x3b, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x42, 0x79, 0x53, 0x74,
|
||||
0x75, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x11, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x55,
|
||||
0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x12, 0x2e, 0x75, 0x73, 0x65,
|
||||
0x72, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x04,
|
||||
0x5a, 0x02, 0x2e, 0x2f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
file_user_user_proto_rawDescOnce sync.Once
|
||||
file_user_user_proto_rawDescData = file_user_user_proto_rawDesc
|
||||
)
|
||||
|
||||
func file_user_user_proto_rawDescGZIP() []byte {
|
||||
file_user_user_proto_rawDescOnce.Do(func() {
|
||||
file_user_user_proto_rawDescData = protoimpl.X.CompressGZIP(file_user_user_proto_rawDescData)
|
||||
})
|
||||
return file_user_user_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_user_user_proto_msgTypes = make([]protoimpl.MessageInfo, 3)
|
||||
var file_user_user_proto_goTypes = []interface{}{
|
||||
(*User)(nil), // 0: user.User
|
||||
(*UserRequest)(nil), // 1: user.UserRequest
|
||||
(*UserResponse)(nil), // 2: user.UserResponse
|
||||
}
|
||||
var file_user_user_proto_depIdxs = []int32{
|
||||
0, // 0: user.UserResponse.student:type_name -> user.User
|
||||
1, // 1: user.UserService.GetUserByStuNumber:input_type -> user.UserRequest
|
||||
2, // 2: user.UserService.GetUserByStuNumber:output_type -> user.UserResponse
|
||||
2, // [2:3] is the sub-list for method output_type
|
||||
1, // [1:2] is the sub-list for method input_type
|
||||
1, // [1:1] is the sub-list for extension type_name
|
||||
1, // [1:1] is the sub-list for extension extendee
|
||||
0, // [0:1] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_user_user_proto_init() }
|
||||
func file_user_user_proto_init() {
|
||||
if File_user_user_proto != nil {
|
||||
return
|
||||
}
|
||||
if !protoimpl.UnsafeEnabled {
|
||||
file_user_user_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*User); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_user_user_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*UserRequest); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_user_user_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*UserResponse); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: file_user_user_proto_rawDesc,
|
||||
NumEnums: 0,
|
||||
NumMessages: 3,
|
||||
NumExtensions: 0,
|
||||
NumServices: 1,
|
||||
},
|
||||
GoTypes: file_user_user_proto_goTypes,
|
||||
DependencyIndexes: file_user_user_proto_depIdxs,
|
||||
MessageInfos: file_user_user_proto_msgTypes,
|
||||
}.Build()
|
||||
File_user_user_proto = out.File
|
||||
file_user_user_proto_rawDesc = nil
|
||||
file_user_user_proto_goTypes = nil
|
||||
file_user_user_proto_depIdxs = nil
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
// 文件位置:pbfile/studnet.proto
|
||||
// 指定使用的语法格式,根据自己下载的protoc的版本选择
|
||||
syntax = "proto3";
|
||||
// 指定代码生成在哪个位置
|
||||
option go_package = "./";
|
||||
// 指定包名
|
||||
package user;
|
||||
|
||||
message User{
|
||||
string name = 1;
|
||||
int32 age = 2;
|
||||
}
|
||||
// 定义请求体结构
|
||||
message UserRequest{
|
||||
int32 number = 1; //请求中传入学生的学号,1表示占位符
|
||||
}
|
||||
|
||||
// 定义响应体结构
|
||||
message UserResponse{
|
||||
User student = 1;
|
||||
}
|
||||
|
||||
//定义一个服务,根据学生的学号查询学生的信息
|
||||
service UserService{
|
||||
rpc GetUserByStuNumber(UserRequest) returns (UserResponse);
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
package __
|
||||
|
||||
import (
|
||||
"context"
|
||||
"google.golang.org/grpc"
|
||||
"log"
|
||||
"net"
|
||||
"qteam/app/utils"
|
||||
"qteam/config"
|
||||
)
|
||||
|
||||
// 服务定义
|
||||
type UserServer struct{}
|
||||
|
||||
func (s *UserServer) GetUserByStuNumber(ctx context.Context, request *UserRequest) (*UserResponse, error) {
|
||||
return &UserResponse{}, nil
|
||||
}
|
||||
|
||||
func (s *UserServer) mustEmbedUnimplementedUserServiceServer() {
|
||||
//TODO implement me
|
||||
//panic("implement me")
|
||||
}
|
||||
func (s *UserServer) StartServer() *grpc.Server {
|
||||
|
||||
server := grpc.NewServer()
|
||||
|
||||
//注册服务
|
||||
RegisterUserServiceServer(server, &UserServer{})
|
||||
//启动监听程序
|
||||
listener, err := net.Listen("tcp", config.GetConf().Rpc.User)
|
||||
if err != nil {
|
||||
log.Fatal("启动监听失败", err)
|
||||
}
|
||||
|
||||
err = server.Serve(listener)
|
||||
|
||||
if err != nil {
|
||||
log.Fatal("启动服务失败", err)
|
||||
}
|
||||
utils.Log(nil, "服务启动成功", config.GetConf().Rpc.User)
|
||||
return server
|
||||
}
|
|
@ -0,0 +1,105 @@
|
|||
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
||||
// versions:
|
||||
// - protoc-gen-go-grpc v1.2.0
|
||||
// - protoc v3.19.5
|
||||
// source: user/user.proto
|
||||
|
||||
package __
|
||||
|
||||
import (
|
||||
context "context"
|
||||
grpc "google.golang.org/grpc"
|
||||
codes "google.golang.org/grpc/codes"
|
||||
status "google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the grpc package it is being compiled against.
|
||||
// Requires gRPC-Go v1.32.0 or later.
|
||||
const _ = grpc.SupportPackageIsVersion7
|
||||
|
||||
// UserServiceClient is the client API for UserService service.
|
||||
//
|
||||
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
|
||||
type UserServiceClient interface {
|
||||
GetUserByStuNumber(ctx context.Context, in *UserRequest, opts ...grpc.CallOption) (*UserResponse, error)
|
||||
}
|
||||
|
||||
type userServiceClient struct {
|
||||
cc grpc.ClientConnInterface
|
||||
}
|
||||
|
||||
func NewUserServiceClient(cc grpc.ClientConnInterface) UserServiceClient {
|
||||
return &userServiceClient{cc}
|
||||
}
|
||||
|
||||
func (c *userServiceClient) GetUserByStuNumber(ctx context.Context, in *UserRequest, opts ...grpc.CallOption) (*UserResponse, error) {
|
||||
out := new(UserResponse)
|
||||
err := c.cc.Invoke(ctx, "/user.UserService/GetUserByStuNumber", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// UserServiceServer is the server API for UserService service.
|
||||
// All implementations must embed UnimplementedUserServiceServer
|
||||
// for forward compatibility
|
||||
type UserServiceServer interface {
|
||||
GetUserByStuNumber(context.Context, *UserRequest) (*UserResponse, error)
|
||||
mustEmbedUnimplementedUserServiceServer()
|
||||
}
|
||||
|
||||
// UnimplementedUserServiceServer must be embedded to have forward compatible implementations.
|
||||
type UnimplementedUserServiceServer struct {
|
||||
}
|
||||
|
||||
func (UnimplementedUserServiceServer) GetUserByStuNumber(context.Context, *UserRequest) (*UserResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method GetUserByStuNumber not implemented")
|
||||
}
|
||||
func (UnimplementedUserServiceServer) mustEmbedUnimplementedUserServiceServer() {}
|
||||
|
||||
// UnsafeUserServiceServer may be embedded to opt out of forward compatibility for this service.
|
||||
// Use of this interface is not recommended, as added methods to UserServiceServer will
|
||||
// result in compilation errors.
|
||||
type UnsafeUserServiceServer interface {
|
||||
mustEmbedUnimplementedUserServiceServer()
|
||||
}
|
||||
|
||||
func RegisterUserServiceServer(s grpc.ServiceRegistrar, srv UserServiceServer) {
|
||||
s.RegisterService(&UserService_ServiceDesc, srv)
|
||||
}
|
||||
|
||||
func _UserService_GetUserByStuNumber_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(UserRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(UserServiceServer).GetUserByStuNumber(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/user.UserService/GetUserByStuNumber",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(UserServiceServer).GetUserByStuNumber(ctx, req.(*UserRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
// UserService_ServiceDesc is the grpc.ServiceDesc for UserService service.
|
||||
// It's only intended for direct use with grpc.RegisterService,
|
||||
// and not to be introspected or modified (even as a copy)
|
||||
var UserService_ServiceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "user.UserService",
|
||||
HandlerType: (*UserServiceServer)(nil),
|
||||
Methods: []grpc.MethodDesc{
|
||||
{
|
||||
MethodName: "GetUserByStuNumber",
|
||||
Handler: _UserService_GetUserByStuNumber_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{},
|
||||
Metadata: "user/user.proto",
|
||||
}
|
Loading…
Reference in New Issue