Compare commits

..

60 Commits

Author SHA1 Message Date
suguo 65d34e17f2 gin.DefaultWriter = io.Discard 2026-01-05 11:21:48 +08:00
suguo e7ae12b939 postgres 2025-10-21 12:45:10 +08:00
suguo 8ed0a4c936 添加注释中的dockerfile配置 2025-08-13 15:56:32 +08:00
suguo.yao 3aecbceef8 gin path remove 2024-08-20 16:04:01 +08:00
suguo.yao d2fae8858e add gin2 2024-08-20 15:45:03 +08:00
suguo.yao de0fde98c0 readme.md 2024-04-22 22:16:19 +08:00
suguo.yao a22a83f93c influx for docker 2023-12-24 10:02:30 +08:00
suguo.yao 13cd706505 mongo docker版 2023-12-15 13:52:21 +08:00
suguo.yao 3ee707e60c 增加mysql redis的docker版 2023-12-13 16:47:46 +08:00
suguo.yao 8de9252ae5 redis配置默认值 2023-10-31 14:03:34 +08:00
suguo.yao 9b7f627b0f bug fix: setlevel重复 2023-10-31 13:57:57 +08:00
suguo.yao 16c67e0b44 logrus支持 2023-10-30 14:55:49 +08:00
suguo.yao d705073188 优化 2023-09-13 17:27:25 +08:00
suguo.yao ae30e2c0de gin默认端口为8080 2023-05-15 13:50:50 +08:00
suguo.yao ccdcfc4ebc mosquitto改mqtt 2023-02-28 11:23:15 +08:00
suguo.yao 1ba9b3ab0a 表初始化位置调整至mysql 2022-12-19 11:28:02 +08:00
suguo.yao 747da175d7 说明单位 2022-12-17 15:19:42 +08:00
suguo.yao a58cb65486 bug fix: config items 2022-12-16 22:01:32 +08:00
suguo.yao de5704b90e gin支持ssl 2022-10-20 13:29:58 +08:00
suguo.yao a589b8d32f exceptionless 列新 2022-09-29 13:49:45 +08:00
suguo.yao 0c5879b6d3 完善exceptionless 2022-09-16 10:10:19 +08:00
suguo.yao 25515fd266 优化exceptionless 2022-08-14 14:22:00 +08:00
suguo.yao d473317c7c 异常不退出 2022-08-13 22:16:47 +08:00
suguo.yao 1310a49824 无用文件清除 2022-08-13 22:16:09 +08:00
suguo.yao bff14dcc82 增加exceptionless,支持error原生 2022-08-13 22:04:48 +08:00
suguo.yao 8c955c89ea 支持表单数形式 2022-05-11 20:00:20 +08:00
suguo.yao e856a26dd6 bug fix: leveldb 2022-04-14 22:18:23 +08:00
suguo.yao 1de0cb8203 增加leveldb支持 2022-04-14 22:12:57 +08:00
suguo.yao 99c261ca91 redis增加集合操作功能 2022-03-19 10:52:15 +08:00
suguo.yao a490fea952 增加gin拦截器 2022-01-13 20:27:28 +08:00
suguo.yao 8fddf71e5c bug fix: setbytes 2022-01-12 14:29:06 +08:00
suguo.yao f998db645e 更新redis,支持byte更新 2022-01-12 13:10:21 +08:00
suguo.yao 830b635af7 mosquitto测试通过 2022-01-04 16:32:40 +08:00
suguo.yao fa8dded9f5 完善influx 2021-12-17 10:51:57 +08:00
suguo.yao ea9b6384af 增加influx标准代码 2021-12-16 20:56:34 +08:00
suguo.yao 1ceea92959 优化gin 2021-12-03 14:00:37 +08:00
suguo.yao 054cbb910e 增加gin组件 2021-12-03 11:36:40 +08:00
suguo.yao 5903333242 增加mongo支持及相关test 2021-11-14 19:07:38 +08:00
suguo.yao 6903a40cc2 修复register误删 2021-10-08 15:05:46 +08:00
suguo.yao aed016e7be 调整consul取微服务支持tag 2021-10-08 13:24:07 +08:00
suguo.yao a7f76f00a9 更新consul使用tag 2021-10-07 14:27:03 +08:00
suguo.yao 6c14e82c92 大小写调整 2021-10-04 13:57:26 +08:00
suguo.yao a42b6770c9 支持grpc拦截器加入 2021-10-04 13:10:37 +08:00
suguo.yao e386683768 增加RegisterAPI 2021-09-17 16:36:23 +08:00
suguo.yao 44b1fba8c8 bug for sync.Done 2021-09-16 14:03:34 +08:00
suguo.yao bc876f20d6 解决grpc start时异步导致端口获取为0的问题 2021-09-16 13:55:01 +08:00
suguo.yao 14cb71f306 fix consul Init 2021-09-16 10:43:31 +08:00
suguo.yao 666348870e grpc更新 2021-09-16 10:38:37 +08:00
suguo.yao b048a2e1f2 对动态ip port进行获取 2021-09-16 09:40:48 +08:00
suguo.yao 9af8b46812 动态ip port 2021-09-16 09:38:22 +08:00
suguo.yao 553f2391c7 增加微服务必用的健康检查 2021-09-16 09:21:51 +08:00
suguo.yao 1e5ea70247 增加redis 2021-09-15 22:53:03 +08:00
suguo.yao 0603d042c5 支持动态端口 2021-09-15 19:04:51 +08:00
suguo.yao abf81b2cc9 修改consul Init过程 2021-09-15 19:00:38 +08:00
suguo.yao e3f6f58a7f 准备启用exceptionless分布式日志 2021-09-14 16:13:55 +08:00
suguo.yao 1916d879ce 通过grpc 调用的拦截 2021-09-14 08:43:09 +08:00
suguo.yao 6680b593e5 拦截器标准结构定义 2021-09-14 08:39:00 +08:00
suguo.yao 0853025ee2 增加consul及grpc组件 2021-09-13 20:52:49 +08:00
suguo.yao c03c0f06ab 基准测试 2021-09-04 11:03:51 +08:00
suguo.yao a86a576ace 逻辑错误bug 2021-09-03 22:38:47 +08:00
45 changed files with 3058 additions and 18 deletions

1
.gitignore vendored
View File

@ -18,3 +18,4 @@ go.sum
tmp/ tmp/
.vscode/ .vscode/
logs/ logs/
snippet

View File

@ -14,3 +14,5 @@
> 支持连接池dbresolver > 支持连接池dbresolver
* Sqlite * Sqlite
> 支持多级目录创建 > 支持多级目录创建
* Grpc
* Consul

222
consul/consul.go Normal file
View File

@ -0,0 +1,222 @@
package consul
import (
"context"
"errors"
"fmt"
"math/rand"
"time"
consulapi "github.com/hashicorp/consul/api"
"google.golang.org/grpc"
)
var (
_client *consulapi.Client
conf *Config
)
type Config struct {
Address string
}
//Init 初始化consul连接
func Init(config *Config) error {
if config == nil {
config = &Config{
Address: "127.0.0.1:8500",
}
}
conf = config
if conf.Address == "" {
conf.Address = "127.0.0.1:8500"
}
return nil
}
func New() (*consulapi.Client, error) {
if _client != nil {
return _client, nil
}
// 创建连接consul服务配置
config := consulapi.DefaultConfig()
config.Address = conf.Address
client, err := consulapi.NewClient(config)
if err != nil {
return nil, err
}
_client = client
return client, nil
}
//Register 注册服务到consul
func Register(name string, addr string, port int, tags ...string) error {
client, err := New()
if err != nil {
return err
}
if client == nil {
return errors.New("consul 实例空")
}
// 创建注册到consul的服务到
registration := new(consulapi.AgentServiceRegistration)
registration.ID = fmt.Sprintf("%s-%s:%d", name, addr, port)
registration.Name = name
registration.Port = port
registration.Tags = tags
registration.Address = addr
// 增加consul健康检查回调函数
check := new(consulapi.AgentServiceCheck)
check.GRPC = fmt.Sprintf("%s:%d", registration.Address, registration.Port)
check.Timeout = "5s"
check.Interval = "5s"
check.DeregisterCriticalServiceAfter = "30s" // 故障检查失败30s后 consul自动将注册服务删除
registration.Check = check
// 注册服务到consul
if err := client.Agent().ServiceRegister(registration); err != nil {
return err
}
return nil
}
//RegisterAPI 注册api服务到consul
func RegisterAPI(name string, addr string, port int, tags ...string) error {
client, err := New()
if err != nil {
return err
}
if client == nil {
return errors.New("consul 实例空")
}
// 创建注册到consul的服务到
registration := new(consulapi.AgentServiceRegistration)
registration.ID = fmt.Sprintf("%s-%s:%d", name, addr, port)
registration.Name = name
registration.Port = port
registration.Tags = tags
registration.Address = addr
// 增加consul健康检查回调函数
check := new(consulapi.AgentServiceCheck)
check.HTTP = fmt.Sprintf("http://%s:%d/health/check", registration.Address, registration.Port)
check.Timeout = "5s"
check.Interval = "5s"
check.DeregisterCriticalServiceAfter = "30s" // 故障检查失败30s后 consul自动将注册服务删除
registration.Check = check
// 注册服务到consul
if err := client.Agent().ServiceRegister(registration); err != nil {
return err
}
return nil
}
//DeRegister 取消consul注册的服务
func DeRegister(name string, addr string, port int) error {
client, err := New()
if err != nil {
return err
}
if client == nil {
return errors.New("consul 实例空")
}
client.Agent().ServiceDeregister(fmt.Sprintf("%s-%s:%d", name, addr, port))
return nil
}
//FindNode 查找节点
func FindNode(servicename, tag string) (*consulapi.AgentService, error) {
client, err := New()
if err != nil {
return nil, err
}
if client == nil {
return nil, errors.New("consul 实例空")
}
services, _, err := client.Health().Service(servicename, tag, true, nil)
if err != nil {
return nil, err
}
l := len(services)
if l == 0 {
return nil, nil
}
if l == 1 {
return services[0].Service, nil
}
r := rand.New(rand.NewSource(time.Now().UnixNano()))
return services[r.Intn(l)%l].Service, nil
}
//FindService 从consul中发现服务并返回grpc连接实例
func FindService(servicename, tag string) (*grpc.ClientConn, error) {
node, err := FindNode(servicename, tag) //无tag视为grpc服务
if err != nil {
return nil, err
}
if node == nil {
return nil, errors.New("微服务" + servicename + "不可用,稍后再试!")
}
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
conn, err := grpc.DialContext(ctx, fmt.Sprintf("%s:%d", node.Address, node.Port), grpc.WithBlock(), grpc.WithInsecure(), grpc.WithBalancerName("round_robin"))
if err != nil {
return nil, err
}
return conn, nil
}
//CheckHeath 健康检查
func CheckHeath(serviceid string) error {
client, err := New()
if err != nil {
return err
}
if client == nil {
return errors.New("consul 实例空")
}
// 健康检查
// a, b, _ := client.Agent().AgentHealthServiceByID(serviceid)
return nil
}
//KVPut test
func KVPut(key string, values *[]byte, flags uint64) (*consulapi.WriteMeta, error) {
client, err := New()
if err != nil {
return nil, err
}
if client == nil {
return nil, errors.New("consul 实例空")
}
return client.KV().Put(&consulapi.KVPair{Key: key, Flags: flags, Value: *values}, nil)
}
//KVGet 获取值
func KVGet(key string, flags uint64) (*[]byte, error) {
client, err := New()
if err != nil {
return nil, err
}
if client == nil {
return nil, errors.New("consul 实例空")
}
// KV get值
data, _, _ := client.KV().Get(key, nil)
if data != nil {
return &data.Value, nil
}
return nil, nil
}

