初始化项目
This commit is contained in:
commit
15917e926e
|
@ -0,0 +1,115 @@
|
|||
# 边缘设备标注服务
|
||||
|
||||
## API
|
||||
|
||||
### 获取目录文件列表
|
||||
|
||||
- 访问地址
|
||||
|
||||
```
|
||||
POST /api/directory/list
|
||||
```
|
||||
|
||||
- 请求头参数
|
||||
|
||||
Content-Type : application/json
|
||||
|
||||
- 请求参数
|
||||
|
||||
```
|
||||
{
|
||||
"path":"/home/goroot/hpds_annotation/logs/"
|
||||
}
|
||||
```
|
||||
|
||||
- 返回值
|
||||
|
||||
主题包含的字段result 中的数据结构体,当前返回的是数组类型
|
||||
|
||||
| 序号 | 字段名称 | 数据类型 | 说明 |
|
||||
|-----|--------|---------|------|
|
||||
| 1 | name | string | 文件名 |
|
||||
| 2 | path | string | 文件所在的路径 |
|
||||
| 3 | isDir | bool | 是否文件夹 |
|
||||
| 4 | size | int | 文件大小 |
|
||||
| 5 | modTime | int | 文件最后修改日期 |
|
||||
|
||||
|
||||
### 获取目录文件详情
|
||||
|
||||
- 访问地址
|
||||
|
||||
```
|
||||
POST /api/directory/info
|
||||
```
|
||||
|
||||
- 请求头参数
|
||||
|
||||
Content-Type : application/json
|
||||
|
||||
- 请求参数
|
||||
|
||||
```
|
||||
{
|
||||
"path":"/home/goroot/hpds_annotation/logs/hpds-edge-web-error.log"
|
||||
}
|
||||
```
|
||||
|
||||
- 返回值
|
||||
|
||||
主题包含的字段result 中的数据结构体,当前返回的是对象
|
||||
|
||||
| 序号 | 字段名称 | 数据类型 | 说明 |
|
||||
|-----|--------|---------|------|
|
||||
| 1 | name | string | 文件名 |
|
||||
| 2 | path | string | 文件所在的路径 |
|
||||
| 3 | isDir | bool | 是否文件夹 |
|
||||
| 4 | size | int | 文件大小 |
|
||||
| 5 | modTime | int | 文件最后修改日期 |
|
||||
| 6 | contentBase | string | 文件内容,用base64进行编码 |
|
||||
| 7 | labelStatus | int | 标注状态,0:未进行标注;1:有病害;2:无病害; |
|
||||
|
||||
|
||||
|
||||
### 提交标注数据
|
||||
|
||||
- 访问地址
|
||||
|
||||
```
|
||||
POST /api/label/submit
|
||||
```
|
||||
|
||||
- 请求头参数
|
||||
|
||||
Content-Type : application/json
|
||||
|
||||
- 请求参数
|
||||
|
||||
```
|
||||
{
|
||||
"fileList": ["/home/data/bridge_capture/crack/0001.tif", "/home/data/bridge_capture/crack/0002.tif"],
|
||||
"labelStatus": true,
|
||||
"trainingSet": "5月10日隧道数据标注集",
|
||||
"bizType": 3
|
||||
}
|
||||
```
|
||||
|
||||
+ 说明
|
||||
|
||||
|
||||
| 序号 | 字段名称 | 数据类型 | 说明 |
|
||||
|-----|--------|-------|-------------------------------------|
|
||||
| 1 | fileList | 字符串数组 | 文件的全路径组成的数组 |
|
||||
| 2 | labelStatus | 布尔值 | 标注状态,true: 有病害; false: 无病害 |
|
||||
| 3 | trainingSet | 字符串 | 训练集名称,上传到云端的训练集名称,如果云端已经存在,将合并训练集操作 |
|
||||
| 4 | bizType | 整型 | 1: 道路; 2: 桥梁; 3: 隧道; 4: 边坡; |
|
||||
|
||||
- 返回值
|
||||
|
||||
```
|
||||
{
|
||||
"code": 200,
|
||||
"message": "成功",
|
||||
"type": "OK"
|
||||
}
|
||||
```
|
|
@ -0,0 +1,119 @@
|
|||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"git.hpds.cc/Component/logging"
|
||||
"github.com/spf13/cobra"
|
||||
"go.uber.org/zap"
|
||||
"hpds_annotation/config"
|
||||
"hpds_annotation/global"
|
||||
router2 "hpds_annotation/internal/router"
|
||||
"hpds_annotation/mq"
|
||||
"hpds_annotation/store"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
var (
|
||||
ConfigFileFlag string = "./config/config.yaml"
|
||||
NodeName string = "edge-node"
|
||||
Mode string = "dev"
|
||||
)
|
||||
|
||||
func must(err error) {
|
||||
if err != nil {
|
||||
_, _ = fmt.Fprint(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func NewStartCmd() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "start",
|
||||
Short: "Start hpds_web application",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
var (
|
||||
cfg *config.WebConfig
|
||||
err error
|
||||
)
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
must(err)
|
||||
configFileFlag, err := cmd.Flags().GetString("c")
|
||||
if err != nil {
|
||||
fmt.Println("get local config err: ", err)
|
||||
return
|
||||
}
|
||||
|
||||
NodeName, err = cmd.Flags().GetString("n")
|
||||
if err != nil {
|
||||
fmt.Println("get remote path config err: ", err)
|
||||
return
|
||||
}
|
||||
Mode, err = cmd.Flags().GetString("m")
|
||||
if err != nil {
|
||||
fmt.Println("get remote path config err: ", err)
|
||||
return
|
||||
}
|
||||
cfg, err = config.ParseConfigByFile(configFileFlag)
|
||||
must(err)
|
||||
global.Cfg = cfg
|
||||
global.Logger = LoadLoggerConfig(cfg.Logging)
|
||||
logger := LoadLoggerConfig(cfg.Logging)
|
||||
global.FileLabelList = store.Load(cfg.TempPath)
|
||||
|
||||
//创建消息连接点
|
||||
mq.MqList, err = mq.NewMqClient(cfg.Funcs, cfg.Node, logger)
|
||||
must(err)
|
||||
// 退出channel
|
||||
exitChannel := make(chan os.Signal)
|
||||
defer close(exitChannel)
|
||||
router := router2.InitRouter(cfg, logger)
|
||||
// 退出信号监听
|
||||
go func(c chan os.Signal) {
|
||||
signal.Notify(c, syscall.SIGINT, syscall.SIGTERM)
|
||||
}(exitChannel)
|
||||
go func() {
|
||||
fmt.Printf("Http Server start at port %d \n", cfg.Port)
|
||||
err = http.ListenAndServe(fmt.Sprintf("%s:%d", cfg.Host, cfg.Port), router)
|
||||
must(err)
|
||||
}()
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
store.Save(cfg.TempPath, global.FileLabelList)
|
||||
logger.With(
|
||||
zap.String("web", "exit"),
|
||||
).Error(ctx.Err().Error())
|
||||
return
|
||||
case errs := <-exitChannel:
|
||||
store.Save(cfg.TempPath, global.FileLabelList)
|
||||
logger.With(
|
||||
zap.String("web", "服务退出"),
|
||||
).Info(errs.String())
|
||||
return
|
||||
}
|
||||
},
|
||||
}
|
||||
cmd.Flags().StringVar(&ConfigFileFlag, "c", "./config/config.yaml", "The configuration file path")
|
||||
cmd.Flags().StringVar(&NodeName, "n", "main-node", "The configuration name")
|
||||
cmd.Flags().StringVar(&Mode, "m", "dev", "run mode : dev | test | releases")
|
||||
return cmd
|
||||
}
|
||||
func LoadLoggerConfig(opt config.LogOptions) *logging.Logger {
|
||||
return logging.NewLogger(
|
||||
logging.SetPath(opt.Path),
|
||||
logging.SetPrefix(opt.Prefix),
|
||||
logging.SetDevelopment(opt.Development),
|
||||
logging.SetDebugFileSuffix(opt.DebugFileSuffix),
|
||||
logging.SetWarnFileSuffix(opt.WarnFileSuffix),
|
||||
logging.SetErrorFileSuffix(opt.ErrorFileSuffix),
|
||||
logging.SetInfoFileSuffix(opt.InfoFileSuffix),
|
||||
logging.SetMaxAge(opt.MaxAge),
|
||||
logging.SetMaxBackups(opt.MaxBackups),
|
||||
logging.SetMaxSize(opt.MaxSize),
|
||||
logging.SetLevel(logging.LogLevel["debug"]),
|
||||
)
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
yaml "gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
type WebConfig struct {
|
||||
Name string `yaml:"name,omitempty"`
|
||||
Host string `yaml:"host,omitempty"`
|
||||
Port int `yaml:"port,omitempty"`
|
||||
Mode string `yaml:"mode,omitempty"`
|
||||
TempPath string `yaml:"tempPath,omitempty"`
|
||||
Logging LogOptions `yaml:"logging"`
|
||||
Node HpdsNode `yaml:"node,omitempty"`
|
||||
Funcs []FuncConfig `yaml:"functions,omitempty"`
|
||||
}
|
||||
|
||||
type LogOptions struct {
|
||||
Path string `yaml:"path" json:"path" toml:"path"` // 文件保存地方
|
||||
Prefix string `yaml:"prefix" json:"prefix" toml:"prefix"` // 日志文件前缀
|
||||
ErrorFileSuffix string `yaml:"errorFileSuffix" json:"errorFileSuffix" toml:"errorFileSuffix"` // error日志文件后缀
|
||||
WarnFileSuffix string `yaml:"warnFileSuffix" json:"warnFileSuffix" toml:"warnFileSuffix"` // warn日志文件后缀
|
||||
InfoFileSuffix string `yaml:"infoFileSuffix" json:"infoFileSuffix" toml:"infoFileSuffix"` // info日志文件后缀
|
||||
DebugFileSuffix string `yaml:"debugFileSuffix" json:"debugFileSuffix" toml:"debugFileSuffix"` // debug日志文件后缀
|
||||
Level string `yaml:"level" json:"level" toml:"level"` // 日志等级
|
||||
MaxSize int `yaml:"maxSize" json:"maxSize" toml:"maxSize"` // 日志文件大小(M)
|
||||
MaxBackups int `yaml:"maxBackups" json:"maxBackups" toml:"maxBackups"` // 最多存在多少个切片文件
|
||||
MaxAge int `yaml:"maxAge" json:"maxAge" toml:"maxAge"` // 保存的最大天数
|
||||
Development bool `yaml:"development" json:"development" toml:"development"` // 是否是开发模式
|
||||
}
|
||||
|
||||
type HpdsNode struct {
|
||||
Host string `yaml:"host"`
|
||||
Port int `yaml:"port"`
|
||||
Token string `yaml:"token,omitempty"`
|
||||
}
|
||||
|
||||
type FuncConfig struct {
|
||||
Name string `yaml:"name"`
|
||||
DataTag uint8 `yaml:"dataTag"`
|
||||
MqType uint `yaml:"mqType"` //消息类型, 发布,1;订阅;2
|
||||
}
|
||||
|
||||
func ParseConfigByFile(path string) (cfg *WebConfig, err error) {
|
||||
buffer, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return load(buffer)
|
||||
}
|
||||
|
||||
func load(buf []byte) (cfg *WebConfig, err error) {
|
||||
cfg = new(WebConfig)
|
||||
cfg.Funcs = make([]FuncConfig, 0)
|
||||
//cViper.ReadConfig(bytes.NewBuffer(buf))
|
||||
err = yaml.Unmarshal(buf, cfg)
|
||||
//err = cViper.Unmarshal(cfg)
|
||||
//if err != nil {
|
||||
// return nil, err
|
||||
//}
|
||||
return
|
||||
}
|
||||
|
||||
func UpdateLocalConfig(cfg *WebConfig, fn string) error {
|
||||
data, err := yaml.Marshal(cfg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = os.WriteFile(fn, data, 0600)
|
||||
return err
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
name: edge-web
|
||||
host: 0.0.0.0
|
||||
port: 8099
|
||||
mode: dev
|
||||
tempPath: "./tmp/"
|
||||
logging:
|
||||
path: ./logs
|
||||
prefix: hpds-edge-web
|
||||
errorFileSuffix: error.log
|
||||
warnFileSuffix: warn.log
|
||||
infoFileSuffix: info.log
|
||||
debugFileSuffix: debug.log
|
||||
maxSize: 100
|
||||
maxBackups: 3000
|
||||
maxAge: 30
|
||||
development: true
|
||||
node:
|
||||
host: 127.0.0.1
|
||||
port: 27188
|
||||
token: 06d36c6f5705507dae778fdce90d0767
|
||||
functions:
|
||||
- name: edge-cmd-request
|
||||
dataTag : 30
|
||||
mqType: 2
|
||||
- name: edge-cmd-response
|
||||
dataTag: 32
|
||||
mqType: 1
|
|
@ -0,0 +1,39 @@
|
|||
package global
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"encoding/hex"
|
||||
"git.hpds.cc/Component/logging"
|
||||
"go.uber.org/zap"
|
||||
"hpds_annotation/config"
|
||||
"io"
|
||||
"os"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
var (
|
||||
FileLabelList map[string]bool
|
||||
Cfg *config.WebConfig
|
||||
Logger *logging.Logger
|
||||
maxGoroutinueNum = 5
|
||||
GoroutinueChan = make(chan bool, maxGoroutinueNum)
|
||||
//encoderPool *sync.Pool
|
||||
)
|
||||
|
||||
func FileMD5(filePath string) (string, error) {
|
||||
file, err := os.Open(filePath)
|
||||
if err != nil {
|
||||
Logger.With(
|
||||
zap.String("获取文件MD5错误", filePath),
|
||||
).Error(err.Error())
|
||||
return "", err
|
||||
}
|
||||
hash := md5.New()
|
||||
_, _ = io.Copy(hash, file)
|
||||
return hex.EncodeToString(hash.Sum(nil)), nil
|
||||
}
|
||||
func deepCopy(s string) string {
|
||||
b := make([]byte, len(s))
|
||||
copy(b, s)
|
||||
return *(*string)(unsafe.Pointer(&b))
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
module hpds_annotation
|
||||
|
||||
go 1.19
|
||||
|
||||
require (
|
||||
git.hpds.cc/Component/gin_valid v0.0.0-20230104142509-f956bce255b6
|
||||
git.hpds.cc/Component/logging v0.0.0-20230106105738-e378e873921b
|
||||
git.hpds.cc/Component/network v0.0.0-20230421024959-bf7300c92a95
|
||||
git.hpds.cc/pavement/hpds_node v0.0.0-20230421025304-47b7490878f0
|
||||
github.com/gin-contrib/zap v0.1.0
|
||||
github.com/gin-gonic/gin v1.9.0
|
||||
github.com/goccy/go-json v0.10.0
|
||||
github.com/klauspost/compress v1.16.5
|
||||
github.com/shirou/gopsutil/v3 v3.23.4
|
||||
github.com/spf13/cobra v1.7.0
|
||||
go.uber.org/zap v1.24.0
|
||||
golang.org/x/image v0.1.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
)
|
||||
|
||||
require (
|
||||
git.hpds.cc/Component/mq_coder v0.0.0-20221010064749-174ae7ae3340 // indirect
|
||||
github.com/bytedance/sonic v1.8.0 // indirect
|
||||
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
|
||||
github.com/gin-contrib/sse v0.1.0 // indirect
|
||||
github.com/go-ole/go-ole v1.2.6 // indirect
|
||||
github.com/go-playground/locales v0.14.1 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||
github.com/go-playground/validator/v10 v10.11.2 // indirect
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect
|
||||
github.com/golang/mock v1.6.0 // indirect
|
||||
github.com/golang/protobuf v1.5.2 // indirect
|
||||
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.0.9 // indirect
|
||||
github.com/leodido/go-urn v1.2.1 // indirect
|
||||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
|
||||
github.com/matoous/go-nanoid/v2 v2.0.0 // indirect
|
||||
github.com/mattn/go-isatty v0.0.17 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/onsi/ginkgo/v2 v2.2.0 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.0.6 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
|
||||
github.com/quic-go/qtls-go1-19 v0.2.1 // indirect
|
||||
github.com/quic-go/qtls-go1-20 v0.1.1 // indirect
|
||||
github.com/quic-go/quic-go v0.33.0 // indirect
|
||||
github.com/shoenig/go-m1cpu v0.1.5 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/tklauser/go-sysconf v0.3.11 // indirect
|
||||
github.com/tklauser/numcpus v0.6.0 // indirect
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||
github.com/ugorji/go/codec v1.2.9 // indirect
|
||||
github.com/yusufpapurcu/wmi v1.2.2 // indirect
|
||||
go.opentelemetry.io/otel v1.10.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.10.0 // indirect
|
||||
go.uber.org/atomic v1.9.0 // indirect
|
||||
go.uber.org/multierr v1.8.0 // indirect
|
||||
golang.org/x/arch v0.0.0-20210923205945-b76863e36670 // indirect
|
||||
golang.org/x/crypto v0.5.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20221205204356-47842c84f3db // indirect
|
||||
golang.org/x/mod v0.6.0 // indirect
|
||||
golang.org/x/net v0.7.0 // indirect
|
||||
golang.org/x/sys v0.7.0 // indirect
|
||||
golang.org/x/text v0.7.0 // indirect
|
||||
golang.org/x/tools v0.2.0 // indirect
|
||||
google.golang.org/protobuf v1.28.1 // indirect
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
)
|
|
@ -0,0 +1,55 @@
|
|||
package handler
|
||||
|
||||
import (
|
||||
"git.hpds.cc/Component/logging"
|
||||
"github.com/gin-gonic/gin"
|
||||
"hpds_annotation/config"
|
||||
"hpds_annotation/internal/proto"
|
||||
"hpds_annotation/internal/service"
|
||||
e "hpds_annotation/pkg/err"
|
||||
)
|
||||
|
||||
type HandlerService struct {
|
||||
AppConfig *config.WebConfig
|
||||
Logger *logging.Logger
|
||||
}
|
||||
|
||||
func NewHandlerService(cfg *config.WebConfig, logger *logging.Logger) *HandlerService {
|
||||
return &HandlerService{
|
||||
AppConfig: cfg,
|
||||
Logger: logger,
|
||||
}
|
||||
}
|
||||
|
||||
func (s HandlerService) GetList(c *gin.Context) (data interface{}, err error) {
|
||||
repo := service.NewEdgeService(s.Logger)
|
||||
var req proto.ListRequest
|
||||
err = c.ShouldBindJSON(&req)
|
||||
if err != nil {
|
||||
return nil, e.NewValidErr(err)
|
||||
}
|
||||
data, err = repo.GetList(c, req)
|
||||
return
|
||||
}
|
||||
|
||||
func (s HandlerService) GetInfo(c *gin.Context) (data interface{}, err error) {
|
||||
repo := service.NewEdgeService(s.Logger)
|
||||
var req proto.ListRequest
|
||||
err = c.ShouldBindJSON(&req)
|
||||
if err != nil {
|
||||
return nil, e.NewValidErr(err)
|
||||
}
|
||||
data, err = repo.GetInfo(c, req)
|
||||
return
|
||||
}
|
||||
|
||||
func (s HandlerService) LabelSubmit(c *gin.Context) (data interface{}, err error) {
|
||||
repo := service.NewEdgeService(s.Logger)
|
||||
var req proto.LabelRequest
|
||||
err = c.ShouldBindJSON(&req)
|
||||
if err != nil {
|
||||
return nil, e.NewValidErr(err)
|
||||
}
|
||||
data, err = repo.LabelSubmit(c, req)
|
||||
return
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
package middleware
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func Cors() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
method := c.Request.Method
|
||||
c.Header("Access-Control-Allow-Origin", "*")
|
||||
c.Header("Access-Control-Allow-Headers", "Content-Type,AccessToken,X-CSRF-Token, Authorization, Token")
|
||||
c.Header("Access-Control-Allow-Methods", "POST, GET,DELETE,PUT,OPTIONS")
|
||||
c.Header("Access-Control-Expose-Headers", "Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers, Content-Type")
|
||||
c.Header("Access-Control-Allow-Credentials", "true")
|
||||
// 放行所有OPTIONS方法
|
||||
if method == "OPTIONS" {
|
||||
c.AbortWithStatus(http.StatusNoContent)
|
||||
}
|
||||
// 处理请求
|
||||
c.Next()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
package proto
|
||||
|
||||
type ListRequest struct {
|
||||
Path string `json:"path"`
|
||||
IsIncludeSubdirectories bool `json:"isIncludeSubdirectories"`
|
||||
}
|
||||
|
||||
type LabelRequest struct {
|
||||
FileList []string `json:"fileList"`
|
||||
LabelStatus bool `json:"labelStatus"`
|
||||
TrainingSet string `json:"trainingSet"`
|
||||
BizType int `json:"bizType"`
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
package proto
|
||||
|
||||
// BaseResponse 基础返回结构
|
||||
type BaseResponse struct {
|
||||
Code int `json:"code"`
|
||||
Message string `json:"message"`
|
||||
Data interface{} `json:"result,omitempty"`
|
||||
Status string `json:"type,omitempty"`
|
||||
Err error `json:"error,omitempty"` // 错误堆栈
|
||||
//Page int64 `json:"page,omitempty"` //当前页码
|
||||
//PageSize int64 `json:"pageSize,omitempty"` // 单页显示记录数--前端参数2
|
||||
//PageCount int64 `json:"totalPage,omitempty"` // 总页数
|
||||
//TotalSize int64 `json:"total,omitempty"` // 总记录数
|
||||
}
|
||||
|
||||
type FileBaseInfo struct {
|
||||
Name string `json:"name"`
|
||||
Path string `json:"path"`
|
||||
IsDir bool `json:"isDir"`
|
||||
Size int64 `json:"size"`
|
||||
ModTime int64 `json:"modTime"`
|
||||
}
|
||||
|
||||
type FileContent struct {
|
||||
FileBaseInfo
|
||||
ContentBase string `json:"contentBase"`
|
||||
LabelStatus int `json:"labelStatus"` //0:未标注;1:有病害;2:无病害
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
package router
|
||||
|
||||
import (
|
||||
"git.hpds.cc/Component/logging"
|
||||
ginzap "github.com/gin-contrib/zap"
|
||||
"github.com/gin-gonic/gin"
|
||||
"hpds_annotation/config"
|
||||
"hpds_annotation/internal/handler"
|
||||
"hpds_annotation/internal/middleware"
|
||||
|
||||
e "hpds_annotation/pkg/err"
|
||||
)
|
||||
|
||||
func InitRouter(cfg *config.WebConfig, logger *logging.Logger) *gin.Engine {
|
||||
hs := handler.NewHandlerService(cfg, logger)
|
||||
gin.SetMode(gin.ReleaseMode)
|
||||
root := gin.New()
|
||||
root.Use(ginzap.Ginzap(logger.Logger, "2006-01-02 15:04:05.000", true))
|
||||
root.Use(middleware.Cors())
|
||||
r := root.Group("/api")
|
||||
{
|
||||
dir := r.Group("/directory")
|
||||
{
|
||||
dir.POST("/list", e.ErrorWrapper(hs.GetList))
|
||||
dir.POST("/info", e.ErrorWrapper(hs.GetInfo))
|
||||
}
|
||||
label := r.Group("/label")
|
||||
{
|
||||
label.POST("/submit", e.ErrorWrapper(hs.LabelSubmit))
|
||||
}
|
||||
}
|
||||
return root
|
||||
}
|
|
@ -0,0 +1,220 @@
|
|||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"git.hpds.cc/Component/logging"
|
||||
"hpds_annotation/config"
|
||||
"hpds_annotation/global"
|
||||
"hpds_annotation/internal/proto"
|
||||
"hpds_annotation/mq"
|
||||
"hpds_annotation/pkg/utils"
|
||||
"hpds_annotation/store"
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
)
|
||||
|
||||
type PagingStruct struct {
|
||||
List interface{} `json:"list"`
|
||||
Total int64 `json:"total"`
|
||||
}
|
||||
|
||||
// FillPaging 填充分页数据
|
||||
func FillPaging(count, pageNum, pageSize int64, list interface{}, data *proto.BaseResponse) *proto.BaseResponse {
|
||||
_ = fmt.Sprintf("%d, %d", pageNum, pageSize)
|
||||
ps := new(PagingStruct)
|
||||
ps.List = list
|
||||
ps.Total = count
|
||||
data.Data = ps
|
||||
//data.PageSize = pageSize
|
||||
//data.Data = list
|
||||
//data.Page = pageNum
|
||||
//data.PageCount = tp
|
||||
//data.TotalSize = count
|
||||
return data
|
||||
}
|
||||
|
||||
type repo struct {
|
||||
AppConfig *config.WebConfig
|
||||
logger *logging.Logger
|
||||
}
|
||||
|
||||
type EdgeService interface {
|
||||
GetList(ctx context.Context, req proto.ListRequest) (rsp *proto.BaseResponse, err error)
|
||||
GetInfo(ctx context.Context, req proto.ListRequest) (rsp *proto.BaseResponse, err error)
|
||||
LabelSubmit(ctx context.Context, req proto.LabelRequest) (rsp *proto.BaseResponse, err error)
|
||||
}
|
||||
|
||||
func NewEdgeService(logger *logging.Logger) EdgeService {
|
||||
return &repo{
|
||||
logger: logger,
|
||||
}
|
||||
}
|
||||
|
||||
func (rp *repo) GetList(ctx context.Context, req proto.ListRequest) (rsp *proto.BaseResponse, err error) {
|
||||
rsp = new(proto.BaseResponse)
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
err = fmt.Errorf("超时/取消")
|
||||
rsp.Code = http.StatusInternalServerError
|
||||
rsp.Status = http.StatusText(http.StatusInternalServerError)
|
||||
rsp.Message = "超时/取消"
|
||||
rsp.Err = ctx.Err()
|
||||
return rsp, ctx.Err()
|
||||
default:
|
||||
if req.IsIncludeSubdirectories {
|
||||
|
||||
} else {
|
||||
var files []os.DirEntry
|
||||
files, err = os.ReadDir(req.Path)
|
||||
if err != nil {
|
||||
goto ReturnPoint
|
||||
}
|
||||
list := make([]proto.FileBaseInfo, len(files))
|
||||
for k, v := range files {
|
||||
info, _ := v.Info()
|
||||
item := proto.FileBaseInfo{
|
||||
Name: v.Name(),
|
||||
Path: path.Join(req.Path, v.Name()),
|
||||
IsDir: v.IsDir(),
|
||||
Size: info.Size(),
|
||||
ModTime: info.ModTime().Unix(),
|
||||
}
|
||||
list[k] = item
|
||||
}
|
||||
|
||||
rsp.Code = http.StatusOK
|
||||
rsp.Status = http.StatusText(http.StatusOK)
|
||||
rsp.Message = "成功"
|
||||
rsp.Data = list
|
||||
rsp.Err = err
|
||||
return rsp, err
|
||||
}
|
||||
}
|
||||
|
||||
ReturnPoint:
|
||||
if err != nil {
|
||||
rsp.Code = http.StatusInternalServerError
|
||||
rsp.Status = http.StatusText(http.StatusInternalServerError)
|
||||
rsp.Err = err
|
||||
rsp.Message = "失败"
|
||||
}
|
||||
return rsp, err
|
||||
}
|
||||
|
||||
func (rp *repo) GetInfo(ctx context.Context, req proto.ListRequest) (rsp *proto.BaseResponse, err error) {
|
||||
rsp = new(proto.BaseResponse)
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
err = fmt.Errorf("超时/取消")
|
||||
rsp.Code = http.StatusInternalServerError
|
||||
rsp.Status = http.StatusText(http.StatusInternalServerError)
|
||||
rsp.Message = "超时/取消"
|
||||
rsp.Err = ctx.Err()
|
||||
return rsp, ctx.Err()
|
||||
default:
|
||||
var (
|
||||
fileInfo os.FileInfo
|
||||
)
|
||||
fileInfo, err = os.Stat(req.Path)
|
||||
if err != nil {
|
||||
goto ReturnPoint
|
||||
}
|
||||
if fileInfo.IsDir() {
|
||||
err = fmt.Errorf("不能获取文件夹的详细信息")
|
||||
goto ReturnPoint
|
||||
}
|
||||
buff := utils.ReadFile(req.Path)
|
||||
res := new(proto.FileContent)
|
||||
res.Name = fileInfo.Name()
|
||||
res.IsDir = false
|
||||
res.Path = req.Path
|
||||
res.Size = fileInfo.Size()
|
||||
res.ModTime = fileInfo.ModTime().Unix()
|
||||
res.ContentBase = base64.StdEncoding.EncodeToString(buff)
|
||||
b, ok := global.FileLabelList[req.Path]
|
||||
if ok {
|
||||
if b {
|
||||
res.LabelStatus = 1
|
||||
} else {
|
||||
res.LabelStatus = 2
|
||||
}
|
||||
} else {
|
||||
res.LabelStatus = 0
|
||||
}
|
||||
|
||||
rsp.Code = http.StatusOK
|
||||
rsp.Status = http.StatusText(http.StatusOK)
|
||||
rsp.Message = "成功"
|
||||
rsp.Data = res
|
||||
rsp.Err = err
|
||||
return rsp, err
|
||||
}
|
||||
|
||||
ReturnPoint:
|
||||
if err != nil {
|
||||
rsp.Code = http.StatusInternalServerError
|
||||
rsp.Status = http.StatusText(http.StatusInternalServerError)
|
||||
rsp.Err = err
|
||||
rsp.Message = "失败"
|
||||
}
|
||||
return rsp, err
|
||||
}
|
||||
|
||||
func (rp *repo) LabelSubmit(ctx context.Context, req proto.LabelRequest) (rsp *proto.BaseResponse, err error) {
|
||||
rsp = new(proto.BaseResponse)
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
err = fmt.Errorf("超时/取消")
|
||||
rsp.Code = http.StatusInternalServerError
|
||||
rsp.Status = http.StatusText(http.StatusInternalServerError)
|
||||
rsp.Message = "超时/取消"
|
||||
rsp.Err = ctx.Err()
|
||||
return rsp, ctx.Err()
|
||||
default:
|
||||
list := make([]proto.FileContent, len(req.FileList))
|
||||
var (
|
||||
fileInfo os.FileInfo
|
||||
)
|
||||
for k, v := range req.FileList {
|
||||
global.FileLabelList[v] = req.LabelStatus
|
||||
fileInfo, err = os.Stat(v)
|
||||
if err != nil {
|
||||
goto ReturnPoint
|
||||
}
|
||||
buff := utils.ReadFile(v)
|
||||
status := 0
|
||||
if req.LabelStatus {
|
||||
status = 1
|
||||
} else {
|
||||
status = 2
|
||||
}
|
||||
dstContent := store.Compress(buff)
|
||||
list[k] = proto.FileContent{
|
||||
FileBaseInfo: proto.FileBaseInfo{
|
||||
Name: fileInfo.Name(),
|
||||
Path: v,
|
||||
Size: fileInfo.Size(),
|
||||
ModTime: fileInfo.ModTime().Unix(),
|
||||
},
|
||||
ContentBase: base64.StdEncoding.EncodeToString(dstContent),
|
||||
LabelStatus: status,
|
||||
}
|
||||
}
|
||||
go mq.SendLabelData(list, rp.logger)
|
||||
rsp.Code = http.StatusOK
|
||||
rsp.Status = http.StatusText(http.StatusOK)
|
||||
rsp.Message = "成功"
|
||||
rsp.Err = err
|
||||
ReturnPoint:
|
||||
if err != nil {
|
||||
rsp.Code = http.StatusInternalServerError
|
||||
rsp.Status = http.StatusText(http.StatusInternalServerError)
|
||||
rsp.Err = err
|
||||
rsp.Message = "失败"
|
||||
}
|
||||
return rsp, err
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/spf13/cobra"
|
||||
"hpds_annotation/cmd"
|
||||
"os"
|
||||
)
|
||||
|
||||
var (
|
||||
rootCmd = &cobra.Command{
|
||||
Use: "hpds_edge_web",
|
||||
Long: "hpds_edge_web is a IoT WEB UI",
|
||||
Version: "0.1",
|
||||
}
|
||||
)
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(cmd.NewStartCmd())
|
||||
}
|
||||
func main() {
|
||||
if err := rootCmd.Execute(); err != nil {
|
||||
_, _ = fmt.Fprint(os.Stderr, err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,103 @@
|
|||
package mq
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"hpds_annotation/global"
|
||||
"hpds_annotation/internal/proto"
|
||||
"hpds_annotation/pkg/utils"
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
)
|
||||
|
||||
func GetList(req proto.ListRequest) (rsp *proto.BaseResponse, err error) {
|
||||
rsp = new(proto.BaseResponse)
|
||||
|
||||
var (
|
||||
files []os.DirEntry
|
||||
list []proto.FileBaseInfo
|
||||
)
|
||||
files, err = os.ReadDir(req.Path)
|
||||
if err != nil {
|
||||
goto ReturnPoint
|
||||
}
|
||||
list = make([]proto.FileBaseInfo, len(files))
|
||||
for k, v := range files {
|
||||
info, _ := v.Info()
|
||||
item := proto.FileBaseInfo{
|
||||
Name: v.Name(),
|
||||
Path: path.Join(req.Path, v.Name()),
|
||||
IsDir: v.IsDir(),
|
||||
Size: info.Size(),
|
||||
ModTime: info.ModTime().Unix(),
|
||||
}
|
||||
list[k] = item
|
||||
}
|
||||
|
||||
rsp.Code = http.StatusOK
|
||||
rsp.Status = http.StatusText(http.StatusOK)
|
||||
rsp.Message = "成功"
|
||||
rsp.Data = list
|
||||
rsp.Err = err
|
||||
return rsp, err
|
||||
ReturnPoint:
|
||||
if err != nil {
|
||||
rsp.Code = http.StatusInternalServerError
|
||||
rsp.Status = http.StatusText(http.StatusInternalServerError)
|
||||
rsp.Err = err
|
||||
rsp.Message = "失败"
|
||||
}
|
||||
return rsp, err
|
||||
}
|
||||
|
||||
func GetFileInfo(req proto.ListRequest) (rsp *proto.BaseResponse, err error) {
|
||||
rsp = new(proto.BaseResponse)
|
||||
var (
|
||||
fileInfo os.FileInfo
|
||||
b, ok bool
|
||||
buff []byte
|
||||
)
|
||||
res := new(proto.FileContent)
|
||||
fileInfo, err = os.Stat(req.Path)
|
||||
if err != nil {
|
||||
goto ReturnPoint
|
||||
}
|
||||
if fileInfo.IsDir() {
|
||||
err = fmt.Errorf("不能获取文件夹的详细信息")
|
||||
goto ReturnPoint
|
||||
}
|
||||
buff = utils.ReadFile(req.Path)
|
||||
res.Name = fileInfo.Name()
|
||||
res.IsDir = false
|
||||
res.Path = req.Path
|
||||
res.Size = fileInfo.Size()
|
||||
res.ModTime = fileInfo.ModTime().Unix()
|
||||
res.ContentBase = base64.StdEncoding.EncodeToString(buff)
|
||||
b, ok = global.FileLabelList[req.Path]
|
||||
if ok {
|
||||
if b {
|
||||
res.LabelStatus = 1
|
||||
} else {
|
||||
res.LabelStatus = 2
|
||||
}
|
||||
} else {
|
||||
res.LabelStatus = 0
|
||||
}
|
||||
|
||||
rsp.Code = http.StatusOK
|
||||
rsp.Status = http.StatusText(http.StatusOK)
|
||||
rsp.Message = "成功"
|
||||
rsp.Data = res
|
||||
rsp.Err = err
|
||||
return rsp, err
|
||||
|
||||
ReturnPoint:
|
||||
if err != nil {
|
||||
rsp.Code = http.StatusInternalServerError
|
||||
rsp.Status = http.StatusText(http.StatusInternalServerError)
|
||||
rsp.Err = err
|
||||
rsp.Message = "失败"
|
||||
}
|
||||
return rsp, err
|
||||
}
|
|
@ -0,0 +1,199 @@
|
|||
package mq
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"go.uber.org/zap"
|
||||
"hpds_annotation/config"
|
||||
"hpds_annotation/global"
|
||||
"hpds_annotation/internal/proto"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"git.hpds.cc/Component/logging"
|
||||
"git.hpds.cc/Component/network/frame"
|
||||
"git.hpds.cc/pavement/hpds_node"
|
||||
"github.com/shirou/gopsutil/v3/host"
|
||||
)
|
||||
|
||||
var MqList []HpdsMqNode
|
||||
|
||||
type HpdsMqNode struct {
|
||||
MqType uint
|
||||
Topic string
|
||||
Node config.HpdsNode
|
||||
EndPoint interface{}
|
||||
}
|
||||
|
||||
var ifChannelsMapInit = false
|
||||
|
||||
var ChannelsMap = map[string]chan string{}
|
||||
|
||||
func initChannelsMap() {
|
||||
ChannelsMap = make(map[string]chan string)
|
||||
}
|
||||
func AddChannel(userId string) {
|
||||
if !ifChannelsMapInit {
|
||||
initChannelsMap()
|
||||
ifChannelsMapInit = true
|
||||
}
|
||||
var newChannel = make(chan string)
|
||||
ChannelsMap[userId] = newChannel
|
||||
logging.L().Info("Build SSE connection for user = " + userId)
|
||||
}
|
||||
|
||||
func must(logger *logging.Logger, err error) {
|
||||
if err != nil {
|
||||
if logger != nil {
|
||||
logger.With(zap.String("web节点", "错误信息")).Error("启动错误", zap.Error(err))
|
||||
} else {
|
||||
_, _ = fmt.Fprint(os.Stderr, err)
|
||||
}
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func NewMqClient(funcs []config.FuncConfig, node config.HpdsNode, logger *logging.Logger) (mqList []HpdsMqNode, err error) {
|
||||
mqList = make([]HpdsMqNode, 0)
|
||||
for _, v := range funcs {
|
||||
switch v.MqType {
|
||||
case 2:
|
||||
sf := hpds_node.NewStreamFunction(
|
||||
v.Name,
|
||||
hpds_node.WithMqAddr(fmt.Sprintf("%s:%d", node.Host, node.Port)),
|
||||
hpds_node.WithObserveDataTags(frame.Tag(v.DataTag)),
|
||||
hpds_node.WithCredential(node.Token),
|
||||
)
|
||||
err = sf.Connect()
|
||||
nodeInfo := HpdsMqNode{
|
||||
MqType: 2,
|
||||
Topic: v.Name,
|
||||
Node: node,
|
||||
EndPoint: sf,
|
||||
}
|
||||
must(logger, err)
|
||||
switch v.Name {
|
||||
case "edge-cmd-request":
|
||||
_ = sf.SetHandler(EdgeCmdHandle)
|
||||
default:
|
||||
|
||||
}
|
||||
mqList = append(mqList, nodeInfo)
|
||||
default:
|
||||
ap := hpds_node.NewAccessPoint(
|
||||
v.Name,
|
||||
hpds_node.WithMqAddr(fmt.Sprintf("%s:%d", node.Host, node.Port)),
|
||||
hpds_node.WithCredential(node.Token),
|
||||
)
|
||||
err = ap.Connect()
|
||||
nodeInfo := HpdsMqNode{
|
||||
MqType: 1,
|
||||
Topic: v.Name,
|
||||
Node: node,
|
||||
EndPoint: ap,
|
||||
}
|
||||
must(logger, err)
|
||||
ap.SetDataTag(frame.Tag(v.DataTag))
|
||||
mqList = append(mqList, nodeInfo)
|
||||
}
|
||||
|
||||
}
|
||||
return mqList, err
|
||||
}
|
||||
|
||||
func GetMqClient(topic string, mqType uint) *HpdsMqNode {
|
||||
for _, v := range MqList {
|
||||
if v.Topic == topic && v.MqType == mqType {
|
||||
return &v
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func GenerateAndSendData(stream hpds_node.AccessPoint, data []byte, logger *logging.Logger) error {
|
||||
logger.With(zap.String("web节点", "发送消息")).Info("数据", zap.String("发送的数据", string(data)))
|
||||
_, err := stream.Write(data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
time.Sleep(1000 * time.Millisecond)
|
||||
return nil
|
||||
}
|
||||
|
||||
func EdgeCmdHandle(data []byte) (frame.Tag, []byte) {
|
||||
global.Logger.Info("任务日志", zap.String("接收数据", string(data)))
|
||||
cmd := new(InstructionReq)
|
||||
err := json.Unmarshal(data, cmd)
|
||||
if err != nil {
|
||||
return 0x0B, []byte(err.Error())
|
||||
}
|
||||
switch cmd.Command {
|
||||
case DataLabelRequest:
|
||||
hi, _ := host.Info()
|
||||
payload := cmd.Payload.(map[string]interface{})
|
||||
if payload["nodeGuid"] == hi.HostID {
|
||||
//currTime := time.Now().Unix()
|
||||
var (
|
||||
req proto.ListRequest
|
||||
res *proto.BaseResponse
|
||||
)
|
||||
if v, ok := payload["path"]; ok {
|
||||
req.Path = v.(string)
|
||||
}
|
||||
resPayload := new(DataLabelRes)
|
||||
resPayload.NodeGuid = hi.HostID
|
||||
|
||||
switch strings.ToLower(payload["cmd"].(string)) {
|
||||
case "get-list":
|
||||
res, err = GetList(req)
|
||||
if err != nil {
|
||||
goto ErrorPoint
|
||||
}
|
||||
case "get-file-info":
|
||||
res, err = GetFileInfo(req)
|
||||
if err != nil {
|
||||
goto ErrorPoint
|
||||
}
|
||||
}
|
||||
ErrorPoint:
|
||||
if err != nil {
|
||||
res.Code = http.StatusInternalServerError
|
||||
res.Status = http.StatusText(http.StatusInternalServerError)
|
||||
res.Err = err
|
||||
res.Message = "失败"
|
||||
}
|
||||
resPayload.Body = res
|
||||
rsp := new(InstructionReq)
|
||||
rsp.Command = DataLabelResponse
|
||||
rsp.Payload = resPayload
|
||||
str, _ := json.Marshal(rsp)
|
||||
cli := GetMqClient("edge-cmd-response", 1)
|
||||
if cli != nil {
|
||||
_ = GenerateAndSendData(cli.EndPoint.(hpds_node.AccessPoint), str, global.Logger)
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0x0B, nil
|
||||
}
|
||||
|
||||
func SendLabelData(list []proto.FileContent, logger *logging.Logger) {
|
||||
cli := GetMqClient("edge-cmd-response", 1)
|
||||
if cli != nil {
|
||||
for _, v := range list {
|
||||
payload := InstructionReq{
|
||||
Command: DataLabelResponse,
|
||||
Payload: v,
|
||||
}
|
||||
s, _ := json.Marshal(payload)
|
||||
err := GenerateAndSendData(cli.EndPoint.(hpds_node.AccessPoint), s, logger)
|
||||
if err != nil {
|
||||
logger.With(
|
||||
zap.String("文件名称", v.Name),
|
||||
zap.String("存储路径", v.Path),
|
||||
).Error("文件传输", zap.Error(err))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
package mq
|
||||
|
||||
const (
|
||||
DataLabelRequest = iota + 12
|
||||
DataLabelResponse
|
||||
)
|
||||
|
||||
type InstructionReq struct {
|
||||
Command int `json:"command"`
|
||||
Payload interface{} `json:"payload"`
|
||||
}
|
||||
|
||||
type DataLabelRes struct {
|
||||
NodeGuid string `json:"nodeGuid"`
|
||||
Body interface{} `json:"body"`
|
||||
}
|
|
@ -0,0 +1,83 @@
|
|||
package e
|
||||
|
||||
import "github.com/goccy/go-json"
|
||||
|
||||
const (
|
||||
UnKnow = 1
|
||||
ValidErr = 1000
|
||||
DbErr = 2020
|
||||
IllegalPhone = 2001
|
||||
NoAuth = 401
|
||||
NoUser = 2003
|
||||
ExistingUserName = 2004
|
||||
UnmarshalReqErr = 3000
|
||||
Ordinary = 6666
|
||||
)
|
||||
|
||||
func init() {
|
||||
m := map[int]string{
|
||||
UnKnow: "未定义错误",
|
||||
ValidErr: "无效的请求参数",
|
||||
DbErr: "数据库错误",
|
||||
IllegalPhone: "请输入正确的手机号码",
|
||||
NoAuth: "未授权",
|
||||
NoUser: "未找到对应用户",
|
||||
ExistingUserName: "已经存在的用户",
|
||||
UnmarshalReqErr: "参数类型错误",
|
||||
}
|
||||
|
||||
errMap[UnKnow] = &ApiError{
|
||||
Status: 500,
|
||||
Code: UnKnow,
|
||||
Message: "未定义错误",
|
||||
}
|
||||
|
||||
for k, v := range m {
|
||||
errMap[k] = &ApiError{
|
||||
Status: 200,
|
||||
Code: k,
|
||||
Message: v,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var errMap = map[int]*ApiError{}
|
||||
|
||||
func NewCode(code int) *ApiError {
|
||||
if e, ok := errMap[code]; ok {
|
||||
return e
|
||||
}
|
||||
return errMap[UnKnow]
|
||||
}
|
||||
func NewString(msg string) *ApiError {
|
||||
return &ApiError{
|
||||
Status: 200,
|
||||
Code: Ordinary,
|
||||
Message: msg,
|
||||
}
|
||||
}
|
||||
func newErr(err *ApiError) *ApiError {
|
||||
return &(*err)
|
||||
}
|
||||
func NewValidErr(err error) error {
|
||||
if _, ok := err.(*json.UnmarshalTypeError); ok {
|
||||
return newErr(errMap[UnmarshalReqErr])
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (e *ApiError) Set(msg string) *ApiError {
|
||||
r := *e
|
||||
r.Message = msg
|
||||
return &r
|
||||
}
|
||||
|
||||
type ApiError struct {
|
||||
Status int `json:"-"`
|
||||
Code int `json:"code"`
|
||||
Message string `json:"msg"`
|
||||
}
|
||||
|
||||
func (e *ApiError) Error() string {
|
||||
return e.Message
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
package e
|
||||
|
||||
import (
|
||||
"git.hpds.cc/Component/gin_valid/gin/binding"
|
||||
validator "git.hpds.cc/Component/gin_valid/go-playground/validator/v10"
|
||||
"github.com/gin-gonic/gin"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type WrapperHandle func(c *gin.Context) (interface{}, error)
|
||||
|
||||
func ErrorWrapper(handle WrapperHandle) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
data, err := handle(c)
|
||||
if err != nil {
|
||||
switch r := err.(type) {
|
||||
case *ApiError:
|
||||
c.JSON(r.Status, r)
|
||||
case validator.ValidationErrors:
|
||||
msg := r.Translate(binding.ValidTrans).Error()
|
||||
c.JSON(http.StatusOK, gin.H{"code": "", "msg": msg})
|
||||
default:
|
||||
er := NewValidErr(err)
|
||||
c.JSON(http.StatusOK, gin.H{"code": 500, "msg": er.Error()})
|
||||
}
|
||||
return
|
||||
}
|
||||
if data != nil {
|
||||
c.JSON(http.StatusOK, data)
|
||||
} else {
|
||||
c.JSON(http.StatusOK, gin.H{"code": 200, "data": data})
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
func ErrorWeChatWrapper(handle WrapperHandle) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
data, err := handle(c)
|
||||
if err != nil {
|
||||
switch r := err.(type) {
|
||||
case *ApiError:
|
||||
c.JSON(r.Status, r)
|
||||
case validator.ValidationErrors:
|
||||
msg := r.Translate(binding.ValidTrans).Error()
|
||||
c.JSON(http.StatusOK, gin.H{"code": "", "msg": msg})
|
||||
default:
|
||||
er := NewValidErr(err)
|
||||
c.JSON(http.StatusOK, gin.H{"code": 500, "msg": er.Error()})
|
||||
}
|
||||
return
|
||||
}
|
||||
if data != nil {
|
||||
c.JSON(http.StatusOK, gin.H{"code": "SUCCESS", "data": data})
|
||||
} else {
|
||||
c.JSON(http.StatusOK, gin.H{"code": "SUCCESS", "message": "成功"})
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,88 @@
|
|||
package utils
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"git.hpds.cc/Component/logging"
|
||||
"go.uber.org/zap"
|
||||
"io"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func CopyFile(src, dst string) error {
|
||||
sourceFileStat, err := os.Stat(src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !sourceFileStat.Mode().IsRegular() {
|
||||
return fmt.Errorf("%s is not a regular file", src)
|
||||
}
|
||||
|
||||
source, err := os.Open(src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func(source *os.File) {
|
||||
_ = source.Close()
|
||||
}(source)
|
||||
|
||||
destination, err := os.Create(dst)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func(destination *os.File) {
|
||||
_ = destination.Close()
|
||||
}(destination)
|
||||
_, err = io.Copy(destination, source)
|
||||
return err
|
||||
}
|
||||
|
||||
func PathExists(path string) bool {
|
||||
_, err := os.Stat(path)
|
||||
if err == nil {
|
||||
return true
|
||||
}
|
||||
if os.IsNotExist(err) {
|
||||
return false
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// ReadFile 读取到file中,再利用ioutil将file直接读取到[]byte中, 这是最优
|
||||
func ReadFile(fn string) []byte {
|
||||
f, err := os.Open(fn)
|
||||
if err != nil {
|
||||
logging.L().Error("Read File", zap.String("File Name", fn), zap.Error(err))
|
||||
return nil
|
||||
}
|
||||
defer func(f *os.File) {
|
||||
_ = f.Close()
|
||||
}(f)
|
||||
|
||||
fd, err := io.ReadAll(f)
|
||||
if err != nil {
|
||||
logging.L().Error("Read File To buff", zap.String("File Name", fn), zap.Error(err))
|
||||
return nil
|
||||
}
|
||||
|
||||
return fd
|
||||
}
|
||||
|
||||
func GetFileName(fn string) string {
|
||||
fileType := path.Ext(fn)
|
||||
return strings.TrimSuffix(fn, fileType)
|
||||
}
|
||||
func GetFileNameAndExt(fn string) string {
|
||||
_, fileName := filepath.Split(fn)
|
||||
return fileName
|
||||
}
|
||||
func GetFileMd5(data []byte) string {
|
||||
hash := md5.New()
|
||||
hash.Write(data)
|
||||
return hex.EncodeToString(hash.Sum(nil))
|
||||
}
|
|
@ -0,0 +1,126 @@
|
|||
package utils
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"mime/multipart"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func HttpDo(reqUrl, method string, params map[string]string, header map[string]string) (data []byte, err error) {
|
||||
var paramStr string = ""
|
||||
if contentType, ok := header["ContentType"]; ok && strings.Contains(contentType, "json") {
|
||||
bytesData, _ := json.Marshal(params)
|
||||
paramStr = string(bytesData)
|
||||
} else {
|
||||
for k, v := range params {
|
||||
if len(paramStr) == 0 {
|
||||
paramStr = fmt.Sprintf("%s=%s", k, url.QueryEscape(v))
|
||||
} else {
|
||||
paramStr = fmt.Sprintf("%s&%s=%s", paramStr, k, url.QueryEscape(v))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
client := &http.Client{}
|
||||
req, err := http.NewRequest(strings.ToUpper(method), reqUrl, strings.NewReader(paramStr))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for k, v := range header {
|
||||
req.Header.Set(k, v)
|
||||
}
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if resp.Body != nil {
|
||||
err = resp.Body.Close()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
var body []byte
|
||||
if resp.Body != nil {
|
||||
body, err = io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return body, nil
|
||||
}
|
||||
|
||||
type UploadFile struct {
|
||||
// 表单名称
|
||||
Name string
|
||||
Filepath string
|
||||
// 文件全路径
|
||||
File *bytes.Buffer
|
||||
}
|
||||
|
||||
func PostFile(reqUrl string, reqParams map[string]string, contentType string, files []UploadFile, headers map[string]string) string {
|
||||
requestBody, realContentType := getReader(reqParams, contentType, files)
|
||||
httpRequest, _ := http.NewRequest("POST", reqUrl, requestBody)
|
||||
// 添加请求头
|
||||
httpRequest.Header.Add("Content-Type", realContentType)
|
||||
if headers != nil {
|
||||
for k, v := range headers {
|
||||
httpRequest.Header.Add(k, v)
|
||||
}
|
||||
}
|
||||
httpClient := &http.Client{}
|
||||
// 发送请求
|
||||
resp, err := httpClient.Do(httpRequest)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer func(Body io.ReadCloser) {
|
||||
_ = Body.Close()
|
||||
}(resp.Body)
|
||||
response, _ := io.ReadAll(resp.Body)
|
||||
return string(response)
|
||||
}
|
||||
|
||||
func getReader(reqParams map[string]string, contentType string, files []UploadFile) (io.Reader, string) {
|
||||
if strings.Index(contentType, "json") > -1 {
|
||||
bytesData, _ := json.Marshal(reqParams)
|
||||
return bytes.NewReader(bytesData), contentType
|
||||
} else if files != nil {
|
||||
body := &bytes.Buffer{}
|
||||
// 文件写入 body
|
||||
writer := multipart.NewWriter(body)
|
||||
for _, uploadFile := range files {
|
||||
part, err := writer.CreateFormFile(uploadFile.Name, filepath.Base(uploadFile.Filepath))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
_, err = io.Copy(part, uploadFile.File)
|
||||
}
|
||||
// 其他参数列表写入 body
|
||||
for k, v := range reqParams {
|
||||
if err := writer.WriteField(k, v); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
if err := writer.Close(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
// 上传文件需要自己专用的contentType
|
||||
return body, writer.FormDataContentType()
|
||||
} else {
|
||||
urlValues := url.Values{}
|
||||
for key, val := range reqParams {
|
||||
urlValues.Set(key, val)
|
||||
}
|
||||
reqBody := urlValues.Encode()
|
||||
return strings.NewReader(reqBody), contentType
|
||||
}
|
||||
}
|
|
@ -0,0 +1,216 @@
|
|||
package utils
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"golang.org/x/image/bmp"
|
||||
"golang.org/x/image/tiff"
|
||||
"image"
|
||||
"image/color"
|
||||
"image/draw"
|
||||
"image/jpeg"
|
||||
"image/png"
|
||||
"math"
|
||||
)
|
||||
|
||||
func BuffToImage(in []byte) image.Image {
|
||||
buff := bytes.NewBuffer(in)
|
||||
m, _, _ := image.Decode(buff)
|
||||
return m
|
||||
}
|
||||
|
||||
// Clip 图片裁剪
|
||||
func Clip(buff *bytes.Buffer, wi, hi int, equalProportion bool) (out image.Image, imageType string, err error) {
|
||||
//buff := bytes.NewBuffer(in)
|
||||
m, imgType, err := image.Decode(buff)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
if equalProportion {
|
||||
w := m.Bounds().Max.X
|
||||
h := m.Bounds().Max.Y
|
||||
if w > 0 && h > 0 && wi > 0 && hi > 0 {
|
||||
wi, hi = fixSize(w, h, wi, hi)
|
||||
}
|
||||
}
|
||||
switch imgType {
|
||||
case "jpeg", "jpg":
|
||||
rgbImg := m.(*image.YCbCr)
|
||||
return rgbImg.SubImage(image.Rect(0, 0, wi, hi)), imgType, nil
|
||||
case "bmp":
|
||||
img := m.(*image.RGBA)
|
||||
if equalProportion {
|
||||
|
||||
}
|
||||
return img.SubImage(image.Rect(0, 0, wi, hi)).(*image.RGBA), imageType, nil
|
||||
case "png":
|
||||
switch m.(type) {
|
||||
case *image.NRGBA:
|
||||
img := m.(*image.NRGBA)
|
||||
subImg := img.SubImage(image.Rect(0, 0, wi, hi)).(*image.NRGBA)
|
||||
|
||||
return subImg, imageType, nil
|
||||
case *image.RGBA:
|
||||
img := m.(*image.RGBA)
|
||||
subImg := img.SubImage(image.Rect(0, 0, wi, hi)).(*image.RGBA)
|
||||
return subImg, imageType, nil
|
||||
}
|
||||
case "gif":
|
||||
img := m.(*image.Paletted)
|
||||
subImg := img.SubImage(image.Rect(0, 0, wi, hi)).(*image.Paletted)
|
||||
return subImg, imageType, nil
|
||||
}
|
||||
return nil, "", fmt.Errorf("未知的图片格式")
|
||||
|
||||
}
|
||||
|
||||
func fixSize(img1W, img2H, wi, hi int) (new1W, new2W int) {
|
||||
var ( //为了方便计算,将图片的宽转为 float64
|
||||
imgWidth, imgHeight = float64(img1W), float64(img2H)
|
||||
ratio float64
|
||||
)
|
||||
if imgWidth >= imgHeight {
|
||||
ratio = imgWidth / float64(wi)
|
||||
return int(imgWidth * ratio), int(imgHeight * ratio)
|
||||
}
|
||||
ratio = imgHeight / float64(hi)
|
||||
return int(imgWidth * ratio), int(imgHeight * ratio)
|
||||
}
|
||||
|
||||
func Gray(buff *bytes.Buffer) (out image.Image, err error) {
|
||||
m, _, _ := image.Decode(buff)
|
||||
bounds := m.Bounds()
|
||||
dx := bounds.Dx()
|
||||
dy := bounds.Dy()
|
||||
newRgba := image.NewRGBA(bounds)
|
||||
for i := 0; i < dx; i++ {
|
||||
for j := 0; j < dy; j++ {
|
||||
colorRgb := m.At(i, j)
|
||||
_, g, _, a := colorRgb.RGBA()
|
||||
gUint8 := uint8(g >> 8)
|
||||
aUint8 := uint8(a >> 8)
|
||||
newRgba.SetRGBA(i, j, color.RGBA{R: gUint8, G: gUint8, B: gUint8, A: aUint8})
|
||||
}
|
||||
}
|
||||
r := image.Rect(0, 0, dx, dy)
|
||||
return newRgba.SubImage(r), nil
|
||||
}
|
||||
|
||||
func Rotate90(buff *bytes.Buffer) image.Image {
|
||||
m, _, _ := image.Decode(buff)
|
||||
rotate90 := image.NewRGBA(image.Rect(0, 0, m.Bounds().Dy(), m.Bounds().Dx()))
|
||||
// 矩阵旋转
|
||||
for x := m.Bounds().Min.Y; x < m.Bounds().Max.Y; x++ {
|
||||
for y := m.Bounds().Max.X - 1; y >= m.Bounds().Min.X; y-- {
|
||||
// 设置像素点
|
||||
rotate90.Set(m.Bounds().Max.Y-x, y, m.At(y, x))
|
||||
}
|
||||
}
|
||||
return rotate90
|
||||
}
|
||||
|
||||
// Rotate180 旋转180度
|
||||
func Rotate180(buff *bytes.Buffer) image.Image {
|
||||
m, _, _ := image.Decode(buff)
|
||||
rotate180 := image.NewRGBA(image.Rect(0, 0, m.Bounds().Dx(), m.Bounds().Dy()))
|
||||
// 矩阵旋转
|
||||
for x := m.Bounds().Min.X; x < m.Bounds().Max.X; x++ {
|
||||
for y := m.Bounds().Min.Y; y < m.Bounds().Max.Y; y++ {
|
||||
// 设置像素点
|
||||
rotate180.Set(m.Bounds().Max.X-x, m.Bounds().Max.Y-y, m.At(x, y))
|
||||
}
|
||||
}
|
||||
return rotate180
|
||||
}
|
||||
|
||||
// Rotate270 旋转270度
|
||||
func Rotate270(buff *bytes.Buffer) image.Image {
|
||||
m, _, _ := image.Decode(buff)
|
||||
rotate270 := image.NewRGBA(image.Rect(0, 0, m.Bounds().Dy(), m.Bounds().Dx()))
|
||||
// 矩阵旋转
|
||||
for x := m.Bounds().Min.Y; x < m.Bounds().Max.Y; x++ {
|
||||
for y := m.Bounds().Max.X - 1; y >= m.Bounds().Min.X; y-- {
|
||||
// 设置像素点
|
||||
rotate270.Set(x, m.Bounds().Max.X-y, m.At(y, x))
|
||||
}
|
||||
}
|
||||
return rotate270
|
||||
|
||||
}
|
||||
|
||||
func ImageToBase64(img image.Image, imgType string) string {
|
||||
buff := ImageToBuff(img, imgType)
|
||||
return base64.StdEncoding.EncodeToString(buff.Bytes())
|
||||
}
|
||||
|
||||
func ImageToBuff(img image.Image, imgType string) *bytes.Buffer {
|
||||
buff := bytes.NewBuffer(nil)
|
||||
switch imgType {
|
||||
case "bmp":
|
||||
imgType = "bmp"
|
||||
_ = bmp.Encode(buff, img)
|
||||
case "png":
|
||||
imgType = "png"
|
||||
_ = png.Encode(buff, img)
|
||||
case "tiff":
|
||||
imgType = "tiff"
|
||||
_ = tiff.Encode(buff, img, nil)
|
||||
default:
|
||||
imgType = "jpeg"
|
||||
_ = jpeg.Encode(buff, img, &jpeg.Options{
|
||||
Quality: 100,
|
||||
})
|
||||
}
|
||||
return buff
|
||||
}
|
||||
func SplitImage(buff *bytes.Buffer, w, h int) []image.Image {
|
||||
img, _, _ := image.Decode(buff)
|
||||
list := make([]image.Image, 0)
|
||||
newWidth := int(math.Ceil(float64(img.Bounds().Dx()) / float64(w)))
|
||||
newHeight := int(math.Ceil(float64(img.Bounds().Dy()) / float64(h)))
|
||||
rect := image.Rect(0, 0, newWidth*w, newHeight*h)
|
||||
newImage := image.NewRGBA(rect)
|
||||
black := color.RGBA{A: 255}
|
||||
draw.Draw(newImage, newImage.Bounds(), &image.Uniform{C: black}, image.Point{}, draw.Src)
|
||||
draw.Draw(newImage, img.Bounds(), img, newImage.Bounds().Min, draw.Over)
|
||||
sp := splitPattern(newImage, w, h)
|
||||
spLen := len(sp)
|
||||
var (
|
||||
square image.Rectangle
|
||||
)
|
||||
for i := 0; i < spLen; i++ {
|
||||
square = image.Rect(sp[i][0], sp[i][1], sp[i][2], sp[i][3])
|
||||
imgPart := img.(interface {
|
||||
SubImage(r image.Rectangle) image.Image
|
||||
}).SubImage(square)
|
||||
list = append(list, imgPart)
|
||||
}
|
||||
return list
|
||||
}
|
||||
func splitPattern(img image.Image, w, h int) [][]int {
|
||||
ret := make([][]int, 0)
|
||||
vOffSet := width(img) / w
|
||||
hOffSet := height(img) / h
|
||||
for r := 0; r < hOffSet; r++ {
|
||||
for c := 0; c < vOffSet; c++ {
|
||||
//行偏移,仅应用在x
|
||||
x1 := w * c
|
||||
y1 := h * r
|
||||
|
||||
x2 := w * (c + 1)
|
||||
y2 := (r + 1) * h
|
||||
el := []int{x1, y1, x2, y2}
|
||||
ret = append(ret, el)
|
||||
}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func width(i image.Image) int {
|
||||
return i.Bounds().Max.X - i.Bounds().Min.X
|
||||
}
|
||||
|
||||
func height(i image.Image) int {
|
||||
return i.Bounds().Max.Y - i.Bounds().Min.Y
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
package utils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"git.hpds.cc/Component/network/log"
|
||||
"net"
|
||||
)
|
||||
|
||||
// GetAvailablePort 获取可用端口
|
||||
func GetAvailablePort() (int, error) {
|
||||
address, err := net.ResolveTCPAddr("tcp", fmt.Sprintf("%s:0", "0.0.0.0"))
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
listener, err := net.ListenTCP("tcp", address)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
defer func(listener *net.TCPListener) {
|
||||
_ = listener.Close()
|
||||
}(listener)
|
||||
return listener.Addr().(*net.TCPAddr).Port, nil
|
||||
}
|
||||
|
||||
// IsPortAvailable 判断端口是否可以(未被占用)
|
||||
func IsPortAvailable(port int) bool {
|
||||
address := fmt.Sprintf("%s:%d", "0.0.0.0", port)
|
||||
listener, err := net.Listen("tcp", address)
|
||||
if err != nil {
|
||||
log.Infof("port %s is taken: %s", address, err)
|
||||
return false
|
||||
}
|
||||
|
||||
defer func(listener net.Listener) {
|
||||
_ = listener.Close()
|
||||
}(listener)
|
||||
return true
|
||||
}
|
|
@ -0,0 +1,91 @@
|
|||
package store
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"hpds_annotation/pkg/utils"
|
||||
"io"
|
||||
"os"
|
||||
"path"
|
||||
|
||||
"github.com/klauspost/compress/zstd"
|
||||
)
|
||||
|
||||
func Load(storePath string) map[string]bool {
|
||||
if !utils.PathExists(storePath) {
|
||||
_ = os.MkdirAll(storePath, os.ModePerm)
|
||||
}
|
||||
fileName := "store"
|
||||
storeFile := path.Join(storePath, fmt.Sprintf("%s.hdb", fileName))
|
||||
if !utils.PathExists(storeFile) {
|
||||
NewFile(storeFile)
|
||||
return make(map[string]bool)
|
||||
}
|
||||
list := make(map[string]bool)
|
||||
f, _ := os.OpenFile(storeFile, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
|
||||
defer func(f *os.File) {
|
||||
_ = f.Close()
|
||||
}(f)
|
||||
buff, err := io.ReadAll(f)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return nil
|
||||
}
|
||||
if len(buff) > 0 {
|
||||
str, err := UnCompress(buff)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
err = json.Unmarshal(str, &list)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return list
|
||||
}
|
||||
|
||||
func Save(storePath string, list map[string]bool) {
|
||||
if !utils.PathExists(storePath) {
|
||||
_ = os.MkdirAll(storePath, os.ModePerm)
|
||||
}
|
||||
fileName := "store"
|
||||
storeFile := path.Join(storePath, fmt.Sprintf("%s.hdb", fileName))
|
||||
if !utils.PathExists(storeFile) {
|
||||
NewFile(storeFile)
|
||||
}
|
||||
f, _ := os.OpenFile(storeFile, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)
|
||||
defer func() {
|
||||
_ = f.Close()
|
||||
}()
|
||||
str, _ := json.Marshal(list)
|
||||
c := Compress(str)
|
||||
_, _ = f.Write(c)
|
||||
}
|
||||
func NewFile(fileName string) {
|
||||
f, _ := os.OpenFile(fileName, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0666)
|
||||
defer func(f *os.File) {
|
||||
_ = f.Close()
|
||||
}(f)
|
||||
}
|
||||
|
||||
// Compress 压缩
|
||||
func Compress(src []byte) []byte {
|
||||
encoder, _ := zstd.NewWriter(nil)
|
||||
zstd.WithEncoderConcurrency(3)
|
||||
return encoder.EncodeAll(src, make([]byte, 0, len(src)))
|
||||
}
|
||||
|
||||
func UnCompress(src []byte) ([]byte, error) {
|
||||
d, err := zstd.NewReader(nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer d.Close()
|
||||
|
||||
uncompressed, err := d.DecodeAll(src, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return uncompressed, nil
|
||||
}
|
Loading…
Reference in New Issue