63
exceptionless/api.go Normal file
View File

@ -0,0 +1,63 @@
package exceptionless
import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
)
// Post posts to the Exceptionless Server
func Post(endpoint string, postBody string, authorization string) string {
url := conf.ServerURL + endpoint
var jsonStr = []byte(postBody)
req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonStr))
if err != nil {
return err.Error()
}
req.Header.Set("Authorization", "Bearer "+authorization)
req.Header.Set("Content-Type", "application/json")
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return err.Error()
}
defer resp.Body.Close()
// body, _ := ioutil.ReadAll(resp.Body)
return string(resp.Status)
}
// GET makes api GET requests
func Get(endpoint string, authorization string) map[string]interface{} {
url := conf.ServerURL + endpoint
httpClient := &http.Client{}
req, err := http.NewRequest("GET", url, nil)
if err != nil {
fmt.Println(err)
// return "Error"
}
req.Header.Add("accept", "application/json")
req.Header.Add("Authorization", "Bearer "+authorization)
res, err := httpClient.Do(req)
if err != nil {
fmt.Println(err)
// return "Error"
}
defer res.Body.Close()
body, err := ioutil.ReadAll(res.Body)
if err != nil {
fmt.Println(err)
// return "Error"
}
// resp := string(body)
var result map[string]interface{}
json.Unmarshal([]byte(body), &result)
return result
}

View File

@ -0,0 +1,217 @@
package exceptionless
import (
"encoding/json"
"fmt"
"math/rand"
"time"
"github.com/google/uuid"
)
var config map[string]interface{} = nil
// Config type defines the client configuration structure
type Config struct {
ApiKey string
ServerURL string
ProgramName string
UpdateSettingsWhenIdleInterval int32
}
var conf *Config
func Init(config *Config) {
conf = config
if conf == nil {
return
}
if conf.ApiKey != "" && conf.UpdateSettingsWhenIdleInterval > 0 {
poll()
}
}
// GetConfig returns the project configuration
func GetConfig() map[string]interface{} {
return config
}
// SubmitEvent sends log events to Exceptionless
func SubmitEvent(eventBody string) string {
if conf.ApiKey == "" {
return ""
}
resp := Post("events", eventBody, conf.ApiKey)
return resp
}
func poll() {
r := rand.New(rand.NewSource(99))
c := time.Tick(10 * time.Second)
for _ = range c {
resp := Get("projects/config", conf.ApiKey)
config = resp
jitter := time.Duration(r.Int31n(conf.UpdateSettingsWhenIdleInterval)) * time.Millisecond
time.Sleep(jitter)
}
}
// Event is the main model for events
type Event struct {
EventType string `json:"type"`
Source string `json:"source,omitempty"`
Date string `json:"date,omitempty"`
Tags []string `json:"tags,omitempty"`
Message string `json:"message,omitempty"`
Geo string `json:"geo,omitempty"`
Value uint `json:"value,omitempty"`
Data map[string]interface{} `json:"data,omitempty"`
ReferenceID string `json:"reference_id,omitempty"`
Count uint `json:"count,omitempty"`
}
// GetBaseEvent returns an empty Event struct that can be built into any type of event.
func GetBaseEvent(eventType string, message string, date string) Event {
return Event{
EventType: eventType,
Message: message,
Date: date,
}
}
// AddSource adds a string value source to an event
func AddSource(event Event, source string) Event {
event.Source = source
return event
}
// AddTags adds a string array of tags for the event
func AddTags(event Event, tags []string) Event {
event.Tags = tags
return event
}
// AddGeo adds the lat and long location of the user the event impacted
func AddGeo(event Event, geo string) Event {
event.Geo = geo
return event
}
// AddValue adds an arbitrary number value to the event
func AddValue(event Event, value uint) Event {
event.Value = value
return event
}
// AddReferenceID adds an indentifier to later refer to this event
func AddReferenceID(event Event, referenceID uuid.UUID) Event {
event.ReferenceID = referenceID.String()
return event
}
// AddCount adds a number to help track the number of times the event has occurred
func AddCount(event Event, count uint) Event {
event.Count = count
return event
}
func AddLogLevel(event Event, level string) Event {
var updatedEvent Event
if event.Data != nil {
event.Data["@level"] = level
updatedEvent = event
} else {
data := map[string]interface{}{}
data["@level"] = level
updatedEvent = AddData(event, data)
}
return updatedEvent
}
// AddData adds a string mapping to create a data object of additional values
func AddData(event Event, data map[string]interface{}) Event {
event.Data = data
return event
}
func SubmitAppError(funcName string, proc string, reqid *string, err error) string {
referenceID := uuid.Must(uuid.NewUUID()).String()
if reqid != nil {
referenceID = *reqid
}
errorMap := map[string]interface{}{}
errorMap["function"] = funcName
errorMap["type"] = "error"
errorMap["message"] = err.Error()
errorMap["date"] = time.Now().Format(time.RFC3339)
data := map[string]interface{}{}
data["@simple_error"] = errorMap
var event = Event{
EventType: "error",
Message: fmt.Sprintf("%s - %s", funcName, proc),
Data: data,
ReferenceID: referenceID,
Source: conf.ProgramName,
}
json, err := json.Marshal(event)
if err != nil {
return err.Error()
}
resp := SubmitEvent(string(json))
return resp
}
// SubmitError is a convenience wrapper to quickly build and submit an error
func SubmitError(err error) string {
if conf.UpdateSettingsWhenIdleInterval > 0 {
GetConfig()
}
referenceID := uuid.Must(uuid.NewUUID())
errorMap := map[string]interface{}{}
errorMap["type"] = "error"
errorMap["message"] = err.Error()
errorMap["date"] = time.Now().Format(time.RFC3339)
data := map[string]interface{}{}
data["@simple_error"] = errorMap
var mainEvent = Event{
EventType: "error",
Message: err.Error(),
Data: data,
ReferenceID: referenceID.String(),
}
json, err := json.Marshal(mainEvent)
if err != nil {
fmt.Println(err)
return err.Error()
}
resp := SubmitEvent(string(json))
return resp
}
func SubmitLog(message string, level string) string {
referenceID := uuid.Must(uuid.NewUUID())
if conf.UpdateSettingsWhenIdleInterval > 0 {
config := GetConfig()
fmt.Println(config)
// We are stripping out info accoring to the config settings
// We would also prevent logs of levels below the log level set by the settings
}
var event Event
date := time.Now().Format(time.RFC3339)
event = GetBaseEvent("log", message, date)
event = AddReferenceID(event, referenceID)
data := map[string]interface{}{}
data["@level"] = level
event = AddData(event, data)
json, err := json.Marshal(event)
if err != nil {
fmt.Println(err)
return err.Error()
}
resp := SubmitEvent(string(json))
return resp
}

84
gin/auth-filter.go Normal file
View File

@ -0,0 +1,84 @@
package gin
import (
"encoding/json"
"github.com/gin-gonic/gin"
"myschools.me/suguo/snippet/redis"
)
// 从redis中认证用户
func AuthUser() gin.HandlerFunc {
return func(c *gin.Context) {
token := c.GetHeader("Authorization")
claims := cacheGet(&token)
if claims == nil {
c.Abort()
return
}
c.Set("user", claims)
c.Next()
}
}
// 从redis中获取用户信息最佳实践经验建议把此代码放service层
func cacheGet(token *string) interface{} {
var user interface{}
b, err := redis.GetBytes(token)
if err != nil {
return nil
}
if err := json.Unmarshal(*b, &user); err != nil {
return nil
}
return &user
}
// gin拦截基于微服务的拦截
// func AuthUserBS() gin.HandlerFunc {
// return func(c *gin.Context) {
// token := c.GetHeader("Authorization")
// claims := userAuthWithGrpc(&token)
// if claims == nil {
// yy.RespUnauth(c, "token无效或过期请重新登录", nil, nil)
// c.Abort()
// }
// c.Set("user", claims)
// c.Next()
// }
// }
// func userAuthWithGrpc(token *string) *yy.UserClaims {
// srv, err := consul.FindService("oauth", "v1")
// if err != nil {
// logrus.WithFields(logrus.Fields{
// "func": "userAuthWithGrpc",
// }).Errorf("consul.FindServer: %s", err.Error())
// return nil
// }
// defer srv.Close()
// client := pb.NewCertificationClient(srv)
// resp, err := client.Auth(context.Background(), &pb.CertificationAuthRequest{
// Token: *token,
// })
// if err != nil {
// logrus.WithFields(logrus.Fields{
// "func": "userAuthWithGrpc",
// }).Errorf("client.Auth: %s", err.Error())
// return nil
// }
// if resp.Result == "ok" {
// r := &yy.UserClaims{}
// if err := json.Unmarshal(resp.Data.Value, r); err != nil {
// logrus.WithFields(logrus.Fields{
// "func": "userAuthWithGrpc",
// }).Errorf("json.Unmarshal: %s", err.Error())
// return nil
// }
// return r
// }
// logrus.WithFields(logrus.Fields{
// "func": "userAuthWithGrpc",
// }).Warnln("nil")
// return nil
// }

11
gin/config.go Normal file
View File

@ -0,0 +1,11 @@
package gin
// GIN 配置
type Config struct {
RootPath string
Addr string
Port int
Ssl bool
SslPem string
SslKey string
}

68
gin/gin.go Normal file
View File

@ -0,0 +1,68 @@
package gin
import (
"fmt"
"log"
"net/http"
"strconv"
"time"
"github.com/gin-gonic/gin"
"github.com/unrolled/secure"
)
func Service(conf *Config) {
if conf == nil {
conf = &Config{}
}
if conf.RootPath == "" {
conf.RootPath = "/"
}
if conf.Addr == "" {
conf.Addr = "0.0.0.0"
}
if conf.Port == 0 {
conf.Port = 8080
}
go func() {
router := gin.New()
routerSetup(router, &conf.RootPath)
if conf.Ssl {
router.Use(tlsHandler(conf))
}
s := &http.Server{
Addr: fmt.Sprintf("%s:%d", conf.Addr, conf.Port),
Handler: router,
ReadTimeout: 10 * time.Second,
WriteTimeout: 10 * time.Second,
MaxHeaderBytes: 1 << 20,
}
log.Printf("start service on %s", fmt.Sprintf("%s:%d", conf.Addr, conf.Port))
if conf.Ssl {
log.Fatal(s.ListenAndServeTLS(conf.SslPem, conf.SslKey))
} else {
log.Fatal(s.ListenAndServe())
}
}()
}
func tlsHandler(conf *Config) gin.HandlerFunc {
return func(c *gin.Context) {
secureMiddleware := secure.New(secure.Options{
SSLRedirect: true,
SSLHost: ":" + strconv.Itoa(conf.Port),
})
err := secureMiddleware.Process(c.Writer, c.Request)
// If there was an error, do not continue.
if err != nil {
return
}
c.Next()
}
}

21
gin/router.go Normal file
View File

@ -0,0 +1,21 @@
package gin
import (
"fmt"
"github.com/gin-gonic/gin"
)
// 路由配置
func routerSetup(router *gin.Engine, rootpath *string) {
router.Use(gin.Recovery())
router.GET(`/health/check`)
r := router.Group(fmt.Sprintf("/%s", *rootpath))
{
r.POST(`/register`)
r.GET(`/accountcheck/:accname`)
r.POST(`/login`)
r.POST(`/forgot`)
}
}

37
gin2/filter-gin.go Normal file
View File

@ -0,0 +1,37 @@
package gin
import (
"net/http"
"github.com/gin-gonic/gin"
"github.com/sirupsen/logrus"
)
func filterUser() gin.HandlerFunc {
return func(c *gin.Context) {
token := c.GetHeader("Authorization")
if token == "" {
token = c.Query("Authorization")
}
staff, err := redis.UserGet(&token)
if err != nil {
logrus.WithFields(logrus.Fields{
"func": "authUser",
}).Errorf("redis.UserGet: %s", err.Error())
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{
"data": "无效TOKEN, 请重新登录!",
})
return
}
if staff == nil {
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{
"data": "无效TOKEN, 请重新登录!",
})
return
}
c.Set("user", staff)
c.Next()
}
}

84
gin2/gin.go Normal file
View File

@ -0,0 +1,84 @@
package gin
import (
"fmt"
"io"
"log"
"net/http"
"os"
"strconv"
"time"
"github.com/gin-gonic/gin"
"github.com/sirupsen/logrus"
"github.com/unrolled/secure"
)
var (
rootPath string
addr string
port int
ssl string
sslPem string
sslKey string
)
func Service() {
addr = os.Getenv("GIN_ADDR")
if addr == "" {
addr = "0.0.0.0"
}
port, _ = strconv.Atoi(os.Getenv("GIN_PORT"))
if port == 0 {
port = 8080
}
ssl = os.Getenv("GIN_SSL")
sslPem = os.Getenv("GIN_SSL_PEM")
sslKey = os.Getenv("GIN_SSL_KEY")
go func() {
gin.DefaultWriter = io.Discard
router := gin.New()
routerSetup(router)
if ssl == "true" {
router.Use(tlsHandler())
}
s := &http.Server{
Addr: fmt.Sprintf("%s:%d", addr, port),
Handler: router,
ReadTimeout: 10 * time.Second,
WriteTimeout: 10 * time.Second,
MaxHeaderBytes: 1 << 20,
}
logrus.WithFields(logrus.Fields{
"func": "Service",
}).Infof("start service on %s:%d", addr, port)
if ssl == "true" {
log.Fatal(s.ListenAndServeTLS(sslPem, sslKey))
} else {
log.Fatal(s.ListenAndServe())
}
}()
}
func tlsHandler() gin.HandlerFunc {
return func(c *gin.Context) {
secureMiddleware := secure.New(secure.Options{
SSLRedirect: true,
SSLHost: ":" + strconv.Itoa(port),
})
err := secureMiddleware.Process(c.Writer, c.Request)
// If there was an error, do not continue.
if err != nil {
return
}
c.Next()
}
}

14
gin2/router-gin.go Normal file
View File

@ -0,0 +1,14 @@
package gin
import (
"github.com/gin-gonic/gin"
)
// 路由配置
func routerSetup(router *gin.Engine) {
router.Use(gin.Recovery())
r := router.Group(`/`)
{
}
}

54
go.mod
View File

@ -3,15 +3,67 @@ module myschools.me/suguo/snippet
go 1.17 go 1.17
require ( require (
github.com/eclipse/paho.mqtt.golang v1.2.0
github.com/gin-gonic/gin v1.7.4
github.com/gomodule/redigo v1.8.5
github.com/google/uuid v1.1.2
github.com/hashicorp/consul/api v1.10.1
github.com/influxdata/influxdb v1.9.5
github.com/stretchr/testify v1.7.0
github.com/syndtr/goleveldb v1.0.0
go.mongodb.org/mongo-driver v1.7.4
google.golang.org/grpc v1.41.0
gorm.io/driver/mysql v1.1.2 gorm.io/driver/mysql v1.1.2
gorm.io/driver/sqlite v1.1.4 gorm.io/driver/sqlite v1.1.4
gorm.io/gorm v1.21.14 gorm.io/gorm v1.21.15
gorm.io/plugin/dbresolver v1.1.0 gorm.io/plugin/dbresolver v1.1.0
) )
require ( require (
github.com/armon/go-metrics v0.3.3 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/fatih/color v1.9.0 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect
github.com/go-playground/locales v0.14.0 // indirect
github.com/go-playground/universal-translator v0.18.0 // indirect
github.com/go-playground/validator/v10 v10.9.0 // indirect
github.com/go-sql-driver/mysql v1.6.0 // indirect github.com/go-sql-driver/mysql v1.6.0 // indirect
github.com/go-stack/stack v1.8.0 // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/golang/snappy v0.0.1 // indirect
github.com/google/go-cmp v0.5.6 // indirect
github.com/hashicorp/go-cleanhttp v0.5.1 // indirect
github.com/hashicorp/go-hclog v0.12.2 // indirect
github.com/hashicorp/go-immutable-radix v1.2.0 // indirect
github.com/hashicorp/go-rootcerts v1.0.2 // indirect
github.com/hashicorp/golang-lru v0.5.4 // indirect
github.com/hashicorp/serf v0.9.5 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.2 // indirect github.com/jinzhu/now v1.1.2 // 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/mattn/go-colorable v0.1.8 // indirect
github.com/mattn/go-isatty v0.0.14 // indirect
github.com/mattn/go-sqlite3 v1.14.5 // indirect github.com/mattn/go-sqlite3 v1.14.5 // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/mitchellh/mapstructure v1.4.2 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/ugorji/go/codec v1.2.6 // indirect
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
github.com/xdg-go/scram v1.0.2 // indirect
github.com/xdg-go/stringprep v1.0.2 // indirect
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 // indirect
golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d // indirect
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
golang.org/x/sys v0.0.0-20210910150752-751e447fb3d0 // indirect
golang.org/x/text v0.3.7 // indirect
google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83 // indirect
google.golang.org/protobuf v1.27.1 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
) )

77
grpc/grpc.go Normal file
View File

@ -0,0 +1,77 @@
package grpc
import (
"fmt"
"log"
"net"
"sync"
"google.golang.org/grpc"
"google.golang.org/grpc/reflection"
)
type GrpcService interface {
Register(srv *grpc.Server)
}
type Config struct {
Address string
Port int
AppName string
Opts []grpc.ServerOption
}
var rpc *grpc.Server
var conf *Config
func Init(config *Config) *grpc.Server {
if config == nil {
config = &Config{
Address: "0.0.0.0",
Port: 0,
AppName: "unknown",
Opts: []grpc.ServerOption{grpc.MaxRecvMsgSize(1024 * 1024), grpc.MaxSendMsgSize(1024 * 1024)},
}
}
conf = config
if rpc == nil {
rpc = grpc.NewServer(conf.Opts...)
}
return rpc
}
func IPAddress() string {
return conf.Address
}
func Port() int {
return conf.Port
}
func Start() {
//注册反射 用于grpcurl调试
reflection.Register(rpc)
wg := sync.WaitGroup{}
wg.Add(1)
// grpc服务启动
go func() {
addr, err := net.ResolveTCPAddr("tcp", fmt.Sprintf("%s:%d", conf.Address, conf.Port))
if err != nil {
log.Fatal("net.ResolveTCPAddr", err)
}
lis, err := net.ListenTCP("tcp", addr)
if err != nil {
log.Fatal("fail to open port: ", err)
}
defer lis.Close()
conf.Port = lis.Addr().(*net.TCPAddr).Port
wg.Done()
log.Printf("starting grpc service on %s:%d", addr.IP, conf.Port)
err = rpc.Serve(lis)
if err != nil {
log.Fatal("fail to open microservice: ", err)
}
}()
wg.Wait()
}

10
influx/config.go Normal file
View File

@ -0,0 +1,10 @@
package influx
type Config struct {
Address string
DBName string
Username string
Password string
//单位:秒
Timeout int
}

132
influx/influx.go Normal file
View File

@ -0,0 +1,132 @@
package influx
import (
"context"
"encoding/json"
"errors"
"fmt"
"time"
"github.com/influxdata/influxdb/client/v2"
)
var conf *Config
//配置初始化
func Init(_conf *Config) {
conf = _conf
if conf == nil {
conf = &Config{
Address: "127.0.0.1",
Timeout: 3,
}
}
}
//实例
func New() (client.Client, error) {
cli, err := client.NewHTTPClient(client.HTTPConfig{
Addr: conf.Address,
Username: conf.Username,
Password: conf.Password,
Timeout: time.Duration(conf.Timeout) * time.Second,
})
if err != nil {
return nil, err
}
return cli, nil
}
// 写入例子
func WriteSample() error {
cli, err := New()
if err != nil {
return err
}
defer cli.Close()
bp, _ := client.NewBatchPoints(client.BatchPointsConfig{
Database: func() string {
if conf.DBName == "" {
return "sample"
}
return conf.DBName
}(),
Precision: "s",
})
fields := make(map[string]interface{})
fields["f1"] = "F1"
fields["f2"] = 1
pt, _ := client.NewPoint(
"heartrate",
map[string]string{"platform": "ws", "orgid": "a001"},
fields,
time.Now(),
)
bp.AddPoint(pt)
if err := cli.Write(bp); err != nil {
return err
}
return nil
}
func ReadSample() error {
query, err := New()
if err != nil {
return err
}
defer query.Close()
resp, err := query.QueryCtx(context.Background(), client.Query{
Command: `select * from heartrate where time>'` + time.Now().Add(-12*time.Hour).Format("2006-01-02T15:04:05") + `' group by time(10m) tz('Asia/Shanghai')`,
Database: func() string {
if conf.DBName == "" {
return "sample"
}
return conf.DBName
}(),
})
if err != nil {
return err
}
for _, s := range resp.Results[0].Series[0].Values {
//fmt.Println(reflect.TypeOf(s[1]))
f1 := s[1].(json.Number).String()
f2, _ := s[2].(json.Number).Int64()
platform := s[3].(json.Number).String()
fmt.Println(f1, f2, platform)
}
return nil
}
//创建数据库,并配置默认过期策略(单位:天)
func CreateDB(dbname string, expired uint) error {
if dbname == "" || expired == 0 {
return errors.New("无效参数")
}
db, err := New()
if err != nil {
return err
}
defer db.Close()
createDbSQL := client.NewQuery(fmt.Sprintf("CREATE DATABASE %s", dbname), "", "")
result, err := db.Query(createDbSQL)
if err != nil {
return err
}
if result.Error() != nil {
return result.Error()
}
// 过期策略
createRPSQL := client.NewQuery(fmt.Sprintf("CREATE RETENTION POLICY default ON %s DURATION %dd REPLICATION 1 DEFAULT", dbname, expired), dbname, "")
result, err = db.Query(createRPSQL)
if err != nil {
return err
}
return result.Error()
}

136
influx2/influx.go Normal file
View File

@ -0,0 +1,136 @@
package influx
import (
"context"
"fmt"
"os"
"time"
influxdb2 "github.com/influxdata/influxdb-client-go/v2"
"github.com/influxdata/influxdb-client-go/v2/api"
)
var (
influx_dsn string
influx_token string
influx_org string
)
func init() {
influx_dsn = os.Getenv("INFLUX_DSN")
if influx_dsn == "" {
influx_dsn = "http://influx:8086"
}
influx_token = os.Getenv("INFLUX_TOKEN")
influx_org = os.Getenv("INFLUX_ORG")
}
// 实例
func newInstance() influxdb2.Client {
client := influxdb2.NewClient(influx_dsn, influx_token)
return client
}
// 写入例子
func write(bucket, measurement string, tags map[string]string, fields map[string]interface{}) error {
client := newInstance()
defer client.Close()
writeAPI := client.WriteAPIBlocking(influx_org, bucket)
// Create point using full params constructor
p := influxdb2.NewPoint(measurement,
tags,
fields,
time.Now())
// write point immediately
if err := writeAPI.WritePoint(context.Background(), p); err != nil {
return err
}
// Or write directly line protocol
// line := fmt.Sprintf("stat,unit=temperature avg=%f,max=%f", 23.5, 45.0)
// err = writeAPI.WriteRecord(context.Background(), line)
// if err != nil {
// panic(err)
// }
return nil
}
func writeLine(bucket, line string) error {
client := newInstance()
defer client.Close()
writeAPI := client.WriteAPIBlocking(influx_org, bucket)
// Create point using full params constructor
p := influxdb2.NewPoint(line,
nil,
nil,
time.Now())
// write point immediately
if err := writeAPI.WritePoint(context.Background(), p); err != nil {
return err
}
return nil
}
func Query(sql string) (*api.QueryTableResult, error) {
client := newInstance()
defer client.Close()
queryAPI := client.QueryAPI(influx_org)
result, err := queryAPI.Query(context.Background(), sql)
if err != nil {
return nil, err
}
return result, nil
}
func QuerySimple() {
client := newInstance()
defer client.Close()
// Get query client
queryAPI := client.QueryAPI(influx_org)
// Get parser flux query result
result, err := queryAPI.Query(context.Background(), `from(bucket:"my-bucket")|> range(start: -1h) |> filter(fn: (r) => r._measurement == "stat")`)
if err == nil {
// Use Next() to iterate over query result lines
for result.Next() {
// Observe when there is new grouping key producing new table
if result.TableChanged() {
fmt.Printf("table: %s\n", result.TableMetadata().String())
}
// read result
fmt.Printf("row: %s\n", result.Record().String())
}
if result.Err() != nil {
fmt.Printf("Query error: %s\n", result.Err().Error())
}
} else {
panic(err)
}
// Ensures background processes finishes
client.Close()
}
// 示例,不要进行生产环境调用
func WriteSimple(bucketName string) {
client := newInstance()
writeAPI := client.WriteAPIBlocking(influx_org, bucketName)
// Create point using full params constructor
p := influxdb2.NewPoint("stat",
map[string]string{"unit": "temperature"},
map[string]interface{}{"avg": 24.5, "max": 45.0},
time.Now())
// write point immediately
writeAPI.WritePoint(context.Background(), p)
// Create point using fluent style
p = influxdb2.NewPointWithMeasurement("stat").
AddTag("unit", "temperature").
AddField("avg", 23.2).
AddField("max", 45.0).
SetTime(time.Now())
if err := writeAPI.WritePoint(context.Background(), p); err != nil {
panic(err)
}
// Or write directly line protocol
line := fmt.Sprintf("stat,unit=temperature avg=%f,max=%f", 23.5, 45.0)
if err := writeAPI.WriteRecord(context.Background(), line); err != nil {
panic(err)
}
}

5
leveldb/config.go Normal file
View File

@ -0,0 +1,5 @@
package leveldb
type Config struct {
Path string
}

112
leveldb/leveldb.go Normal file
View File

@ -0,0 +1,112 @@
package leveldb
import (
"errors"
"github.com/syndtr/goleveldb/leveldb"
"github.com/syndtr/goleveldb/leveldb/opt"
)
var _db *leveldb.DB
var config *Config
func Init(conf *Config) {
config = conf
if config == nil {
config = &Config{
Path: "./",
}
}
}
func New() (*leveldb.DB, error) {
if _db != nil {
return _db, nil
}
db, err := leveldb.OpenFile(config.Path, nil)
if err != nil {
return nil, err
}
_db = db
return _db, nil
}
func Close() {
if _db != nil {
_db.Close()
}
_db = nil
}
func Get(key *string) (*[]byte, error) {
db, err := New()
if err != nil {
return nil, err
}
if db == nil {
return nil, errors.New("leveldb is nil")
}
ret, err := db.Has([]byte(*key), &opt.ReadOptions{
DontFillCache: false,
Strict: 0,
})
if err != nil {
return nil, errors.New("key检查异常")
}
if !ret {
return nil, errors.New("key不存在")
}
val, err := db.Get([]byte(*key), nil)
if err != nil {
return nil, err
}
if len(val) == 0 {
return nil, errors.New("key empty")
}
return &val, nil
}
func Put(key *string, payload *[]byte) error {
db, err := New()
if err != nil {
return err
}
if db == nil {
return errors.New("leveldb is busy now")
}
if err := db.Put([]byte(*key), *payload, nil); err != nil {
return err
}
return nil
}
func Delete(key *string) error {
db, err := New()
if err != nil {
return err
}
if db == nil {
return errors.New("leveldb is busy now")
}
ret, err := db.Has([]byte(*key), &opt.ReadOptions{
DontFillCache: false,
Strict: 0,
})
if err != nil {
return err
}
if !ret {
return nil
}
if err := db.Delete([]byte(*key), nil); err != nil {
return err
}
return nil
}

77
logrus/logrus.go Normal file
View File

@ -0,0 +1,77 @@
package main
import (
"fmt"
"os"
"time"
rotatelogs "github.com/lestrrat-go/file-rotatelogs"
"github.com/rifflock/lfshook"
"github.com/sirupsen/logrus"
)
/*
注意当天文件放项目根目录下
env LOGLEVEL=debug
*/
func init() {
//日志初始化
level := os.Getenv("LOGLEVEL")
switch level {
case "debug":
logrus.SetLevel(logrus.DebugLevel)
case "info":
logrus.SetLevel(logrus.InfoLevel)
case "warn":
logrus.SetLevel(logrus.WarnLevel)
case "error":
logrus.SetLevel(logrus.ErrorLevel)
case "fatal":
logrus.SetLevel(logrus.FatalLevel)
default:
logrus.SetLevel(logrus.PanicLevel)
}
logrus.AddHook(newLfsHook(72))
}
func newLfsHook(maxRemainCnt uint) logrus.Hook {
//检查与创建日志文件夹
_, err := os.Stat("logs")
if os.IsNotExist(err) {
os.Mkdir("logs", 0755)
}
logName := fmt.Sprintf(`logs/%s`, "device-wg")
writer, err := rotatelogs.New(
logName+"%Y%m%d.log",
// WithLinkName为最新的日志建立软连接以方便随着找到当前日志文件
rotatelogs.WithLinkName(logName),
// WithRotationTime设置日志分割的时间这里设置为一小时分割一次
rotatelogs.WithRotationTime(24*time.Hour),
// WithMaxAge和WithRotationCount二者只能设置一个
// WithMaxAge设置文件清理前的最长保存时间
// WithRotationCount设置文件清理前最多保存的个数。
//rotatelogs.WithMaxAge(time.Hour*24),
rotatelogs.WithRotationCount(maxRemainCnt),
)
if err != nil {
panic("config local file system for logger error: " + err.Error())
}
lfsHook := lfshook.NewHook(lfshook.WriterMap{
logrus.DebugLevel: writer,
logrus.InfoLevel: writer,
logrus.WarnLevel: writer,
logrus.ErrorLevel: writer,
logrus.FatalLevel: writer,
logrus.PanicLevel: writer,
}, &logrus.TextFormatter{DisableColors: true})
return lfsHook
}

1
logrus/readme.md Normal file
View File

@ -0,0 +1 @@
当前包适合放根目录下使用

View File

@ -43,7 +43,7 @@ func main() {
// get all file link and download it // get all file link and download it
func dl(client http.Client, path string, wg *sync.WaitGroup) { func dl(client http.Client, path string, wg *sync.WaitGroup) {
_, err := os.Stat(path) _, err := os.Stat(path)
if !os.IsNotExist(err) { if os.IsNotExist(err) {
os.MkdirAll(path, 0775) os.MkdirAll(path, 0775)
} }

View File

@ -0,0 +1,90 @@
package middleware
import (
"github.com/gin-gonic/gin"
)
const (
TokenName = "token"
)
//AuthJWT jwt用户认证拦截器
func AuthJWT() gin.HandlerFunc {
return func(c *gin.Context) {
// tokenString := c.GetHeader(TokenName)
// claims := &model.UserClaims{}
// tkn, err := jwt.ParseWithClaims(tokenString, claims, func(t *jwt.Token) (interface{}, error) {
// return []byte(config.JwtKey), nil
// })
// if err != nil {
// if err == jwt.ErrSignatureInvalid {
// zt.RespUnauth(c, fmt.Sprintf("%s无效!", TokenName), nil)
// return
// }
// zt.RespUnauth(c, "请求失败", err)
// return
// }
// if !tkn.Valid {
// zt.RespUnauth(c, fmt.Sprintf("%s 验证失败!", TokenName), nil)
// return
// }
// c.Set("user", claims)
// c.Next()
}
}
//WsUserAuth wodeschool用户角色验证拦截
func WsUserAuth() gin.HandlerFunc {
return func(c *gin.Context) {
// tokenString := c.GetHeader(TokenName)
// if tokenString == "" {
// yy.RespUnauth(c, "token无效!", nil)
// return
// }
// srv, err := consul.FindServer("user")
// if err != nil {
// log.WithFields(log.Fields{
// "func": "WsUserAuth",
// }).Errorf("FindServer: %s", err.Error())
// yy.RespUnauth(c, "用户认证失败,请检查后再试!", err)
// return
// }
// defer srv.Close()
// client := pb.NewCertificationClient(srv)
// resp, err := client.Auth(context.Background(), &pb.CertificationAuthRequest{
// PlatformID: conf.Golbal.PlatformID,
// Token: tokenString,
// })
// if err != nil {
// log.WithFields(log.Fields{
// "func": "WsUserAuth",
// }).Errorf("Auth: %s", err.Error())
// yy.RespUnauth(c, "用户认证失败,请检查后再试!", err)
// return
// }
// c.Set("user", yy.User{
// UserID: resp.UserID,
// PlatformID: resp.PlatformID,
// OrgID: resp.OrgID,
// Logname: resp.LogName,
// Logpassword: "",
// UserName: resp.UserName,
// Avatar: resp.Avator,
// Sex: resp.Sex,
// RoleName: resp.RoleName,
// Born: resp.Born,
// Email: resp.Email,
// Mobile: resp.Mobile,
// RegionID: uint(resp.RegionID),
// Address: resp.Address,
// })
// c.Next()
}
}

84
mongo/mongo.go Normal file
View File

@ -0,0 +1,84 @@
package mongo
import (
"context"
"errors"
"strings"
"time"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
"go.mongodb.org/mongo-driver/mongo/readpref"
)
type Config struct {
Uri string
Username string
Password string
Database string
Timeout int
}
var config *Config
func Init(conf *Config) {
config = conf
if config == nil {
config = &Config{
Uri: "mongodb://localhost:27017",
Database: "admin",
Timeout: 3,
}
}
}
//New 获取mongo客户端
//// "mongodb://user:password@localhost:27017"
func New() (*mongo.Client, error) {
clientOpts := options.Client().ApplyURI(config.Uri)
if config.Username != "" && config.Password != "" {
clientOpts = clientOpts.SetAuth(options.Credential{
AuthMechanism: "SCRAM-SHA-256",
AuthSource: config.Database,
Username: config.Username,
Password: config.Password,
})
}
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(config.Timeout)*time.Second)
defer cancel()
return mongo.Connect(ctx, clientOpts)
}
func Ping() error {
client, err := New()
if err != nil {
return err
}
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(config.Timeout)*time.Second)
defer cancel()
defer client.Disconnect(ctx)
return client.Ping(ctx, readpref.Primary())
}
//Collection 获取集合
func Collection(client *mongo.Client, cname string) (*mongo.Collection, error) {
collection := client.Database(config.Database).Collection(cname)
return collection, nil
}
//CollectionMulti 支持多数据库直接获取collection
func CollectionMulti(client *mongo.Client, cname string) (*mongo.Collection, error) {
name := strings.Split(cname, ".")
if len(name) != 2 {
return nil, errors.New("collection名称不正确请使用[database.collection]方式使用")
}
if name[0] == "" || name[1] == "" {
return nil, errors.New("名称不能为空")
}
collection := client.Database(name[0]).Collection(name[1])
return collection, nil
}

151
mongo/mongo_test.go Normal file
View File

@ -0,0 +1,151 @@
package mongo
import (
"context"
"fmt"
"testing"
"time"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
"go.mongodb.org/mongo-driver/mongo"
)
func init() {
Init(&Config{
Uri: "mongodb://192.168.0.254:27017",
Database: "test",
Username: "test",
Password: "Test1231",
Timeout: 3,
})
}
func TestMongo(t *testing.T) {
if err := Ping(); err != nil {
t.Fatal(err)
}
}
type Student struct {
ID primitive.ObjectID `bson:"_id" json:"id,omitempty"`
Name string `json:"name,omitempty"`
Sex string `json:"sex,omitempty"`
}
func TestInsert(t *testing.T) {
client, err := New()
if err != nil {
t.Fatal(err)
}
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
defer client.Disconnect(ctx)
col, err := Collection(client, "student")
if err != nil {
t.Fatal(err)
}
// ret, err := col.InsertOne(ctx, Student{Name: "张惠", Sex: "女"})
ret, err := col.InsertOne(ctx, Student{ID: primitive.NewObjectID(), Name: "张惠", Sex: "女"})
if err != nil {
t.Fatal(err)
}
fmt.Println(ret)
}
func TestDelete(t *testing.T) {
client, err := New()
if err != nil {
t.Fatal(err)
}
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
defer client.Disconnect(ctx)
col, err := Collection(client, "student")
if err != nil {
t.Fatal(err)
}
if err := col.FindOneAndDelete(ctx, bson.D{{"name", "张惠"}}).Err(); err != nil {
t.Fatal(err)
}
}
func TestSingleResult(t *testing.T) {
client, err := New()
if err != nil {
t.Fatal(err)
}
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
defer client.Disconnect(ctx)
col, err := Collection(client, "student")
if err != nil {
t.Fatal(err)
}
var result Student
// if err := col.FindOne(ctx, bson.D{{"name", "张惠"}}).Decode(&result); err != nil {
if err := col.FindOne(ctx, bson.M{"name": "张惠"}).Decode(&result); err != nil {
if err != mongo.ErrNoDocuments {
t.Fatal(err)
}
}
fmt.Println(result)
}
func TestFindAll(t *testing.T) {
client, err := New()
if err != nil {
t.Fatal(err)
}
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
defer client.Disconnect(ctx)
col, err := Collection(client, "student")
if err != nil {
t.Fatal(err)
}
cur, err := col.Find(ctx, bson.D{})
if err != nil {
t.Fatal(err)
}
defer cur.Close(ctx)
for cur.Next(ctx) {
var result Student
if err := cur.Decode(&result); err != nil {
t.Fatal(err)
}
fmt.Println(result, result.ID.String(), result.ID.Timestamp(), result.ID.Timestamp().Local())
}
if err := cur.Err(); err != nil {
t.Fatal(err)
}
}
func TestFind(t *testing.T) {
client, err := New()
if err != nil {
t.Fatal(err)
}
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
defer client.Disconnect(ctx)
col, err := Collection(client, "student")
if err != nil {
t.Fatal(err)
}
cur, err := col.Find(ctx, bson.M{"name": bson.M{"$ne": "张惠"}}) //注意这里的格式$ne不相等,$gt大于,$gte大于等于,$in in,$nin no in ,$exists是否包含这个键...
if err != nil {
t.Fatal(err)
}
defer cur.Close(ctx)
for cur.Next(ctx) {
var result Student
if err := cur.Decode(&result); err != nil {
t.Fatal(err)
}
fmt.Println(result, result.ID.String(), result.ID.Timestamp(), result.ID.Timestamp().Local())
}
if err := cur.Err(); err != nil {
t.Fatal(err)
}
}

109
mongo2/mongo.go Normal file
View File

@ -0,0 +1,109 @@
package mongo
import (
"context"
"errors"
"os"
"strings"
"time"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
"go.mongodb.org/mongo-driver/mongo/readpref"
)
var (
mongo_uri string
mongo_database string
mongo_username string
mongo_password string
)
func init() {
mongo_uri = os.Getenv("MONGO_URI")
if mongo_uri == "" {
mongo_uri = "mongodb://localhost:27017"
}
mongo_database = os.Getenv("MONGO_DB")
if mongo_database == "" {
mongo_database = "admin"
}
mongo_username = os.Getenv("MONGO_USERNAME")
mongo_password = os.Getenv("MONGO_PASSWORD")
}
// 新连接获取
func newConnect() (*mongo.Client, error) {
clientOpts := options.Client().ApplyURI(mongo_uri).SetConnectTimeout(3 * time.Second)
if mongo_username != "" && mongo_password != "" {
clientOpts = clientOpts.SetAuth(options.Credential{
AuthMechanism: "SCRAM-SHA-256",
AuthSource: "admin",
Username: mongo_username,
Password: mongo_password,
})
}
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
return mongo.Connect(ctx, clientOpts)
}
func newDB(client *mongo.Client, dbname string) (*mongo.Database, error) {
db := dbname
if db == "" {
db = mongo_database
}
return client.Database(db), nil
}
func newCollection(client *mongo.Client, dbname, colname string) (*mongo.Collection, error) {
db, err := newDB(client, dbname)
if err != nil {
return nil, err
}
return db.Collection(colname), nil
}
// 获取默认集合,即数据库名已指定的库
func newDefaultCollection(client *mongo.Client, colname string) (*mongo.Collection, error) {
db, err := newDB(client, "")
if err != nil {
return nil, err
}
return db.Collection(colname), nil
}
func ping() error {
client, err := newConnect()
if err != nil {
return err
}
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
defer client.Disconnect(ctx)
return client.Ping(ctx, readpref.Primary())
}
// Collection 获取集合
func Collection(client *mongo.Client, cname string) (*mongo.Collection, error) {
collection := client.Database(mongo_database).Collection(cname)
return collection, nil
}
// CollectionMulti 支持多数据库直接获取collection
func CollectionMulti(client *mongo.Client, cname string) (*mongo.Collection, error) {
name := strings.Split(cname, ".")
if len(name) != 2 {
return nil, errors.New("collection名称不正确请使用[database.collection]方式使用")
}
if name[0] == "" || name[1] == "" {
return nil, errors.New("名称不能为空")
}
collection := client.Database(name[0]).Collection(name[1])
return collection, nil
}

270
mongo2/mongo_test.go Normal file
View File

@ -0,0 +1,270 @@
package mongo
import (
"context"
"fmt"
"testing"
"time"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
)
func init() {
mongo_uri = "mongodb://192.168.0.254:27017"
mongo_database = "sample"
mongo_username = "aaa"
mongo_password = "bbb123"
}
func TestPing(t *testing.T) {
if err := ping(); err != nil {
t.Fatal(err)
}
}
type Student struct {
ID primitive.ObjectID `bson:"_id,omitempty" json:"id,omitempty"` //确保在 BSON 编码/解码时,当 _id 为空时不会将其包含在文档中。在插入文档时,可以不设置 ID 字段MongoDB 驱动会自动为其生成一个唯一的 ObjectID。
Name string `json:"name,omitempty"`
Sex string `json:"sex,omitempty"`
CourseID int `bson:"courseid,omitempty" json:"course_id,omitempty"`
StudentID int `json:"student_id,omitempty"`
}
func TestInsert(t *testing.T) {
client, err := newConnect()
if err != nil {
t.Fatal(err)
}
defer client.Disconnect(context.Background())
for i := 0; i < 10000; i++ {
collection, err := newCollection(client, "", "student")
if err != nil {
t.Fatal(err)
}
n := fmt.Sprintf("张小凡%d", i)
ret, err := collection.InsertOne(context.Background(), &Student{Name: n, Sex: "女", StudentID: i, CourseID: i % 10})
if err != nil {
t.Fatal(err)
}
fmt.Println(i, ret)
fmt.Println(ret.InsertedID.(primitive.ObjectID).Hex())
}
}
func TestInsertCourse(t *testing.T) {
client, err := newConnect()
if err != nil {
t.Fatal(err)
}
defer client.Disconnect(context.Background())
for i := 2; i < 10; i++ {
collection, err := newCollection(client, "", "course")
if err != nil {
t.Fatal(err)
}
n := fmt.Sprintf("sports%d", i)
ret, err := collection.InsertOne(context.Background(), &Course{ID: i, Name: n, Title: "sports"})
if err != nil {
t.Fatal(err)
}
fmt.Println(i, ret)
}
}
func TestDelete(t *testing.T) {
client, err := newConnect()
if err != nil {
t.Fatal(err)
}
defer client.Disconnect(context.Background())
col, err := newCollection(client, "", "student")
if err != nil {
t.Fatal(err)
}
if err := col.FindOneAndDelete(context.Background(), bson.D{{"name", "张惠"}}).Err(); err != nil {
t.Fatal(err)
}
ret, err := col.DeleteOne(context.Background(), bson.D{{"name", "张惠"}})
if err != nil {
t.Fatal(err)
}
fmt.Println(ret)
ret, err = col.DeleteMany(context.Background(), bson.D{{"name", "张惠"}})
if err != nil {
t.Fatal(err)
}
fmt.Println(ret)
}
func TestSingleResult(t *testing.T) {
client, err := newConnect()
if err != nil {
t.Fatal(err)
}
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
defer client.Disconnect(ctx)
col, err := Collection(client, "student")
if err != nil {
t.Fatal(err)
}
var result Student
// if err := col.FindOne(ctx, bson.D{{"name", "张惠"}}).Decode(&result); err != nil {
if err := col.FindOne(ctx, bson.M{"name": "张惠0"}).Decode(&result); err != nil {
if err != mongo.ErrNoDocuments {
t.Fatal(err)
}
}
fmt.Println(result)
}
func TestFindAll(t *testing.T) {
client, err := newConnect()
if err != nil {
t.Fatal(err)
}
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
defer client.Disconnect(ctx)
col, err := newCollection(client, "", "student")
if err != nil {
t.Fatal(err)
}
cur, err := col.Find(ctx, bson.D{})
if err != nil {
t.Fatal(err)
}
defer cur.Close(ctx)
for cur.Next(ctx) {
var result Student
if err := cur.Decode(&result); err != nil {
t.Fatal(err)
}
fmt.Println(result, result.ID.String(), result.ID.Timestamp(), result.ID.Timestamp().Local())
}
if err := cur.Err(); err != nil {
t.Fatal(err)
}
}
func TestFind(t *testing.T) {
client, err := newConnect()
if err != nil {
t.Fatal(err)
}
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
defer client.Disconnect(ctx)
col, err := newDefaultCollection(client, "student")
if err != nil {
t.Fatal(err)
}
cur, err := col.Find(ctx, bson.M{"name": "张小凡1"}) //注意这里的格式$ne不相等,$gt大于,$gte大于等于,$in in,$nin no in ,$exists是否包含这个键...
if err != nil {
t.Fatal(err)
}
defer cur.Close(ctx)
for cur.Next(ctx) {
var result Student
if err := cur.Decode(&result); err != nil {
t.Fatal(err)
}
fmt.Println(result, result.ID.String(), result.ID.Timestamp(), result.ID.Timestamp().Local())
}
if err := cur.Err(); err != nil {
t.Fatal(err)
}
}
func TestPage(t *testing.T) {
client, err := newConnect()
if err != nil {
t.Fatal(err)
}
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
defer client.Disconnect(ctx)
col, err := newDefaultCollection(client, "student")
if err != nil {
t.Fatal(err)
}
cur, err := col.Find(ctx, bson.M{}, options.Find().SetSkip(1000).SetLimit(1000))
if err != nil {
t.Fatal(err)
}
defer cur.Close(ctx)
for cur.Next(ctx) {
var result Student
if err := cur.Decode(&result); err != nil {
t.Fatal(err)
}
fmt.Println(result, result.ID.String(), result.ID.Timestamp(), result.ID.Timestamp().Local())
}
if err := cur.Err(); err != nil {
t.Fatal(err)
}
}
type Course struct {
ID int `bson:"_id,omitempty" json:"id,omitempty"`
Name string `json:"name,omitempty"`
Title string `json:"title,omitempty"`
}
// 关连表查询
func TestAggregate(t *testing.T) {
// 建立 MongoDB 连接
client, err := newConnect()
if err != nil {
t.Fatal(err)
}
defer client.Disconnect(context.Background())
// 选择数据库和集合
studentsCollection, err := newDefaultCollection(client, "student")
if err != nil {
t.Fatal(err)
}
// 执行关联查询
pipeline := bson.A{
bson.D{
{"$match", bson.D{
{"name", "张小凡1"},
}},
},
bson.D{
{"$lookup", bson.D{
{"from", "course"},
{"localField", "courseid"},
{"foreignField", "_id"},
{"as", "courses"},
}},
},
}
// 执行聚合查询结果OK
cursor, err := studentsCollection.Aggregate(context.Background(), pipeline)
if err != nil {
t.Fatal(err)
}
defer cursor.Close(context.Background())
// 解码查询结果
var result []bson.M
if err := cursor.All(context.Background(), &result); err != nil {
t.Fatal(err)
}
// 打印查询结果
fmt.Println("Result:")
for _, doc := range result {
fmt.Println(doc)
}
}

8
mqtt/config.go Normal file
View File

@ -0,0 +1,8 @@
package mqtt
type Config struct {
Host string
Username string
Password string
ClientID string
}

23
mqtt/handler.go Normal file
View File

@ -0,0 +1,23 @@
package mqtt
import (
"fmt"
MQTT "github.com/eclipse/paho.mqtt.golang"
)
var connHandler MQTT.OnConnectHandler = func(client MQTT.Client) {
token := client.Subscribe("#", 0, serviceHandler)
if token.Wait() && token.Error() != nil {
fmt.Println(token.Error())
}
}
var connLostHandler MQTT.ConnectionLostHandler = func(client MQTT.Client, err error) {
fmt.Println(err.Error())
}
// 具体业务订阅的处理,此处为示例
var serviceHandler MQTT.MessageHandler = func(client MQTT.Client, msg MQTT.Message) {
fmt.Println(msg.Topic())
}

104
mqtt/mqtt.go Normal file
View File

@ -0,0 +1,104 @@
package mqtt
import (
"encoding/json"
"errors"
MQTT "github.com/eclipse/paho.mqtt.golang"
)
var (
clientSubscribe, clientDistribute MQTT.Client
config *Config
)
func Init(c *Config) {
config = c
if config == nil {
config = &Config{
Host: "tcp://127.0.0.1:1883",
}
}
}
func Options() *MQTT.ClientOptions {
opts := MQTT.NewClientOptions()
opts.AddBroker(config.Host)
opts.Username = config.Username
opts.Password = config.Password
opts.ClientID = config.ClientID
return opts
}
func New() (MQTT.Client, error) {
if clientDistribute != nil {
return clientDistribute, nil
}
opts := Options()
clientDistribute = MQTT.NewClient(opts)
token := clientDistribute.Connect()
if token.Wait() && token.Error() != nil {
clientDistribute = nil
return nil, token.Error()
}
return clientDistribute, nil
}
// 订阅当事件为nil时可获取client
func Subscribe(connHandler MQTT.OnConnectHandler, lostHandler MQTT.ConnectionLostHandler) error {
if connHandler == nil {
return errors.New("handler is nil")
}
opts := Options()
opts.OnConnect = connHandler //当连接成功后调用注册
if lostHandler != nil {
opts.OnConnectionLost = lostHandler
}
if clientSubscribe == nil {
clientSubscribe = MQTT.NewClient(opts)
}
token := clientSubscribe.Connect()
if token.Wait() && token.Error() != nil {
clientSubscribe = nil
return token.Error()
}
return nil
}
// 取消订阅
func UnSubscribe(topic string) {
if clientSubscribe == nil {
return
}
c := clientSubscribe
c.Unsubscribe(topic)
c.Disconnect(250)
}
// 分发
func Distribute(topic *string, qos int, retained bool, payload *[]byte) error {
c, err := New()
if err != nil {
return err
}
token := c.Publish(*topic, byte(qos), retained, *payload)
if token.Wait() && token.Error() != nil {
return token.Error()
}
return nil
}
// 分发Object
func DistributeObject(topic *string, qos int, retained bool, obj interface{}) error {
payload, err := json.Marshal(obj)
if err != nil {
return err
}
return Distribute(topic, qos, retained, &payload)
}

View File

@ -7,6 +7,7 @@ import (
"gorm.io/driver/mysql" "gorm.io/driver/mysql"
"gorm.io/gorm" "gorm.io/gorm"
"gorm.io/gorm/logger" "gorm.io/gorm/logger"
"gorm.io/gorm/schema"
"gorm.io/plugin/dbresolver" "gorm.io/plugin/dbresolver"
) )
@ -20,29 +21,58 @@ type Config struct {
ConnMaxLifetime int64 //ConnMaxLifetime 最大连接时间,单位:小时 ConnMaxLifetime int64 //ConnMaxLifetime 最大连接时间,单位:小时
MaxIdleConns int MaxIdleConns int
MaxOpenConns int MaxOpenConns int
InitTable bool
} }
//Init mysql初始化 // Init mysql初始化
func Init(conf *Config) { func Init(config *Config) {
if conf != nil { if config == nil {
_conf = conf config = &Config{
MaxIdleConns: 10,
MaxOpenConns: 100,
}
} }
if config.ConnString == "" {
config.ConnString = "root:root@tcp(127.0.0.1:3306)/mysql?charset=utf8&parseTime=True&loc=Local"
}
if config.ConnMaxLifetime < 1 {
config.ConnMaxLifetime = 1
}
if config.ConnMaxLifetime > 6 {
config.ConnMaxLifetime = 6
}
if config.MaxIdleConns < 1 {
config.MaxIdleConns = 1
}
if config.MaxIdleConns > 50 {
config.MaxIdleConns = 50
}
if config.MaxOpenConns < 1 {
config.MaxOpenConns = 1
}
if config.MaxOpenConns > 500 {
config.MaxOpenConns = 500
}
_conf = config
} }
//New 创建实例 // New 创建实例
func New() (*gorm.DB, error) { func New() (*gorm.DB, error) {
if _db != nil { if _db != nil {
return _db, nil return _db, nil
} }
if _conf == nil { if _conf == nil {
return nil, errors.New("组件未初始化请执行Init") return nil, errors.New("组件未初始化,请执行Init!")
} }
var err error var err error
_db, err = gorm.Open(mysql.Open(_conf.ConnString), &gorm.Config{ _db, err = gorm.Open(mysql.Open(_conf.ConnString), &gorm.Config{
SkipDefaultTransaction: true, SkipDefaultTransaction: true,
Logger: logger.Default.LogMode(logger.Silent), Logger: logger.Default.LogMode(logger.Silent),
NamingStrategy: schema.NamingStrategy{
SingularTable: true,
},
}) })
if err != nil { if err != nil {
return nil, err return nil, err

11
mysql/tables-mysql.go Normal file
View File

@ -0,0 +1,11 @@
package mysql
func InitTable() error {
//不初始化表时返回
if !_conf.InitTable {
return nil
}
//成功初始化后返回
return nil
}

105
mysql2/mysql.go Normal file
View File

@ -0,0 +1,105 @@
package mysql
import (
"os"
"strconv"
"time"
"gorm.io/driver/mysql"
"gorm.io/gorm"
"gorm.io/gorm/logger"
"gorm.io/gorm/schema"
"gorm.io/plugin/dbresolver"
)
/*
docker配置
env MYSQL_DSN=root:root@tcp(mysql:3306)/sample?charset=utf8mb4&parseTime=True&loc=Local
env MYSQL_MAXLIFETIME=2
env MYSQL_MAXIDLECONNS=2
env MYSQL_MAXOPENCONNS=200
env MYSQL_INIT=true
*/
var (
_db *gorm.DB
)
// 创建实例
func newDB() (*gorm.DB, error) {
if _db != nil {
return _db, nil
}
dsn := os.Getenv("MYSQL_DSN")
if dsn == "" {
dsn = "root:root@tcp(127.0.0.1:3306)/mysql?charset=utf8&parseTime=True&loc=Local"
}
maxLifetime := func() int {
c := os.Getenv("MYSQL_MAXLIFETIME")
cc, err := strconv.Atoi(c)
if err != nil {
return 1
}
if cc <= 0 {
return 1
}
if cc >= 1000 {
cc = 1000
}
return cc
}()
maxIdleConns := func() int {
c := os.Getenv("MYSQL_MAXIDLECONNS")
cc, err := strconv.Atoi(c)
if err != nil {
return 1
}
if cc < 0 {
return 0
}
if cc >= 1000 {
cc = 1000
}
return cc
}()
maxOpenConns := func() int {
c := os.Getenv("MYSQL_MAXOPENCONNS")
cc, err := strconv.Atoi(c)
if err != nil {
return 1
}
if cc < 0 {
return 0
}
if cc >= 1000 {
cc = 1000
}
return cc
}()
var err error
_db, err = gorm.Open(mysql.Open(dsn), &gorm.Config{
SkipDefaultTransaction: true,
Logger: logger.Default.LogMode(logger.Silent),
NamingStrategy: schema.NamingStrategy{
SingularTable: true,
},
})
if err != nil {
return nil, err
}
_db.Use(
dbresolver.Register(dbresolver.Config{
Sources: []gorm.Dialector{mysql.Open(dsn)},
Replicas: []gorm.Dialector{mysql.Open(dsn)},
Policy: dbresolver.RandomPolicy{},
}).SetConnMaxIdleTime(time.Hour).
SetConnMaxLifetime(time.Duration(maxLifetime) * time.Hour).
SetMaxIdleConns(maxIdleConns).
SetMaxOpenConns(maxOpenConns))
return _db, nil
}

4
mysql2/readme.md Normal file
View File

@ -0,0 +1,4 @@
MYSQL_DSN=root:root@tcp(127.0.0.1:3306)/mysql?charset=utf8&parseTime=True&loc=Local
MYSQL_MAXLIFETIME=1
MYSQL_MAXIDLECONNS=1
MYSQL_MAXOPENCONNS=1

22
mysql2/tables-mysql.go Normal file
View File

@ -0,0 +1,22 @@
package mysql
import (
"os"
"myschools.me/suguo/intelligent-community/model"
)
func init() {
//不初始化表时返回
if os.Getenv("MYSQL_INIT") != "true" {
return
}
db, err := newDB()
if err != nil {
panic(err)
}
if err := db.AutoMigrate(&model.User{}); err != nil {
panic(err)
}
}

96
postgres/postgres.go Normal file
View File

@ -0,0 +1,96 @@
package postgres
import (
"os"
"strconv"
"time"
"gorm.io/driver/postgres"
"gorm.io/gorm"
"gorm.io/gorm/logger"
"gorm.io/gorm/schema"
"gorm.io/plugin/dbresolver"
)
var (
_db *gorm.DB
)
// 创建实例
func newDB() (*gorm.DB, error) {
if _db != nil {
return _db, nil
}
dsn := os.Getenv("POSTGRES_DSN")
if dsn == "" {
dsn = "host=localhost user=postgres password=postgres dbname=postgres port=5432 sslmode=disable TimeZone=Asia/Shanghai"
}
maxLifetime := func() int {
c := os.Getenv("MAXLIFETIME")
cc, err := strconv.Atoi(c)
if err != nil {
return 1
}
if cc <= 0 {
return 1
}
if cc >= 1000 {
cc = 1000
}
return cc
}()
maxIdleConns := func() int {
c := os.Getenv("MAXIDLECONNS")
cc, err := strconv.Atoi(c)
if err != nil {
return 1
}
if cc < 0 {
return 0
}
if cc >= 1000 {
cc = 1000
}
return cc
}()
maxOpenConns := func() int {
c := os.Getenv("MAXOPENCONNS")
cc, err := strconv.Atoi(c)
if err != nil {
return 1
}
if cc < 0 {
return 0
}
if cc >= 1000 {
cc = 1000
}
return cc
}()
var err error
_db, err = gorm.Open(postgres.Open(dsn), &gorm.Config{
SkipDefaultTransaction: true,
Logger: logger.Default.LogMode(logger.Silent),
NamingStrategy: schema.NamingStrategy{
SingularTable: true,
},
})
if err != nil {
return nil, err
}
_db.Use(
dbresolver.Register(dbresolver.Config{
Sources: []gorm.Dialector{postgres.Open(dsn)},
Replicas: []gorm.Dialector{postgres.Open(dsn)},
Policy: dbresolver.RandomPolicy{},
}).SetConnMaxIdleTime(time.Hour).
SetConnMaxLifetime(time.Duration(maxLifetime) * time.Hour).
SetMaxIdleConns(maxIdleConns).
SetMaxOpenConns(maxOpenConns))
return _db, nil
}

View File

@ -0,0 +1,27 @@
package postgres
import (
"os"
"myschools.me/community/community-api/model"
)
func init() {
if os.Getenv("POSTGRES_INIT") != "true" {
return
}
db, err := newDB()
if err != nil {
panic(err)
}
if err := db.AutoMigrate(&model.User{}); err != nil {
panic(err)
}
if err := db.AutoMigrate(&model.Premises{}); err != nil {
panic(err)
}
if err := db.AutoMigrate(&model.Application{}, &model.ApplicationMenu{}); err != nil {
panic(err)
}
}

177
redis/redis.go Normal file
View File

@ -0,0 +1,177 @@
package redis
import (
"encoding/json"
"time"
redigo "github.com/gomodule/redigo/redis"
)
var pool *redigo.Pool
// Config 配置
type Config struct {
Host string `yml:"host" json:"host"`
Password string `yml:"password" json:"password"`
Database int `yml:"database" json:"database"`
MaxIdle int `yml:"max_idle" json:"max_idle"`
MaxActive int `yml:"max_active" json:"max_active"`
IdleTimeout int `yml:"idle_timeout" json:"idle_timeout"` //second
}
// Init init
func Init(opts *Config) error {
if opts == nil {
opts = &Config{}
}
if opts.Host == "" {
opts.Host = "127.0.0.1:6379"
}
if opts.MaxIdle == 0 {
opts.MaxIdle = 1
}
if opts.MaxActive == 0 {
opts.MaxActive = 10
}
if opts.IdleTimeout == 0 {
opts.IdleTimeout = 600
}
pool = &redigo.Pool{
MaxActive: opts.MaxActive,
MaxIdle: opts.MaxIdle,
IdleTimeout: time.Second * time.Duration(opts.IdleTimeout),
Dial: func() (redigo.Conn, error) {
return redigo.Dial("tcp", opts.Host,
redigo.DialDatabase(opts.Database),
redigo.DialPassword(opts.Password),
)
},
TestOnBorrow: func(conn redigo.Conn, t time.Time) error {
if time.Since(t) < time.Minute {
return nil
}
_, err := conn.Do("PING")
return err
},
}
return nil
}
// GetBytes 获取一个字节数组值
func GetBytes(key *string) (*[]byte, error) {
conn := pool.Get()
defer conn.Close()
data, err := redigo.Bytes(conn.Do("GET", *key))
return &data, err
}
// Get 获取一个值
func Get(key string) interface{} {
conn := pool.Get()
defer conn.Close()
var data []byte
var err error
if data, err = redigo.Bytes(conn.Do("GET", key)); err != nil {
return nil
}
var reply interface{}
if err = json.Unmarshal(data, &reply); err != nil {
return nil
}
return reply
}
// 集合Set增加元素
func SetAdd(key string, data ...interface{}) error {
conn := pool.Get()
defer conn.Close()
var err error
for _, d := range data {
_, e := conn.Do("SADD", key, d)
if e != nil {
err = e
break
}
}
return err
}
// 集合Set删除元素
func SetRem(key string, data ...interface{}) error {
conn := pool.Get()
defer conn.Close()
var err error
for _, d := range data {
_, e := conn.Do("SREM", key, d)
if e != nil {
err = e
break
}
}
return err
}
// 集合Set判断是否存在成员member结果.(int64)==1表示存在
func SetIsMember(key string, member interface{}) (interface{}, error) {
conn := pool.Get()
defer conn.Close()
return conn.Do("SISMEMBER", key, member)
}
// 设置一个值
func Set(key string, val interface{}, timeout time.Duration) error {
data, err := json.Marshal(val)
if err != nil {
return err
}
return SetBytes(&key, &data, timeout)
}
func SetBytes(key *string, data *[]byte, timeout time.Duration) error {
conn := pool.Get()
defer conn.Close()
_, err := conn.Do("SETEX", *key, int64(timeout/time.Second), *data)
return err
}
// IsExist 判断key是否存在
func IsExist(key string) bool {
conn := pool.Get()
defer conn.Close()
a, _ := conn.Do("EXISTS", key)
i := a.(int64)
return i > 0
}
// Delete 删除
func Delete(key string) error {
conn := pool.Get()
defer conn.Close()
if _, err := conn.Do("DEL", key); err != nil {
return err
}
return nil
}
// Expire 失效时间配置
func Expire(key string, t int64) error {
conn := pool.Get()
defer conn.Close()
if _, err := conn.Do("expire", key, t); err != nil {
return err
}
return nil
}

36
redis/redis_test.go Normal file
View File

@ -0,0 +1,36 @@
package redis
import (
"fmt"
"testing"
)
func TestRedis(t *testing.T) {
Init(&Config{
Host: "192.168.0.254:6379",
Password: "",
Database: 0,
MaxIdle: 3,
MaxActive: 10,
IdleTimeout: 600,
})
err := SetAdd("aaa", "aaa", "aa1", "aa2", "aa4", "aa3")
if err != nil {
t.Fatal(err)
}
Expire("aaa", 100)
ret, err := SetIsMember("aaa", "aa1")
if err != nil {
t.Fatal(err)
}
fmt.Println(ret, ret.(int64))
err = SetRem("aaa", "aa2", "aa3")
if err != nil {
t.Fatal(err)
}
err = Delete("aaa")
if err != nil {
t.Fatal(err)
}
}

3
redis2/readme.md Normal file
View File

@ -0,0 +1,3 @@
REDIS_DSN=172.17.0.4:6379
REDIS_DB=eYVX7EwVmmxKPCDmwMtyKVge8oLd2t81
REDIS_PWD=1

169
redis2/redis.go Normal file
View File

@ -0,0 +1,169 @@
package redis
import (
"encoding/json"
"os"
"strconv"
"time"
redigo "github.com/gomodule/redigo/redis"
)
/*
env REDIS_DSN=127.0.0.1:6379
env REDIS_PWD=
env REDIS_DB=0
*/
var pool *redigo.Pool
func init() {
dbNumber := func() int {
db := os.Getenv("REDIS_DB")
database, err := strconv.Atoi(db)
if err != nil {
return 0
}
if database < 0 {
database = 0
}
if database > 16 {
database = 0
}
return database
}()
pool = &redigo.Pool{
MaxActive: 100,
MaxIdle: 1,
IdleTimeout: time.Second * time.Duration(60),
Dial: func() (redigo.Conn, error) {
return redigo.Dial("tcp", os.Getenv("REDIS_DSN"),
redigo.DialDatabase(dbNumber),
redigo.DialPassword(os.Getenv("REDIS_PWD")),
)
},
TestOnBorrow: func(conn redigo.Conn, t time.Time) error {
if time.Since(t) < time.Minute {
return nil
}
_, err := conn.Do("PING")
return err
},
}
}
// GetBytes 获取一个字节数组值
func GetBytes(key *string) (*[]byte, error) {
conn := pool.Get()
defer conn.Close()
data, err := redigo.Bytes(conn.Do("GET", *key))
return &data, err
}
// Get 获取一个值
func Get(key string) interface{} {
conn := pool.Get()
defer conn.Close()
var data []byte
var err error
if data, err = redigo.Bytes(conn.Do("GET", key)); err != nil {
return nil
}
var reply interface{}
if err = json.Unmarshal(data, &reply); err != nil {
return nil
}
return reply
}
// 集合Set增加元素
func SetAdd(key string, data ...interface{}) error {
conn := pool.Get()
defer conn.Close()
var err error
for _, d := range data {
_, e := conn.Do("SADD", key, d)
if e != nil {
err = e
break
}
}
return err
}
// 集合Set删除元素
func SetRem(key string, data ...interface{}) error {
conn := pool.Get()
defer conn.Close()
var err error
for _, d := range data {
_, e := conn.Do("SREM", key, d)
if e != nil {
err = e
break
}
}
return err
}
// 集合Set判断是否存在成员member结果.(int64)==1表示存在
func SetIsMember(key string, member interface{}) (interface{}, error) {
conn := pool.Get()
defer conn.Close()
return conn.Do("SISMEMBER", key, member)
}
// 设置一个值
func Set(key string, val interface{}, timeout time.Duration) error {
data, err := json.Marshal(val)
if err != nil {
return err
}
return SetBytes(&key, &data, timeout)
}
func SetBytes(key *string, data *[]byte, timeout time.Duration) error {
conn := pool.Get()
defer conn.Close()
_, err := conn.Do("SETEX", *key, int64(timeout/time.Second), *data)
return err
}
// IsExist 判断key是否存在
func IsExist(key string) bool {
conn := pool.Get()
defer conn.Close()
a, _ := conn.Do("EXISTS", key)
i := a.(int64)
return i > 0
}
// Delete 删除
func Delete(key string) error {
conn := pool.Get()
defer conn.Close()
if _, err := conn.Do("DEL", key); err != nil {
return err
}
return nil
}
// Expire 失效时间配置
func Expire(key string, t int64) error {
conn := pool.Get()
defer conn.Close()
if _, err := conn.Do("expire", key, t); err != nil {
return err
}
return nil
}

41
redis2/user-redis.go Normal file
View File

@ -0,0 +1,41 @@
package redis
import (
"encoding/json"
"errors"
"time"
"github.com/sirupsen/logrus"
"myschools.me/suguo/intelligent-community/model"
)
// 存储用户的Token
func UserTokenSet(key *string, usr *model.User) error {
err := Set(*key, usr, 7210*time.Second)
if err != nil {
logrus.WithFields(logrus.Fields{
"func": "UserTokenSet",
}).Warnf("Set: %s", err.Error())
return err
}
return nil
}
// 从redis中获取用户信息最佳实践经验建议把此代码放service层
func UserTokenGet(token *string) (*model.User, error) {
b, err := GetBytes(token)
if err != nil {
return nil, err
}
if b == nil {
return nil, errors.New("无效token,请重新登录!")
}
var user *model.User
if err := json.Unmarshal(*b, user); err != nil {
return nil, err
}
return user, nil
}

37
test/mosquitto_test.go Normal file
View File

@ -0,0 +1,37 @@
package test
import (
"fmt"
"testing"
MQTT "github.com/eclipse/paho.mqtt.golang"
"myschools.me/suguo/snippet/mosquitto"
)
func TestSubscribe(t *testing.T) {
mosquitto.Init(&mosquitto.Config{})
if err := mosquitto.Subscribe(connHandler, connLostHandler); err != nil {
t.Fatal(err)
}
topic := "xtj/aaaa"
payload := []byte("sadfdsaf")
mosquitto.Distribute(&topic, 0, false, &payload)
select {}
}
var connHandler MQTT.OnConnectHandler = func(client MQTT.Client) {
token := client.Subscribe("#", 0, serviceHandler)
if token.Wait() && token.Error() != nil {
fmt.Println(token.Error())
}
}
var connLostHandler MQTT.ConnectionLostHandler = func(client MQTT.Client, err error) {
fmt.Println(err.Error())
}
//具体业务订阅的处理,此处为示例
var serviceHandler MQTT.MessageHandler = func(client MQTT.Client, msg MQTT.Message) {
fmt.Println(msg.Topic())
}

7
test/redis_test.go Normal file
View File

@ -0,0 +1,7 @@
package test
import "testing"
func TestRedis(t *testing.T) {
}

View File

@ -1,8 +1,11 @@
package test package test
import ( import (
"fmt"
"math/rand"
"testing" "testing"
"github.com/stretchr/testify/assert"
"myschools.me/suguo/snippet/sqlite" "myschools.me/suguo/snippet/sqlite"
) )
@ -20,21 +23,28 @@ type CBA struct {
func TestSqlite(t *testing.T) { func TestSqlite(t *testing.T) {
conf := &sqlite.Config{ conf := &sqlite.Config{
DBFile: "tmp1/abc/aaaaa/abc.db", DBFile: "tmp/abc.db",
} }
if err := sqlite.Init(conf); err != nil { if err := sqlite.Init(conf); err != nil {
t.Fatal(err) assert.Nil(t, err)
} }
db, err := sqlite.New() db, err := sqlite.New()
if err != nil { assert.Nil(t, err)
t.Fatal(err) assert.NotNil(t, db)
}
if db == nil {
t.Error("DB nil")
}
if err := sqlite.Migrate(&ABC{}, &CBA{}); err != nil { if err := sqlite.Migrate(&ABC{}, &CBA{}); err != nil {
t.Fatal(err) assert.Nil(t, err)
} }
} }
func BenchmarkSqlite(b *testing.B) {
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
m := rand.Intn(100) + 1
n := rand.Intn(m)
for i := 0; i < n; i++ {
fmt.Printf("%d", i)
}
}
})
}