refactor: reorganize modules and add Linux build tooling

This commit is contained in:
2026-08-09 12:41:08 +08:00
parent 86024fa81d
commit 5cc5fd92ed
1461 changed files with 4054 additions and 4724 deletions

View File

@@ -0,0 +1,70 @@
# FTS微服务更新日志
所有FTS微服务的重要更改都将记录在此文件中。
格式基于 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.0.0/)
并且此项目遵循 [语义版本控制](https://semver.org/lang/zh-CN/)。
## [v2.0.0] - 2024-10-02
### 重大更新 🚀
- **升级Go版本到1.25.1** - 使用最新Go版本获得更好的性能和特性
- **现代化项目架构** - 参考最佳实践重构项目结构
- **移除gRPC依赖** - 简化架构专注HTTP API服务
### 新增功能 ✨
- **现代化构建工具链**
- 添加完整的Makefile支持多平台构建
- Docker容器化支持包含优化的多阶段构建
- docker-compose完整开发环境配置
- **增强的错误处理系统**
- 统一的业务错误定义和处理
- 标准化的响应格式
- 详细的错误码分类
- **完善的健康检查机制**
- 详细健康检查端点(/health
- 简单健康检查端点(/health/simple
- 系统资源监控
- 依赖服务状态检查
- **优雅关闭机制** - 支持服务的优雅启动和关闭
- **版本信息端点** - /version接口显示构建信息
### 开发体验改进 🛠️
- **开发工具支持**
- golangci-lint代码检查
- gosec安全扫描
- 代码覆盖率测试
- 自动化格式化
- **完整的项目文档** - 详细的README和使用指南
- **标准化项目结构** - 清晰的目录组织
### 技术升级 📦
- 升级bsm-sdk/core到v0.0.88
- 保持所有核心依赖的最新兼容版本
- 优化依赖管理,移除不必要的包
### 性能优化 ⚡
- 改进的配置加载机制
- 更好的资源管理
- 优化的HTTP服务器配置
### 安全增强 🔒
- 增强的错误处理,避免信息泄露
- 更安全的依赖配置
- 标准化的认证和授权准备
### 运维支持 📊
- 详细的健康检查
- 结构化日志支持
- 监控友好的端点设计
- 容器化部署支持
## [v1.0.0] - 2024-xx-xx
### 新增
- 初始版本发布
- 文件传输服务核心功能
- OSS存储支持
- 本地存储支持
- 文件上传接口
- 健康检查接口

View File

@@ -0,0 +1,59 @@
# 构建阶段
FROM golang:1.26.5-alpine AS builder
# 设置工作目录
WORKDIR /app
# 安装必要的工具
RUN apk add --no-cache git ca-certificates tzdata
# 设置Go环境变量
ENV GO111MODULE=on \
GOPROXY=https://goproxy.cn,direct \
GOPRIVATE=git.apinb.com/* \
GONOPROXY=git.apinb.com/* \
GOINSECURE=git.apinb.com/* \
GONOSUMDB=git.apinb.com/* \
GOEXPERIMENT=jsonv2
# 复制go模块文件
COPY go.mod go.sum ./
# 下载依赖
RUN go mod download
# 复制源代码
COPY . .
# 构建应用
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -ldflags '-w -s' -o fts ./cmd/main/main.go
# 运行阶段
FROM alpine:latest
# 设置时区
RUN apk --no-cache add ca-certificates tzdata \
&& cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \
&& echo "Asia/Shanghai" > /etc/timezone \
&& apk del tzdata
WORKDIR /root/
# 从构建阶段复制二进制文件
COPY --from=builder /app/fts .
# 复制配置文件
COPY --from=builder /app/etc ./etc
# 创建日志目录
RUN mkdir -p /var/log/fts
# 暴露端口
EXPOSE 8080
# 设置健康检查
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD curl -f http://localhost:8080/ || exit 1
# 运行应用
CMD ["./fts"]

167
module/base/fts/Makefile Normal file
View File

@@ -0,0 +1,167 @@
# FTS Service Makefile
# 变量定义
APP_NAME := fts
VERSION := $(shell git describe --tags --always --dirty 2>/dev/null || echo "v1.0.0")
BUILD_TIME := $(shell date +%Y-%m-%d\ %H:%M:%S)
GIT_COMMIT := $(shell git rev-parse HEAD 2>/dev/null || echo "unknown")
GO_VERSION := $(shell go version | awk '{print $$3}')
# 构建标志
LDFLAGS := -ldflags "-X main.Version=$(VERSION) -X main.BuildTime=$(BUILD_TIME) -X main.GitCommit=$(GIT_COMMIT) -w -s"
BUILD_FLAGS := -trimpath $(LDFLAGS)
# 目录
BUILD_DIR := build
# Go环境变量
export GO111MODULE=on
export GOPROXY=https://goproxy.cn,direct
export GOPRIVATE=git.apinb.com/*
export GONOPROXY=git.apinb.com/*
export GOINSECURE=git.apinb.com/*
export GONOSUMDB=git.apinb.com/*
export GOEXPERIMENT=jsonv2
.PHONY: help
help: ## 显示帮助信息
@echo "FTS Service - 可用的命令:"
@echo ""
@awk 'BEGIN {FS = ":.*?## "} /^[a-zA-Z_-]+:.*?## / {printf " \033[36m%-15s\033[0m %s\n", $$1, $$2}' $(MAKEFILE_LIST)
.PHONY: deps
deps: ## 下载依赖
@echo "下载Go依赖..."
go mod download
go mod tidy
.PHONY: build
build: deps ## 构建应用
@echo "构建应用..."
@mkdir -p $(BUILD_DIR)
CGO_ENABLED=0 go build $(BUILD_FLAGS) -o $(BUILD_DIR)/$(APP_NAME) ./cmd/main/main.go
@echo "构建完成: $(BUILD_DIR)/$(APP_NAME)"
.PHONY: build-linux
build-linux: deps ## 构建Linux版本
@echo "构建Linux版本..."
@mkdir -p $(BUILD_DIR)
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build $(BUILD_FLAGS) -o $(BUILD_DIR)/$(APP_NAME)-linux-amd64 ./cmd/main/main.go
@echo "构建完成: $(BUILD_DIR)/$(APP_NAME)-linux-amd64"
.PHONY: build-windows
build-windows: deps ## 构建Windows版本
@echo "构建Windows版本..."
@mkdir -p $(BUILD_DIR)
CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build $(BUILD_FLAGS) -o $(BUILD_DIR)/$(APP_NAME)-windows-amd64.exe ./cmd/main/main.go
@echo "构建完成: $(BUILD_DIR)/$(APP_NAME)-windows-amd64.exe"
.PHONY: build-darwin
build-darwin: deps ## 构建macOS版本
@echo "构建macOS版本..."
@mkdir -p $(BUILD_DIR)
CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build $(BUILD_FLAGS) -o $(BUILD_DIR)/$(APP_NAME)-darwin-amd64 ./cmd/main/main.go
@echo "构建完成: $(BUILD_DIR)/$(APP_NAME)-darwin-amd64"
.PHONY: build-all
build-all: build-linux build-windows build-darwin ## 构建所有平台版本
.PHONY: run
run: build ## 运行应用
@echo "启动应用..."
./$(BUILD_DIR)/$(APP_NAME)
.PHONY: dev
dev: ## 开发模式运行
@echo "开发模式启动..."
go run ./cmd/main/main.go
.PHONY: test
test: ## 运行测试
@echo "运行测试..."
go test -v ./...
.PHONY: test-coverage
test-coverage: ## 运行测试并生成覆盖率报告
@echo "运行测试覆盖率..."
go test -v -coverprofile=coverage.out ./...
go tool cover -html=coverage.out -o coverage.html
@echo "覆盖率报告生成: coverage.html"
.PHONY: lint
lint: ## 运行代码检查
@echo "运行代码检查..."
@if command -v golangci-lint >/dev/null 2>&1; then \
golangci-lint run; \
else \
echo "golangci-lint 未安装使用go vet..."; \
go vet ./...; \
fi
.PHONY: fmt
fmt: ## 格式化代码
@echo "格式化代码..."
go fmt ./...
@if command -v goimports >/dev/null 2>&1; then \
goimports -w .; \
fi
.PHONY: security
security: ## 安全检查
@echo "运行安全检查..."
@if command -v gosec >/dev/null 2>&1; then \
gosec ./...; \
else \
echo "gosec 未安装,请运行: go install github.com/securego/gosec/v2/cmd/gosec@latest"; \
fi
.PHONY: docker-build
docker-build: ## 构建Docker镜像
@echo "构建Docker镜像..."
docker build -t $(APP_NAME):$(VERSION) .
docker tag $(APP_NAME):$(VERSION) $(APP_NAME):latest
.PHONY: docker-run
docker-run: ## 运行Docker容器
@echo "运行Docker容器..."
docker run -d --name $(APP_NAME) -p 8080:8080 $(APP_NAME):latest
.PHONY: docker-stop
docker-stop: ## 停止Docker容器
@echo "停止Docker容器..."
docker stop $(APP_NAME) || true
docker rm $(APP_NAME) || true
.PHONY: clean
clean: ## 清理构建文件
@echo "清理构建文件..."
rm -rf $(BUILD_DIR)
rm -f coverage.out coverage.html
docker system prune -f
.PHONY: install-tools
install-tools: ## 安装开发工具
@echo "安装开发工具..."
go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest
go install golang.org/x/tools/cmd/goimports@latest
go install github.com/securego/gosec/v2/cmd/gosec@latest
.PHONY: mod-update
mod-update: ## 更新依赖
@echo "更新依赖..."
go get -u ./...
go mod tidy
.PHONY: version
version: ## 显示版本信息
@echo "应用名称: $(APP_NAME)"
@echo "版本: $(VERSION)"
@echo "构建时间: $(BUILD_TIME)"
@echo "Git提交: $(GIT_COMMIT)"
@echo "Go版本: $(GO_VERSION)"
.PHONY: all
all: clean deps build test lint ## 执行完整的构建流程
# 默认目标
.DEFAULT_GOAL := help

216
module/base/fts/README.md Normal file
View File

@@ -0,0 +1,216 @@
# FTS - 文件传输服务
基于Go 1.26.5和Gin框架的现代化文件传输微服务
## 特性
- 🚀 **现代化架构**: 使用Go 1.26.5最新特性
- 📁 **多存储支持**: 支持本地存储和MinIO对象存储
- 🔧 **配置灵活**: 支持YAML配置文件环境变量覆盖
- 🏥 **健康检查**: 完整的健康检查和监控端点
- 🔒 **安全性**: 文件类型检查大小限制JWT认证支持
- 📊 **可观测性**: 完整的日志记录和错误追踪
- 🐳 **容器化**: Docker和docker-compose开箱即用
- 🛠️ **开发友好**: 完整的Makefile工具链
## 快速开始
### 环境要求
- Go 1.26.5
- PostgreSQL / MySQL (可选)
- Redis (可选)
- MinIO (可选,用于对象存储)
### 安装依赖
```bash
make deps
```
### 运行服务
```bash
# 开发模式
make dev
# 或构建后运行
make build
make run
```
### 使用Docker
```bash
# 启动完整环境包括数据库、Redis等
docker-compose up -d
# 仅构建应用镜像
make docker-build
```
## API文档
### 健康检查
```http
GET /health #
GET /health/simple #
GET /version #
```
### 文件操作
```http
POST /fts/upload #
GET /fts/download #
```
## 配置说明
主要配置文件:`etc/fts_dev.yaml`
```yaml
Service: fts
Host: 0.0.0.0
Port: 16290
# 数据库配置
Databases:
Driver: postgres
Source:
- "postgres://user:pass@localhost/fts?sslmode=disable"
# 缓存配置
Cache: redis://localhost:6379/0
# 对象存储配置
MinioOss:
Site: http://localhost:9000
Endpoint: localhost:9000
AccessKeyId: minioadmin
AccessKeySecret: minioadmin
UseSSL: false
# 本地存储配置
Local:
Site: http://localhost:16290
UploadDir: ./uploads/
# FTS特定配置
FtsConfig:
InputKey: file
MaxSize: 5368709120 # 5GB
Allows:
- .jpg
- .png
- .pdf
- .doc
# ... 更多支持的文件类型
```
## 开发工具
```bash
# 查看所有可用命令
make help
# 代码检查
make lint
# 运行测试
make test
# 安全扫描
make security
# 格式化代码
make fmt
# 更新依赖
make mod-update
```
## 构建和部署
```bash
# 构建所有平台
make build-all
# 构建Linux版本
make build-linux
# 构建Windows版本
make build-windows
# 构建macOS版本
make build-darwin
```
## 监控和运维
### 健康检查端点
- `GET /health` - 返回详细的服务状态包括数据库、Redis、ETCD连接状态
- `GET /health/simple` - 返回简单的服务状态
- `HEAD /` - 快速健康检查
### 日志记录
服务支持结构化日志记录,日志级别可通过配置调整。
### 错误处理
统一的错误响应格式:
```json
{
"code": 10001,
"message": "错误描述",
"trace_id": "request-trace-id"
}
```
## 项目结构
```
fts/
├── cmd/
│ ├── cli/ # CLI工具
│ └── main/ # 主服务入口
├── internal/
│ ├── config/ # 配置管理
│ ├── errors/ # 错误定义
│ ├── health/ # 健康检查
│ ├── impl/ # 服务实现
│ ├── logic/ # 业务逻辑
│ ├── models/ # 数据模型
│ ├── response/ # 响应处理
│ └── routers/ # 路由定义
├── etc/ # 配置文件
├── scripts/ # 脚本文件
├── test/ # 测试文件
├── build/ # 构建输出
├── Dockerfile # Docker镜像构建
├── docker-compose.yml # Docker编排
├── Makefile # 构建工具
└── README.md # 项目文档
```
## 贡献指南
1. Fork 项目
2. 创建特性分支 (`git checkout -b feature/AmazingFeature`)
3. 提交更改 (`git commit -m 'Add some AmazingFeature'`)
4. 推送到分支 (`git push origin feature/AmazingFeature`)
5. 打开 Pull Request
## 许可证
此项目采用 MIT 许可证 - 查看 [LICENSE](LICENSE) 文件了解详情。
## 联系方式
- 项目主页: [GitHub仓库地址]
- 问题反馈: [Issues页面]
- 邮箱: [team@example.com]

View File

@@ -0,0 +1,18 @@
package main
import (
"git.apinb.com/bsm-sdk/core/licence"
"git.apinb.com/bsm-sdk/core/utils"
)
// go env -w CGO_ENABLED=0
// go env -w GOOS=linux
// go env -w GOARCH=amd64
// go build -o generateLicence ./cmd/cli/
func main() {
data, err := licence.BuildLicence("slcor", 20250101, 20350101, []string{"bsm", licence.GetMachineCode()}, nil)
if err != nil {
panic(err)
}
utils.StringToFile("/data/app/etc/licence.key", data)
}

View File

@@ -0,0 +1,50 @@
package service
import (
"fmt"
"bsm/full/module/base/fts/internal/config"
"bsm/full/module/base/fts/internal/impl"
"bsm/full/module/base/fts/internal/routers"
"git.apinb.com/bsm-sdk/core/infra"
"git.apinb.com/bsm-sdk/core/middleware"
"github.com/gin-gonic/gin"
)
var (
ServiceKey = "fts"
)
func Run() {
config.New(ServiceKey)
impl.NewImpl()
// 初始化Gin引擎
app := gin.Default()
// gin 中间件
app.Use(middleware.Cors())
app.Use(gin.Recovery())
middleware.Mode(app)
if config.Spec.OnMicroService {
// register service
infra.Service.Register(impl.EtcdService, ServiceKey, config.Spec.Port)
}
// register health check
app.HEAD("/", infra.Health)
// register routers
routers.Register(ServiceKey, app)
// start
err := app.Run(fmt.Sprintf(":%s", config.Spec.Port))
if err != nil {
panic(err)
}
}
func main() {
Run()
}

View File

@@ -0,0 +1,95 @@
version: '3.8'
services:
fts:
build: .
container_name: fts-service
ports:
- "8080:8080"
environment:
- ENV=dev
- GIN_MODE=release
volumes:
- ./etc:/root/etc:ro
- ./logs:/var/log/fts
depends_on:
- postgres
- redis
- etcd
networks:
- fts-network
restart: unless-stopped
postgres:
image: postgres:15-alpine
container_name: fts-postgres
environment:
POSTGRES_DB: fts
POSTGRES_USER: fts
POSTGRES_PASSWORD: fts123456
ports:
- "5432:5432"
volumes:
- postgres_data:/var/lib/postgresql/data
networks:
- fts-network
restart: unless-stopped
redis:
image: redis:7-alpine
container_name: fts-redis
ports:
- "6379:6379"
volumes:
- redis_data:/data
networks:
- fts-network
restart: unless-stopped
etcd:
image: quay.io/coreos/etcd:v3.5.9
container_name: fts-etcd
environment:
- ETCD_NAME=etcd-server
- ETCD_DATA_DIR=/etcd-data
- ETCD_LISTEN_CLIENT_URLS=http://0.0.0.0:2379
- ETCD_ADVERTISE_CLIENT_URLS=http://0.0.0.0:2379
- ETCD_LISTEN_PEER_URLS=http://0.0.0.0:2380
- ETCD_INITIAL_ADVERTISE_PEER_URLS=http://0.0.0.0:2380
- ETCD_INITIAL_CLUSTER=etcd-server=http://0.0.0.0:2380
- ETCD_INITIAL_CLUSTER_TOKEN=etcd-cluster
- ETCD_INITIAL_CLUSTER_STATE=new
ports:
- "2379:2379"
- "2380:2380"
volumes:
- etcd_data:/etcd-data
networks:
- fts-network
restart: unless-stopped
minio:
image: minio/minio:latest
container_name: fts-minio
command: server /data --console-address ":9001"
environment:
MINIO_ROOT_USER: admin
MINIO_ROOT_PASSWORD: admin123456
ports:
- "9000:9000"
- "9001:9001"
volumes:
- minio_data:/data
networks:
- fts-network
restart: unless-stopped
volumes:
postgres_data:
redis_data:
etcd_data:
minio_data:
networks:
fts-network:
driver: bridge

View File

@@ -0,0 +1,77 @@
Service: fts
Host: 0.0.0.0
Port: 16290
Databases:
Driver: postgres
Source:
- host=47.109.77.183 user=postgres password=CHANGE_ME dbname=bsm_dev port=5432 sslmode=disable TimeZone=Asia/Shanghai
# cache DB的选择请在后面直接带参数不带会自动HASH计算选择DB库。
Cache: redis://null:CHANGE_ME@47.109.77.183:6379/0
# 是否启用微服务模式
OnMicroService: false
# 微服务调用密钥
SecretKey: 6ad9529688041483f458f8de11ed16ff
MinioOss:
Site: http://oss.make-w.com
Endpoint: fts.apinb.com:9000
AccessKeyId: 9jtPPB7wwJpe5R2164bS
AccessKeySecret: CHANGE_ME
UseSSL: false
Local:
Site: http://files.apinb.com
UploadDir: ./uploader/
FtsConfig:
InputKey: file
MaxSize: 5368709120 # MB
Allows:
- .doc
- .docx
- .txt
- .png
- .jpg
- .jpeg
- .gif
- .pdf
- .xls
- .xlsx
- .ppt
- .pptx
- .mp4
- .avi
- .mkv
- .webp
- .mov
- .wmv
- .mp3
- .wav
# # 日志记录
# Log:
# ServiceName: {ServiceKey}
# Mode: file
# Path: logs/{ServiceKey}
# Stat: false
# # 性能监控
# Prometheus:
# Host: 127.0.0.1
# Port: 22214
# Path: /metrics
# 链路追踪
# Telemetry:
# Name: {Workspace}.{ServiceKey}.{RuntimeMode}
# Endpoint: http://127.0.0.1:14268/api/traces
# Sampler: 1.0
# Batcher: jaeger

View File

@@ -0,0 +1,77 @@
Service: fts
Host: 0.0.0.0
Port: 16290
Databases:
Driver: postgres
Source:
- host=47.109.77.183 user=postgres password=CHANGE_ME dbname=bsm_dev port=5432 sslmode=disable TimeZone=Asia/Shanghai
# cache DB的选择请在后面直接带参数不带会自动HASH计算选择DB库。
Cache: redis://null:CHANGE_ME@47.109.77.183:6379/0
# 是否启用微服务模式
OnMicroService: false
# 微服务调用密钥
SecretKey: 6ad9529688041483f458f8de11ed16ff
MinioOss:
Site: http://oss.make-w.com
Endpoint: fts.apinb.com:9000
AccessKeyId: 9jtPPB7wwJpe5R2164bS
AccessKeySecret: CHANGE_ME
UseSSL: false
Local:
Site: http://files.apinb.com
UploadDir: ./uploader/
FtsConfig:
InputKey: file
MaxSize: 5368709120 # MB
Allows:
- .doc
- .docx
- .txt
- .png
- .jpg
- .jpeg
- .gif
- .pdf
- .xls
- .xlsx
- .ppt
- .pptx
- .mp4
- .avi
- .mkv
- .webp
- .mov
- .wmv
- .mp3
- .wav
# # 日志记录
# Log:
# ServiceName: {ServiceKey}
# Mode: file
# Path: logs/{ServiceKey}
# Stat: false
# # 性能监控
# Prometheus:
# Host: 127.0.0.1
# Port: 22214
# Path: /metrics
# 链路追踪
# Telemetry:
# Name: {Workspace}.{ServiceKey}.{RuntimeMode}
# Endpoint: http://127.0.0.1:14268/api/traces
# Sampler: 1.0
# Batcher: jaeger

View File

@@ -0,0 +1,77 @@
Service: fts
Host: 0.0.0.0
Port: 16290
Databases:
Driver: postgres
Source:
- host=47.109.77.183 user=postgres password=CHANGE_ME dbname=bsm_dev port=5432 sslmode=disable TimeZone=Asia/Shanghai
# cache DB的选择请在后面直接带参数不带会自动HASH计算选择DB库。
Cache: redis://null:CHANGE_ME@47.109.77.183:6379/0
# 是否启用微服务模式
OnMicroService: false
# 微服务调用密钥
SecretKey: 6ad9529688041483f458f8de11ed16ff
MinioOss:
Site: http://oss.make-w.com
Endpoint: fts.apinb.com:9000
AccessKeyId: 9jtPPB7wwJpe5R2164bS
AccessKeySecret: CHANGE_ME
UseSSL: false
Local:
Site: http://files.apinb.com
UploadDir: ./uploader/
FtsConfig:
InputKey: file
MaxSize: 5368709120 # MB
Allows:
- .doc
- .docx
- .txt
- .png
- .jpg
- .jpeg
- .gif
- .pdf
- .xls
- .xlsx
- .ppt
- .pptx
- .mp4
- .avi
- .mkv
- .webp
- .mov
- .wmv
- .mp3
- .wav
# 日志记录
Log:
ServiceName: {ServiceKey}
Mode: file
Path: logs/{ServiceKey}
Stat: false
# 性能监控
Prometheus:
Host: 127.0.0.1
Port: 22214
Path: /metrics
# 链路追踪
# Telemetry:
# Name: {Workspace}.{ServiceKey}.{RuntimeMode}
# Endpoint: http://127.0.0.1:14268/api/traces
# Sampler: 1.0
# Batcher: jaeger

View File

@@ -0,0 +1,8 @@
[program:bsm-apps-fts]
command=/data/app/bsm-apps-fts
directory=/data/app
autostart=true
autorestart=true
user=root
redirect_stderr=true
stdout_logfile=/data/app/logs/bsm-apps-fts.log

110
module/base/fts/go.mod Normal file
View File

@@ -0,0 +1,110 @@
module bsm/full/module/base/fts
go 1.26.5
require (
git.apinb.com/bsm-sdk/core v0.2.0
github.com/gin-gonic/gin v1.12.0
github.com/minio/minio-go/v7 v7.0.95
github.com/patrickmn/go-cache v2.1.0+incompatible
go.etcd.io/etcd/client/v3 v3.7.1
gorm.io/gorm v1.31.2
)
require (
filippo.io/edwards25519 v1.2.0 // indirect
github.com/KyleBanks/depth v1.2.1 // indirect
github.com/bytedance/gopkg v0.1.4 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/go-openapi/jsonpointer v1.0.0 // indirect
github.com/go-openapi/jsonreference v1.0.0 // indirect
github.com/go-openapi/spec v0.22.9 // indirect
github.com/go-openapi/swag/conv v0.28.0 // indirect
github.com/go-openapi/swag/jsonutils v0.28.0 // indirect
github.com/go-openapi/swag/loading v0.28.0 // indirect
github.com/go-openapi/swag/pools v0.28.0 // indirect
github.com/go-openapi/swag/stringutils v0.28.0 // indirect
github.com/go-openapi/swag/typeutils v0.28.0 // indirect
github.com/go-openapi/swag/yamlutils v0.28.0 // indirect
github.com/go-sql-driver/mysql v1.10.0 // indirect
github.com/goccy/go-yaml v1.19.2 // indirect
github.com/golang-jwt/jwt/v5 v5.3.1 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.30.0 // indirect
github.com/klauspost/compress v1.19.2 // indirect
github.com/minio/crc64nvme v1.1.1 // indirect
github.com/philhofer/fwd v1.2.0 // indirect
github.com/quic-go/qpack v0.6.0 // indirect
github.com/quic-go/quic-go v0.61.0 // indirect
github.com/rs/xid v1.6.0 // indirect
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e // indirect
github.com/swaggo/swag v1.16.6 // indirect
github.com/tinylib/msgp v1.4.0 // indirect
go.etcd.io/etcd/client/pkg/v3 v3.7.1 // indirect
go.mongodb.org/mongo-driver/v2 v2.8.0 // indirect
go.uber.org/atomic v1.11.0 // indirect
go.yaml.in/yaml/v3 v3.0.5 // indirect
golang.org/x/mod v0.37.0 // indirect
golang.org/x/tools v0.47.0 // indirect
google.golang.org/grpc v1.83.0 // indirect
)
require (
github.com/bytedance/sonic v1.15.2 // indirect
github.com/bytedance/sonic/loader v0.5.2 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/cloudwego/base64x v0.1.7 // indirect
github.com/coreos/go-semver v0.3.1 // indirect
github.com/coreos/go-systemd/v22 v22.7.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.15 // indirect
github.com/gin-contrib/cors v1.7.7 // indirect
github.com/gin-contrib/sse v1.1.1 // indirect
github.com/go-ini/ini v1.67.0 // indirect
github.com/go-ole/go-ole v1.3.0 // 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.30.3 // indirect
github.com/goccy/go-json v0.10.6 // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/jackc/pgpassfile v1.0.0 // indirect
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
github.com/jackc/pgx/v5 v5.10.0 // indirect
github.com/jackc/puddle/v2 v2.2.2 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/cpuid/v2 v2.4.0 // indirect
github.com/leodido/go-urn v1.5.0 // indirect
github.com/mattn/go-isatty v0.0.24 // indirect
github.com/minio/md5-simd v1.1.2 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/oklog/ulid/v2 v2.1.2 // indirect
github.com/pelletier/go-toml/v2 v2.4.3 // indirect
github.com/redis/go-redis/v9 v9.22.0 // indirect
github.com/shirou/gopsutil v3.21.11+incompatible // indirect
github.com/swaggo/files v1.0.1
github.com/swaggo/gin-swagger v1.6.1
github.com/tklauser/go-sysconf v0.4.0 // indirect
github.com/tklauser/numcpus v0.12.0 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.3.2 // indirect
github.com/yusufpapurcu/wmi v1.2.4 // indirect
go.etcd.io/etcd/api/v3 v3.7.1 // indirect
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.28.0 // indirect
golang.org/x/arch v0.30.0 // indirect
golang.org/x/crypto v0.54.0 // indirect
golang.org/x/net v0.57.0 // indirect
golang.org/x/sync v0.22.0 // indirect
golang.org/x/sys v0.47.0 // indirect
golang.org/x/text v0.40.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20260807164820-c8921c73eeea // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20260807164820-c8921c73eeea // indirect
google.golang.org/protobuf v1.36.11 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
gorm.io/driver/mysql v1.6.0 // indirect
gorm.io/driver/postgres v1.6.2 // indirect
)
replace git.apinb.com/bsm-sdk/core => D:/work/bsm-sdk/core

300
module/base/fts/go.sum Normal file
View File

@@ -0,0 +1,300 @@
filippo.io/edwards25519 v1.2.0 h1:crnVqOiS4jqYleHd9vaKZ+HKtHfllngJIiOpNpoJsjo=
filippo.io/edwards25519 v1.2.0/go.mod h1:xzAOLCNug/yB62zG1bQ8uziwrIqIuxhctzJT18Q77mc=
github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc=
github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE=
github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs=
github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c=
github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
github.com/bytedance/gopkg v0.1.4 h1:oZnQwnX82KAIWb7033bEwtxvTqXcYMxDBaQxo5JJHWM=
github.com/bytedance/gopkg v0.1.4/go.mod h1:v1zWfPm21Fb+OsyXN2VAHdL6TBb2L88anLQgdyje6R4=
github.com/bytedance/sonic v1.15.2 h1:90H+rcF/FwLXwfB1cudOLq/je83n683Utf4Cbp0xHCo=
github.com/bytedance/sonic v1.15.2/go.mod h1:mT2NbXunuaEbnZ+mRIX/vYqKISmgEuHFDI4UzmKx2SA=
github.com/bytedance/sonic/loader v0.5.2 h1:0QtP1gevc1OZ6/H8Lb9BRZiCXd1Ftjd3OKuj1T1lBIo=
github.com/bytedance/sonic/loader v0.5.2/go.mod h1:AR4NYCk5DdzZizZ5djGqQ92eEhCCcdf5x77udYiSJRo=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cloudwego/base64x v0.1.7 h1:NppS+Fgzg5ovhn4NkUXaDT3x9jldgH5ToMCqzBSi2zI=
github.com/cloudwego/base64x v0.1.7/go.mod h1:Cu1PV9zfrSf7ET2tIbWbbEy7jO7HHJ13q4X2SQ8aWYg=
github.com/coreos/go-semver v0.3.1 h1:yi21YpKnrx1gt5R+la8n5WgS0kCrsPp33dmEyHReZr4=
github.com/coreos/go-semver v0.3.1/go.mod h1:irMmmIw/7yzSRPWryHsK7EYSg09caPQL03VsM8rvUec=
github.com/coreos/go-systemd/v22 v22.7.0 h1:LAEzFkke61DFROc7zNLX/WA2i5J8gYqe0rSj9KI28KA=
github.com/coreos/go-systemd/v22 v22.7.0/go.mod h1:xNUYtjHu2EDXbsxz1i41wouACIwT7Ybq9o0BQhMwD0w=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/gabriel-vasile/mimetype v1.4.15 h1:05iP/CYtZ/w455R/KZM6rZ5ieAdh99UPtd+d3YzLmaI=
github.com/gabriel-vasile/mimetype v1.4.15/go.mod h1:azpTcoLcDZRNgFou5j+APrqQx9HqVPWa6ijYQIIVswQ=
github.com/gin-contrib/cors v1.7.7 h1:Oh9joP463x7Mw72vhvJ61YQm8ODh9b04YR7vsOErD0Q=
github.com/gin-contrib/cors v1.7.7/go.mod h1:K5tW0RkzJtWSiOdikXloy8VEZlgdVNpHNw8FpjUPNrE=
github.com/gin-contrib/gzip v0.0.6 h1:NjcunTcGAj5CO1gn4N8jHOSIeRFHIbn51z6K+xaN4d4=
github.com/gin-contrib/gzip v0.0.6/go.mod h1:QOJlmV2xmayAjkNS2Y8NQsMneuRShOU/kjovCXNuzzk=
github.com/gin-contrib/sse v1.1.1 h1:uGYpNwTacv5R68bSGMapo62iLTRa9l5zxGCps4hK6ko=
github.com/gin-contrib/sse v1.1.1/go.mod h1:QXzuVkA0YO7o/gun03UI1Q+FTI8ZV/n5t03kIQAI89s=
github.com/gin-gonic/gin v1.12.0 h1:b3YAbrZtnf8N//yjKeU2+MQsh2mY5htkZidOM7O0wG8=
github.com/gin-gonic/gin v1.12.0/go.mod h1:VxccKfsSllpKshkBWgVgRniFFAzFb9csfngsqANjnLc=
github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A=
github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
github.com/go-logr/logr v1.4.4 h1:tG4xh9yMsRCAiodLVTxyrkzSZ9+o0L1Kg/+cPVcbP/8=
github.com/go-logr/logr v1.4.4/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
github.com/go-openapi/jsonpointer v1.0.0 h1:kR9tHqY0CtZaOPVFm622dPVNhrvYpwr4uCxgL3h1H8s=
github.com/go-openapi/jsonpointer v1.0.0/go.mod h1:Z3rw7dWu1p9IgitXCFamSlA5lmDiklEB6vkaxcNZW5Y=
github.com/go-openapi/jsonreference v1.0.0 h1:jlmTr6torcd1YgDQvSfNmRtKzYDO4FGBkrAdlAVWnpY=
github.com/go-openapi/jsonreference v1.0.0/go.mod h1:jtwdyGbJk0Xhe5Y+rwtglQP6Sb1WZST4rT32LWB+sv0=
github.com/go-openapi/spec v0.22.9 h1:/vKIFDcGKp0ktZWGbym/tJEWbk6/XOEmAVU0kqKMH+w=
github.com/go-openapi/spec v0.22.9/go.mod h1:b/mNUYIOQOyIiUzUzXEE8xzyZqf93KvM9hQGP91yfl0=
github.com/go-openapi/swag v0.28.0 h1:xkgbOSKj6DZziNpyqRRAOt3GJGtgjgsd2RoyT30VWuw=
github.com/go-openapi/swag/conv v0.28.0 h1:GtqqbyFe7vR5Y7ehxG9W6/OvrSFdf1OLeTGp40TqxH8=
github.com/go-openapi/swag/conv v0.28.0/go.mod h1:mbUE+mzctnhxi864m0Q07SpN8OowD9JhxmxuYvZZD/k=
github.com/go-openapi/swag/jsonutils v0.28.0 h1:YIch6FwO7RXzeAnbO8Tu7dWBZeUEH+4nA0HXltVTnv4=
github.com/go-openapi/swag/jsonutils v0.28.0/go.mod h1:CYM3WlTUcagR2ZoHdz54di/cbBqt82tuxuXgAjxw+mg=
github.com/go-openapi/swag/jsonutils/fixtures_test v0.28.0 h1:qV+VVUAx5Oro8WjVWpZeql7YReTKhT4smR4zhcOQZr0=
github.com/go-openapi/swag/jsonutils/fixtures_test v0.28.0/go.mod h1:mofwUWx70wvskwESqRJ//k/9kURmCgyJl5m5Ppoh5kY=
github.com/go-openapi/swag/loading v0.28.0 h1:td8QZdZC9MIYGGSnSPKShKiK22I2tU5UQvuUhIBPRLU=
github.com/go-openapi/swag/loading v0.28.0/go.mod h1:rXB0QiQX5mMveXEA7ouM4KiiM9jVJe4K6BVbwhD1M4k=
github.com/go-openapi/swag/pools v0.28.0 h1:HPMZWSAfce3rdVTFcjFiCIBtDg9h4x2QlRrHipwhxeU=
github.com/go-openapi/swag/pools v0.28.0/go.mod h1:kVQefhSK5RWuRe7BXsL8htgBPAMpN7HDGpGEknqugeE=
github.com/go-openapi/swag/stringutils v0.28.0 h1:ixsc9iYgDPubHL/8nSkbnryEHpD2VRlBMLKpQyPXcDU=
github.com/go-openapi/swag/stringutils v0.28.0/go.mod h1:lzRN95CxXmA03XcDWHLOb6nOMcxCqR5rGY0lOgsfRoM=
github.com/go-openapi/swag/typeutils v0.28.0 h1:nRBKSBXjDgf01VDPB3fWeD9nQuhCOVeIYAkUx2tbkyY=
github.com/go-openapi/swag/typeutils v0.28.0/go.mod h1:Srm0xFNRZ1Y+vCxJclo5qzx8aj+1pAKda/YfFPrG0dQ=
github.com/go-openapi/swag/yamlutils v0.28.0 h1:TV3JXH6DS46KUroDtMLAYHGkdWf5VDq3wVWFirmzROY=
github.com/go-openapi/swag/yamlutils v0.28.0/go.mod h1:x0q/yndZHEgk9Rx3DyDqzFUmHy55KTvIZldvF2dTJXs=
github.com/go-openapi/testify/enable/yaml/v2 v2.6.0 h1:gGHwAJ0R/5jU8BEGDbfRNR3hL68dAVi84WuOApp29B0=
github.com/go-openapi/testify/enable/yaml/v2 v2.6.0/go.mod h1:tY+St1SGq4NFl0QIqdTY4aEdbChAHxhyB77XQi9iJCo=
github.com/go-openapi/testify/v2 v2.6.0 h1:5PKH2HE7YJ/LuRPQGvSxBRlFXNQhSetBLlGAgUEu3ug=
github.com/go-openapi/testify/v2 v2.6.0/go.mod h1:SgsVHtfooshd0tublTtJ50FPKhujf47YRqauXXOUxfw=
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
github.com/go-playground/validator/v10 v10.30.3 h1:4MU6YkEwx7GbcPJOZxrtbu+QfF3pJLJuaYTeAH0DYy8=
github.com/go-playground/validator/v10 v10.30.3/go.mod h1:4Axh7oCNGcoGkqLoE4YWt6n20mcEIsPRlB7vPk3lpyc=
github.com/go-sql-driver/mysql v1.10.0 h1:Q+1LV8DkHJvSYAdR83XzuhDaTykuDx0l6fkXxoWCWfw=
github.com/go-sql-driver/mysql v1.10.0/go.mod h1:M+cqaI7+xxXGG9swrdeUIoPG3Y3KCkF0pZej+SK+nWk=
github.com/goccy/go-json v0.10.6 h1:p8HrPJzOakx/mn/bQtjgNjdTcN+/S6FcG2CTtQOrHVU=
github.com/goccy/go-json v0.10.6/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
github.com/goccy/go-yaml v1.19.2 h1:PmFC1S6h8ljIz6gMRBopkjP1TVT7xuwrButHID66PoM=
github.com/goccy/go-yaml v1.19.2/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
github.com/golang-jwt/jwt/v5 v5.3.1 h1:kYf81DTWFe7t+1VvL7eS+jKFVWaUnK9cB1qbwn63YCY=
github.com/golang-jwt/jwt/v5 v5.3.1/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.30.0 h1:/Tnpcb2E0Pz/tN9s3bfEY2Q8ePCEX9iuS+cneUwncnw=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.30.0/go.mod h1:zOBXOsUaBSjKgmH4OGzV1esUpR3oUSCPYVd2cUBjKYY=
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo=
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
github.com/jackc/pgx/v5 v5.10.0 h1:VhSvgU2jSli8o3AqIEOTJr7rZwAEUVo4E4XhR94Zfr0=
github.com/jackc/pgx/v5 v5.10.0/go.mod h1:mal1tBGAFfLHvZzaYh77YS/eC6IX9OWbRV1QIIM0Jn4=
github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo=
github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/klauspost/compress v1.19.2 h1:hMRETovs/pu/dVWN7zIT1PGG8t509MwT6bO7XSi26R8=
github.com/klauspost/compress v1.19.2/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ=
github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/cpuid/v2 v2.4.0 h1:S6Hrbc7+ywsr0r+RLapfGBHfyefhCTwEh3A0tV913Dw=
github.com/klauspost/cpuid/v2 v2.4.0/go.mod h1:19jmZ9mjzoF//ddRSUsv0zfBTJWh3QJh9FNxZTMrGxU=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/leodido/go-urn v1.5.0 h1:pLqT2kq1zpHW/1D18QMjMpdtX7cekxqtJJjg5ANyWw0=
github.com/leodido/go-urn v1.5.0/go.mod h1:9BORnCDhdPBJNDEX+w1bJisa8yOKYi116VeO96s4ifE=
github.com/mattn/go-isatty v0.0.24 h1:tGZZoVgT/KiqK1c8ocVLeDS8BSWMRd47J3Lbz7vsReI=
github.com/mattn/go-isatty v0.0.24/go.mod h1:nMCL3Zebbrt45jsMDgnfIwz6ydEQApk5oEI3HqDio6A=
github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
github.com/minio/crc64nvme v1.1.1 h1:8dwx/Pz49suywbO+auHCBpCtlW1OfpcLN7wYgVR6wAI=
github.com/minio/crc64nvme v1.1.1/go.mod h1:eVfm2fAzLlxMdUGc0EEBGSMmPwmXD5XiNRpnu9J3bvg=
github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34=
github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM=
github.com/minio/minio-go/v7 v7.0.95 h1:ywOUPg+PebTMTzn9VDsoFJy32ZuARN9zhB+K3IYEvYU=
github.com/minio/minio-go/v7 v7.0.95/go.mod h1:wOOX3uxS334vImCNRVyIDdXX9OsXDm89ToynKgqUKlo=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/oklog/ulid/v2 v2.1.2 h1:IEclFb9JNvzYA6MW2SCxbLzcHTVsfqm3PrqGQJH5zec=
github.com/oklog/ulid/v2 v2.1.2/go.mod h1:rcEKHmBBKfef9DhnvX7y1HZBYxjXb0cP5ExxNsTT1QQ=
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
github.com/pborman/getopt v0.0.0-20170112200414-7148bc3a4c30/go.mod h1:85jBQOZwpVEaDAr341tbn15RS4fCAsIst0qp7i8ex1o=
github.com/pelletier/go-toml/v2 v2.4.3 h1:GTRvJQutkOSftxIFD5xw9aepkYNuPWmVJpffdDPYVpY=
github.com/pelletier/go-toml/v2 v2.4.3/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
github.com/philhofer/fwd v1.2.0 h1:e6DnBTl7vGY+Gz322/ASL4Gyp1FspeMvx1RNDoToZuM=
github.com/philhofer/fwd v1.2.0/go.mod h1:RqIHx9QI14HlwKwm98g9Re5prTQ6LdeRQn+gXJFxsJM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/quic-go/go-ossfuzz-seeds v0.1.0 h1:APacT+iIaNF6fd8AGEiN3bT/Jtkd2jz4v4TzM7MFjy0=
github.com/quic-go/go-ossfuzz-seeds v0.1.0/go.mod h1:3IOHRbJIc+L6YKMwfDtJAM9Vj9k0YY4muhuyUYk5tbk=
github.com/quic-go/qpack v0.6.0 h1:g7W+BMYynC1LbYLSqRt8PBg5Tgwxn214ZZR34VIOjz8=
github.com/quic-go/qpack v0.6.0/go.mod h1:lUpLKChi8njB4ty2bFLX2x4gzDqXwUpaO1DP9qMDZII=
github.com/quic-go/quic-go v0.61.0 h1:ui88A53s8MSVYLC56en0KQ17HARk+9986Dn0SBfKNvA=
github.com/quic-go/quic-go v0.61.0/go.mod h1:9So2anK4Tp22URSQq00k+Vo2PNkle96ycDPDHL4s9vs=
github.com/redis/go-redis/v9 v9.22.0 h1:laDvpYXTJtZLloinw1fA5Kqd6HAEH2XKxOkG/PDq2F0=
github.com/redis/go-redis/v9 v9.22.0/go.mod h1:y2g0Wj8rQvuK0ELM+oxSudcLtC09JScs98I/X9gRWY4=
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
github.com/rs/xid v1.6.0 h1:fV591PaemRlL6JfRxGDEPl69wICngIQ3shQtzfy2gxU=
github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0=
github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI=
github.com/shirou/gopsutil v3.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0=
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/swaggo/files v1.0.1 h1:J1bVJ4XHZNq0I46UU90611i9/YzdrF7x92oX1ig5IdE=
github.com/swaggo/files v1.0.1/go.mod h1:0qXmMNH6sXNf+73t65aKeB+ApmgxdnkQzVTAj2uaMUg=
github.com/swaggo/gin-swagger v1.6.1 h1:Ri06G4gc9N4t4k8hekMigJ9zKTFSlqj/9paAQCQs7cY=
github.com/swaggo/gin-swagger v1.6.1/go.mod h1:LQ+hJStHakCWRiK/YNYtJOu4mR2FP+pxLnILT/qNiTw=
github.com/swaggo/swag v1.16.6 h1:qBNcx53ZaX+M5dxVyTrgQ0PJ/ACK+NzhwcbieTt+9yI=
github.com/swaggo/swag v1.16.6/go.mod h1:ngP2etMK5a0P3QBizic5MEwpRmluJZPHjXcMoj4Xesg=
github.com/tinylib/msgp v1.4.0 h1:SYOeDRiydzOw9kSiwdYp9UcBgPFtLU2WDHaJXyHruf8=
github.com/tinylib/msgp v1.4.0/go.mod h1:cvjFkb4RiC8qSBOPMGPSzSAx47nAsfhLVTCZZNuHv5o=
github.com/tklauser/go-sysconf v0.4.0 h1:7H0uAN+7RkwWRaxhYXDLqa5V3LPrJeV8wmD9dRUgPQU=
github.com/tklauser/go-sysconf v0.4.0/go.mod h1:8mTNWyog7H+MpKijp4VmKJAd2bbYQ2zuUwkYRbUArPI=
github.com/tklauser/numcpus v0.12.0 h1:NR85qdvHA9pFse3x3weVZ0r0ST8R6l5RHbZrlRaqob4=
github.com/tklauser/numcpus v0.12.0/go.mod h1:ABHeXzJnr/qqwguhClkZKT1/8VABcYrsyUiUGobwWJg=
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
github.com/ugorji/go/codec v1.3.2 h1:zkEASHHyEClGeURfgNT9PJZVfAbs9oEX9QXggwWNJbc=
github.com/ugorji/go/codec v1.3.2/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
github.com/zeebo/xxh3 v1.1.0 h1:s7DLGDK45Dyfg7++yxI0khrfwq9661w9EN78eP/UZVs=
github.com/zeebo/xxh3 v1.1.0/go.mod h1:IisAie1LELR4xhVinxWS5+zf1lA4p0MW4T+w+W07F5s=
go.etcd.io/etcd/api/v3 v3.7.1 h1:KJG0/DcWGfe3Y1otDf/fsBf0TSSgpxZ5RO/L8SFt73E=
go.etcd.io/etcd/api/v3 v3.7.1/go.mod h1:8bXIpCMeV7E3/XL0Ix123ATn3dB+0V7d9zklHbB0m78=
go.etcd.io/etcd/client/pkg/v3 v3.7.1 h1:rKYsj3pRkR0eK3yjT3XOgrhqfmIfj9pzNgxjh7mfFv4=
go.etcd.io/etcd/client/pkg/v3 v3.7.1/go.mod h1:cnzZGIUzSfjEwLC6UBVsSXlEK1eepS/JUD7wE6PLRT0=
go.etcd.io/etcd/client/v3 v3.7.1 h1:0PEMMC0KuZmVIN+RAbdqfkZ45pYTgKVtmBEbRCvZFUg=
go.etcd.io/etcd/client/v3 v3.7.1/go.mod h1:ffNqALa8tRCYhYo1F9oR489y23K39Gz+BSR3ApAGYq0=
go.mongodb.org/mongo-driver/v2 v2.8.0 h1:CxWDGQYY8QQwNjAl/aq2sfWakdnWZynnqJ9F4DhHbP8=
go.mongodb.org/mongo-driver/v2 v2.8.0/go.mod h1:yOI9kBsufol30iFsl1slpdq1I0eHPzybRWdyYUs8K/0=
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
go.opentelemetry.io/otel v1.45.0 h1:pdrWmLHofpubmArBv1LgFSv1Z0Ie/ppdZzu+kUN5EeU=
go.opentelemetry.io/otel v1.45.0/go.mod h1:XZxIqPapzEYnhNSScF5DIqXhm/rYi0FzCe2XddAwZfQ=
go.opentelemetry.io/otel/metric v1.45.0 h1:7Eg1uH7CJ5cXv9is6tnBe1FI6rj1nwUdbFypRm3br/M=
go.opentelemetry.io/otel/metric v1.45.0/go.mod h1:HAPbm1nd3p1PmFH7v2dR+6BjXxw+Lq4a2+pndMAm08s=
go.opentelemetry.io/otel/sdk v1.45.0 h1:4VVSMgQ83dUgW2aoX5f6JgLvHwIvzcuLnF9lUdCSpCw=
go.opentelemetry.io/otel/sdk v1.45.0/go.mod h1:Sr40LgXV7DsKMMJMKOhUWOgMWTfAaqvm2kF0g7ilwuA=
go.opentelemetry.io/otel/sdk/metric v1.44.0 h1:3LlKgI+VjbVsjNRFZJZAJ30WjXC5VkNRks6si09iEfI=
go.opentelemetry.io/otel/sdk/metric v1.44.0/go.mod h1:5B5pMARnXxKhltooO4xUuCBorl65a4EpnTalObqOigA=
go.opentelemetry.io/otel/trace v1.45.0 h1:l/mP6Uv7oNO7/TblbhpbgMidxhq1uO/rPsikOyVhxag=
go.opentelemetry.io/otel/trace v1.45.0/go.mod h1:qoJJA2xNMnxRrdISU/kLtfUH2wNeQbiv+jhs/CxI8bc=
go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y=
go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go.uber.org/zap v1.28.0 h1:IZzaP1Fv73/T/pBMLk4VutPl36uNC+OSUh3JLG3FIjo=
go.uber.org/zap v1.28.0/go.mod h1:rDLpOi171uODNm/mxFcuYWxDsqWSAVkFdX4XojSKg/Q=
go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw=
go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg=
golang.org/x/arch v0.30.0 h1:sB9h+1gRGa2+LauFSV0tm8bK1J2yo1bx6/Uyi/P6DTU=
golang.org/x/arch v0.30.0/go.mod h1:0X+GdSIP+kL5wPmpK7sdkEVTt2XoYP0cSjQSbZBwOi8=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.54.0 h1:YLIA59K4fiNzHzjnZt2tUJQjQtUWfWbeHBqKtk3eScw=
golang.org/x/crypto v0.54.0/go.mod h1:KWL8ny2AZdGR2cWmzeHrp2azQPGogOv+HeQaVEXC2dk=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.37.0 h1:vF1DjpVEshcIqoEaauuHebaLk1O1forxjxBaVn884JQ=
golang.org/x/mod v0.37.0/go.mod h1:m8S8VeM9r4dzDwjrKO0a1sZP3YjeMamRRlD+fmR2Q/0=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.57.0 h1:K5+3DljvIuDG9/Jv9rvyMywYNFCQ9RSUY6OOTTkT+tE=
golang.org/x/net v0.57.0/go.mod h1:KpXc8iv+r3XplLAG/f7Jsf9RPszJzdR0f58q9vGOuEU=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek=
golang.org/x/sync v0.22.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs=
golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.40.0 h1:Ub2Z6/xjgF1WrYQz2nuITOEegKFtiIy+rieRJ5lHZKs=
golang.org/x/text v0.40.0/go.mod h1:hpnzDAfGV753zIKo+wk3u1bVKCGPbrnF7+7LBF/UHVY=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.47.0 h1:7Kn5x/d1svx/PzryTsqeoZN4TZwqeH5pGWjefhLi/1Q=
golang.org/x/tools v0.47.0/go.mod h1:dFHnyTvFWY212G+h7ZY4Vsp/K3U4/7W9TyVaAul8uCA=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4=
gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E=
google.golang.org/genproto/googleapis/api v0.0.0-20260807164820-c8921c73eeea h1:Jifw/kjs/r3B0uszvls/m3c3tmZs2YHGM9C+rvxP9gY=
google.golang.org/genproto/googleapis/api v0.0.0-20260807164820-c8921c73eeea/go.mod h1:K/+WGbmBY7aNW1HDw1fJnKYo10i0DkAX6pows00dLig=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260807164820-c8921c73eeea h1:kVhQEPTpKQahD5+JSBTfBB19wcgQTTjAIn45MBqnyHk=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260807164820-c8921c73eeea/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
google.golang.org/grpc v1.83.0 h1:JeNZEKJFbQxArAMl+hiytHauacDNqJUllNfmIMmpqnQ=
google.golang.org/grpc v1.83.0/go.mod h1:kDyl6SKsiHKt0uylY5gtn5cEjkrIOhQOGDgIc4JGwzQ=
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gorm.io/driver/mysql v1.6.0 h1:eNbLmNTpPpTOVZi8MMxCi2aaIm0ZpInbORNXDwyLGvg=
gorm.io/driver/mysql v1.6.0/go.mod h1:D/oCC2GWK3M/dqoLxnOlaNKmXz8WNTfcS9y5ovaSqKo=
gorm.io/driver/postgres v1.6.2 h1:BvXQ/cNUg63q5TFNg672DmDcowZSFrNLkkA3Xe6GXq4=
gorm.io/driver/postgres v1.6.2/go.mod h1:0c4fQA44XhOklXDkgtuKqysHCycTa5i9e3EIpDGCwXk=
gorm.io/driver/sqlite v1.6.0 h1:WHRRrIiulaPiPFmDcod6prc4l2VGVWHz80KspNsxSfQ=
gorm.io/driver/sqlite v1.6.0/go.mod h1:AO9V1qIQddBESngQUKWL9yoH93HIeA1X6V633rBwyT8=
gorm.io/gorm v1.31.2 h1:3o8FXNo9v9S858gil+3LlZA1LkCOzgb4g5BL64FgaCo=
gorm.io/gorm v1.31.2/go.mod h1:XyQVbO2k6YkOis7C2437jSit3SsDK72s7n7rsSHd+Gs=

View File

@@ -0,0 +1,41 @@
package config
import (
"git.apinb.com/bsm-sdk/core/conf"
)
var (
Spec SrvConfig
)
type SrvConfig struct {
conf.Base `yaml:",inline"`
Databases *conf.DBConf `yaml:"Databases"` // 数据库配置
Rpc map[string]conf.RpcConf `yaml:"Rpc"` // RPC配置
Apm *conf.ApmConf `yaml:"APM"` // APM配置
Etcd *conf.EtcdConf `yaml:"Etcd"` // ETCD配置
MinioOss *conf.OssConf `yaml:"MinioOss"` // OSS配置
Local *LocalConf `yaml:"Local"` // 本地文件配置
FtsConfig *ftsConf `yaml:"FtsConfig"` // FTS配置
}
type ftsConf struct {
MaxSize int64 `yaml:"MaxSize"` // 上传文件大小限制
InputKey string `yaml:"InputKey"` // 上传文件名
Allows []string `yaml:"Allows"` // 允许上传的文件类型
}
type LocalConf struct {
Site string `yaml:"Site"` // 站点HOST
UploadDir string `yaml:"UploadDir"` //本地上传文件夹
}
func New(srvKey string) {
// 初始化配置 创建一个新的配置实例,用于服务配置
conf.New(srvKey, &Spec)
// 配置校验 服务端口如果不合规,则随机分配端口
Spec.Port = conf.CheckPort(Spec.Port)
// 配置校验 服务名称地址及监听地址不能为空
conf.NotNil(Spec.Service, Spec.Cache)
}

View File

@@ -0,0 +1,118 @@
/**
* @Author: FTS Team
* @Date: 2024-10-02
* @Description: 错误处理定义
*/
package errors
import (
"fmt"
"net/http"
)
// 业务错误码定义
const (
// 通用错误码
ErrCodeInternalError = 10001 // 内部错误
ErrCodeInvalidParam = 10002 // 参数错误
ErrCodeNotFound = 10003 // 资源不存在
ErrCodeUnauthorized = 10004 // 未授权
ErrCodeForbidden = 10005 // 权限不足
ErrCodeTooManyRequests = 10006 // 请求过于频繁
// 文件相关错误码
ErrCodeFileNotFound = 20001 // 文件不存在
ErrCodeFileTooLarge = 20002 // 文件过大
ErrCodeFileTypeNotAllow = 20003 // 文件类型不允许
ErrCodeUploadFailed = 20004 // 上传失败
ErrCodeDownloadFailed = 20005 // 下载失败
// 存储相关错误码
ErrCodeOSSConnectionFailed = 30001 // OSS连接失败
ErrCodeOSSUploadFailed = 30002 // OSS上传失败
ErrCodeOSSDeleteFailed = 30003 // OSS删除失败
ErrCodeLocalStorageFailed = 30004 // 本地存储失败
// 数据库相关错误码
ErrCodeDBConnectionFailed = 40001 // 数据库连接失败
ErrCodeDBQueryFailed = 40002 // 数据库查询失败
ErrCodeDBInsertFailed = 40003 // 数据库插入失败
ErrCodeDBUpdateFailed = 40004 // 数据库更新失败
ErrCodeDBDeleteFailed = 40005 // 数据库删除失败
)
// BusinessError 业务错误
type BusinessError struct {
Code int `json:"code"`
Message string `json:"message"`
Detail string `json:"detail,omitempty"`
}
func (e *BusinessError) Error() string {
if e.Detail != "" {
return fmt.Sprintf("业务错误[%d]: %s (%s)", e.Code, e.Message, e.Detail)
}
return fmt.Sprintf("业务错误[%d]: %s", e.Code, e.Message)
}
// HTTPStatus 返回对应的HTTP状态码
func (e *BusinessError) HTTPStatus() int {
switch e.Code {
case ErrCodeNotFound, ErrCodeFileNotFound:
return http.StatusNotFound
case ErrCodeUnauthorized:
return http.StatusUnauthorized
case ErrCodeForbidden:
return http.StatusForbidden
case ErrCodeInvalidParam, ErrCodeFileTooLarge, ErrCodeFileTypeNotAllow:
return http.StatusBadRequest
case ErrCodeTooManyRequests:
return http.StatusTooManyRequests
default:
return http.StatusInternalServerError
}
}
// New 创建新的业务错误
func New(code int, message string) *BusinessError {
return &BusinessError{
Code: code,
Message: message,
}
}
// NewWithDetail 创建带详细信息的业务错误
func NewWithDetail(code int, message, detail string) *BusinessError {
return &BusinessError{
Code: code,
Message: message,
Detail: detail,
}
}
// 预定义常用错误
var (
ErrInternalError = New(ErrCodeInternalError, "内部服务器错误")
ErrInvalidParam = New(ErrCodeInvalidParam, "参数错误")
ErrNotFound = New(ErrCodeNotFound, "资源不存在")
ErrUnauthorized = New(ErrCodeUnauthorized, "未授权访问")
ErrForbidden = New(ErrCodeForbidden, "权限不足")
ErrTooManyRequests = New(ErrCodeTooManyRequests, "请求过于频繁")
ErrFileNotFound = New(ErrCodeFileNotFound, "文件不存在")
ErrFileTooLarge = New(ErrCodeFileTooLarge, "文件大小超出限制")
ErrFileTypeNotAllow = New(ErrCodeFileTypeNotAllow, "文件类型不允许")
ErrUploadFailed = New(ErrCodeUploadFailed, "文件上传失败")
ErrDownloadFailed = New(ErrCodeDownloadFailed, "文件下载失败")
ErrOSSConnectionFailed = New(ErrCodeOSSConnectionFailed, "OSS连接失败")
ErrOSSUploadFailed = New(ErrCodeOSSUploadFailed, "OSS上传失败")
ErrOSSDeleteFailed = New(ErrCodeOSSDeleteFailed, "OSS删除失败")
ErrLocalStorageFailed = New(ErrCodeLocalStorageFailed, "本地存储失败")
ErrDBConnectionFailed = New(ErrCodeDBConnectionFailed, "数据库连接失败")
ErrDBQueryFailed = New(ErrCodeDBQueryFailed, "数据库查询失败")
ErrDBInsertFailed = New(ErrCodeDBInsertFailed, "数据库插入失败")
ErrDBUpdateFailed = New(ErrCodeDBUpdateFailed, "数据库更新失败")
ErrDBDeleteFailed = New(ErrCodeDBDeleteFailed, "数据库删除失败")
)

View File

@@ -0,0 +1,25 @@
package impl
import (
"bsm/full/module/base/fts/internal/config"
"git.apinb.com/bsm-sdk/core/cache/redis"
"git.apinb.com/bsm-sdk/core/with"
cache "github.com/patrickmn/go-cache"
clientv3 "go.etcd.io/etcd/client/v3"
"gorm.io/gorm"
)
var (
RedisService *redis.RedisClient
EtcdService *clientv3.Client
DBService *gorm.DB
MemorySerice *cache.Cache
)
func NewImpl() {
// with activating
MemorySerice = with.Memory(nil)
RedisService = with.RedisCache(config.Spec.Cache) // redis cache
DBService = with.Databases(config.Spec.Databases, nil) // model
EtcdService = with.Etcd(config.Spec.Etcd) // etcd
}

View File

@@ -0,0 +1,11 @@
package logic
import (
"bsm/full/module/base/fts/internal/config"
"git.apinb.com/bsm-sdk/core/infra"
"github.com/gin-gonic/gin"
)
func Config(ctx *gin.Context) {
infra.Response.Success(ctx, config.Spec.FtsConfig)
}

View File

@@ -0,0 +1,30 @@
package logic
// import (
// "context"
// "net/http"
// "bsm/full/module/base/fts/internal/models"
// "bsm/full/module/base/fts/internal/svc"
// "bsm/full/module/base/fts/internal/types"
// "git.apinb.com/bsm-sdk/core/exception"
// "github.com/zeromicro/go-zero/core/logx"
// )
// func (l *ListLogic) List(in *types.Paginate, r *http.Request) (resp *types.Base, err error) {
// // 解析token
// claims, err := parseToken(r.Header.Get("Authorization"))
// if err != nil {
// return nil, exception.ErrAuthNotFound
// }
// // 获取数据
// data, cnt, err := models.GetFileList(in.Offset, in.Size, claims.Identity)
// if err != nil {
// return nil, exception.ErrDBFatal
// }
// if cnt == 0 {
// return Success(""), nil
// }
// return Success(data), nil
// }

View File

@@ -0,0 +1,134 @@
package logic
import (
"crypto/sha256"
"encoding/hex"
"io"
"log"
"mime/multipart"
"path/filepath"
"strings"
"bsm/full/module/base/fts/internal/config"
"bsm/full/module/base/fts/internal/impl"
"bsm/full/module/base/fts/internal/models"
"git.apinb.com/bsm-sdk/core/errcode"
"git.apinb.com/bsm-sdk/core/infra"
"git.apinb.com/bsm-sdk/core/middleware"
"git.apinb.com/bsm-sdk/core/types"
"git.apinb.com/bsm-sdk/core/utils"
"github.com/gin-gonic/gin"
)
// Handler 上传文件
func Handler(c *gin.Context) {
var (
provider = strings.ToLower(c.PostForm("provider"))
bucket = strings.ToLower(c.PostForm("bucket"))
claims *types.JwtClaims
err error
)
claims, err = middleware.ParseAuth(c)
if err != nil {
log.Printf("获取当前登录用户信息失败: %v\n", err)
infra.Response.Error(c, err)
return
}
if provider == "" || bucket == "" {
infra.Response.Error(c, errcode.NewError(400, "参数错误"))
return
}
// if !strings.Contains(provider, "local") {
// log.Println("provider参数错误")
// infra.Response.Error(c, errcode.NewError(400, "provider参数错误"))
// return
// }
fh, err := c.FormFile(config.Spec.FtsConfig.InputKey)
if err != nil {
infra.Response.Error(c, err)
return
}
// 获取文件信息
fileSize := fh.Size
if fileSize > config.Spec.FtsConfig.MaxSize {
infra.Response.Error(c, errcode.NewError(400, "文件大小超过限制"))
return
}
// 检测是否是允许上传的文件类型
fileExt := filepath.Ext(fh.Filename)
if !isAllow(fileExt) {
log.Println("不允许上传的文件类型:", fileExt)
infra.Response.Error(c, errcode.NewError(501, "不允许上传的文件类型"))
return
}
fileHash, err := chksum(fh)
if err != nil {
log.Println("文件校验失败:", err)
infra.Response.Error(c, err)
return
}
record := models.FtsRecord{
Identity: utils.UUID(),
OwnerID: claims.ID,
OwnerIdentity: claims.Identity,
Name: fh.Filename,
Ext: fileExt,
Size: uint64(fileSize),
Hash: fileHash,
Status: 0,
}
switch provider {
case "local":
err = LocalUpload(fh, &record, c, bucket)
case "minio":
err = OssUpload(fh, &record, c, bucket)
default:
infra.Response.Error(c, errcode.NewError(400, "provider参数错误"))
return
}
if err != nil {
infra.Response.Error(c, err)
return
}
if err := impl.DBService.Create(&record).Error; err != nil {
infra.Response.Error(c, err)
return
}
infra.Response.Success(c, record)
}
func chksum(fh *multipart.FileHeader) (string, error) {
// 2. 打开文件读取内容
file, err := fh.Open()
if err != nil {
log.Println("multipart err:", err)
return "", err
}
defer file.Close()
// 3. 计算文件哈希值sha256
hash := sha256.New()
if _, err = io.Copy(hash, file); err != nil {
log.Println("哈希计算失败:", err)
return "", err
}
return hex.EncodeToString(hash.Sum(nil)), nil
}
func isAllow(extName string) bool {
for _, allowExt := range config.Spec.FtsConfig.Allows {
if extName == allowExt {
return true
}
}
return false
}

View File

@@ -0,0 +1,11 @@
package logic
import (
"git.apinb.com/bsm-sdk/core/infra"
"github.com/gin-gonic/gin"
)
// Ping
func Ping(ctx *gin.Context) {
infra.Response.Success(ctx, gin.H{"message": "Pong"})
}

View File

@@ -0,0 +1,116 @@
package logic
import (
"context"
"errors"
"fmt"
"io"
"log"
"mime/multipart"
"os"
"path/filepath"
"time"
"bsm/full/module/base/fts/internal/config"
"bsm/full/module/base/fts/internal/models"
"git.apinb.com/bsm-sdk/core/infra"
"git.apinb.com/bsm-sdk/core/utils"
"github.com/gin-gonic/gin"
"github.com/minio/minio-go/v7"
"github.com/minio/minio-go/v7/pkg/credentials"
)
// LocalUpload 本地上传
func LocalUpload(fh *multipart.FileHeader, record *models.FtsRecord, ctx *gin.Context, bucket string) (err error) {
subdirpath := NewSubdir(record.OwnerIdentity)
saveDir := filepath.Join(config.Spec.Local.UploadDir, bucket, subdirpath)
// 创建目录并确保权限正确
if err = os.MkdirAll(saveDir, 0755); err != nil {
log.Println("目录创建失败:", err)
infra.Response.Error(ctx, errors.New("目录创建失败"))
return
}
// 保存文件到指定路径
fileName := utils.ULID() + record.Ext
savePath := filepath.Join(saveDir, fileName)
// 使用自定义方式保存文件,确保权限控制
file, err := os.OpenFile(savePath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
if err != nil {
log.Println("文件创建失败:", err)
infra.Response.Error(ctx, err)
return
}
defer file.Close()
src, err := fh.Open()
if err != nil {
log.Println("文件打开失败:", err)
infra.Response.Error(ctx, err)
return
}
defer src.Close()
if _, err = io.Copy(file, src); err != nil {
log.Println("文件保存失败:", err)
infra.Response.Error(ctx, err)
return
}
// 再次确认文件权限
if err = os.Chmod(savePath, 0644); err != nil {
log.Printf("警告: 设置文件权限失败: %v", err)
infra.Response.Error(ctx, err)
return
}
record.LocalPath = savePath
record.SaveName = fileName
record.ResultUrl = config.Spec.Local.Site + "/" + bucket + "/" + subdirpath + "/" + fileName
return nil
}
// OssUpload 上传文件到指定的MinIO存储桶
func OssUpload(file *multipart.FileHeader, record *models.FtsRecord, c *gin.Context, bucket string) (err error) {
// Initialize minio client object.
minioClient, err := minio.New(config.Spec.MinioOss.Endpoint, &minio.Options{
Creds: credentials.NewStaticV4(config.Spec.MinioOss.AccessKeyID, config.Spec.MinioOss.AccessKeySecret, ""), // 修正字段名
Secure: config.Spec.MinioOss.UseSSL,
})
if err != nil {
return err
}
// 设置保存格式为: 年/identity/identity后3位+filename
subdirpath := NewSubdir(record.OwnerIdentity)
fileName := utils.ULID() + record.Ext
savePath := filepath.Join(subdirpath, fileName)
// Open the file
src, err := file.Open()
if err != nil {
return err
}
defer src.Close()
_, err = minioClient.PutObject(context.Background(), bucket, savePath, src, file.Size, minio.PutObjectOptions{ContentType: "application/octet-stream"})
if err != nil {
fmt.Println("err = ", err)
return err
}
record.OssPath = savePath
fmt.Println("savePath = ", savePath)
record.ResultUrl = config.Spec.MinioOss.Site + "/" + bucket + "/" + savePath
fmt.Println("record.ResultUrl = ", record.ResultUrl)
return nil
}
func NewSubdir(identity string) string {
ym := time.Now().Format("2006-01")
return ym + "/" + identity[0:2]
}

View File

@@ -0,0 +1,98 @@
package models
import (
"time"
"git.apinb.com/bsm-sdk/core/database"
"gorm.io/gorm"
)
/*
* Object Storage Record
* Comment: 文件存储库
* Version: 10
* Created: 2022-04-11 18:41:52 , Updated:0001-01-01 00:00:00
*/
type FtsRecord struct {
ID uint `gorm:"column:id;primarykey;" json:"id"`
Identity string `gorm:"column:identity;type:varchar(36);uniqueIndex;" json:"identity"` // 唯一标识24位NanoID,36位为ULID
CreatedAt time.Time `gorm:"column:created_at;type:TIMESTAMP;" json:"created_at"`
UpdatedAt time.Time `gorm:"column:updated_at;type:TIMESTAMP;" json:"updated_at"`
DeletedAt gorm.DeletedAt `gorm:"column:deleted_at;type:TIMESTAMP;index;" json:"deleted_at"`
OwnerID uint `gorm:"column:owner_id;Index;"` // 用户id
OwnerIdentity string `gorm:"column:owner_identity;type:varchar(36);Index;"` // 用户唯一标识24位NanoID,36位为UUID
Hash string `gorm:"column:hash;type:varchar(255);not null;" json:"hash"` // 文件hash值
Name string `gorm:"column:name;type:varchar(255);not null;" json:"name"` // 文件源名称
Ext string `gorm:"column:ext;type:varchar(255);not null;" json:"ext"` // 文件后缀
Size uint64 `gorm:"column:size;default:0;" json:"size"` // 文件大小
HandleCmd string `gorm:"column:handle_cmd;type:varchar(255);default:'';" json:"handle_cmd"` // 文件上传成功后操作命令
HandleArgs string `gorm:"column:handle_args;type:varchar(255);default:'';" json:"handle_args"` // 操作参数
SaveName string `gorm:"column:save_name;type:varchar(500);default:'';" json:"save_name"` // 保存的文件名
LocalPath string `gorm:"column:local_path;type:varchar(500);default:'';" json:"local_path"` // 本地路径
OssPath string `gorm:"column:oss_path;type:varchar(500);default:'';" json:"oss_path"` // 对象存储路径
ResultUrl string `gorm:"column:result_url;type:varchar(500);default:'';" json:"result_url"` // 处理结果路径
Status int8 `gorm:"default:0;index;"` // -1:准备清除0待续1无需处理2准备处理3处理中4处理失败5处理成功。
}
func init() {
database.MigrateTables = append(database.MigrateTables, &FtsRecord{})
}
// TableName .
func (table *FtsRecord) TableName() string {
return "fts_record" //对应数据库表名
}
// func GetFileList(page, size int, identity string) ([]types.Record, int64, error) {
// var list = make([]types.Record, 0)
// tx := DBService.Model(&FtsRecord{}).Select("id,identity,name,ext,size,local_path,oss_path,result_path,status").Where("passport_identity = ?", identity)
// tx.Order("id").Limit(size).Offset((page - 1) * size).Find(&list)
// cnt := tx.RowsAffected
// err := tx.Error
// return list, cnt, err
// }
// func GetFileDetails(identity string) (data *types.Record, cnt int64, err error) {
// tx := DBService.Model(&FtsRecord{}).Select("id,identity,name,ext,size,local_path,oss_path,result_path,status").Where("identity = ?", identity).Scan(&data)
// cnt = tx.RowsAffected
// err = tx.Error
// return data, cnt, err
// }
// func TableSql(tableName string) string {
// sql := `
// CREATE SEQUENCE IF NOT EXISTS fts_record_bigserial;
// CREATE TABLE IF NOT EXISTS {TABLE_NAME} (
// id INT DEFAULT nextval('fts_record_bigserial') PRIMARY KEY,
// "identity" varchar(36) NULL,
// passport_id int8 NULL,
// passport_identity varchar(36) NULL,
// hash varchar(255) NULL,
// "name" varchar(255) NULL,
// ext varchar(255) NULL,
// "size" float8 NULL,
// handle_cmd varchar(255) NULL,
// handle_args varchar(255) NULL,
// local_path varchar(255) NULL,
// oss_path varchar(255) NULL,
// result_path varchar(255) NULL,
// status int2 NULL,
// created_at timestamp(6) NULL,
// updated_at timestamp(6) NULL,
// deleted_at timestamp(6) NULL,
// CONSTRAINT {TABLE_NAME}_pkey PRIMARY KEY (id)
// );
// COMMENT ON TABLE fts_record IS '文件存储库';
// CREATE UNIQUE INDEX idx_{TABLE_NAME}_identity ON {TABLE_NAME} USING btree (identity);
// CREATE INDEX idx_{TABLE_NAME}_passport_id ON {TABLE_NAME} USING btree (passport_id);
// CREATE INDEX idx_{TABLE_NAME}_passport_identity ON {TABLE_NAME} USING btree (passport_identity);
// CREATE INDEX idx_{TABLE_NAME}_passport_status ON {TABLE_NAME} USING btree (status);
// `
// sql = strings.ReplaceAll(sql, "{TABLE_NAME}", tableName)
// return sql
// }

View File

@@ -0,0 +1,6 @@
package models
// 初始化数据
func InitData() {
}

View File

@@ -0,0 +1,144 @@
/**
* @Author: FTS Team
* @Date: 2024-10-02
* @Description: 统一响应处理
*/
package response
import (
"net/http"
"bsm/full/module/base/fts/internal/errors"
"github.com/gin-gonic/gin"
)
// Response 统一响应结构
type Response struct {
Code int `json:"code"`
Message string `json:"message"`
Data interface{} `json:"data,omitempty"`
TraceID string `json:"trace_id,omitempty"`
}
// Success 成功响应
func Success(c *gin.Context, data interface{}) {
c.JSON(http.StatusOK, Response{
Code: 0,
Message: "成功",
Data: data,
TraceID: getTraceID(c),
})
}
// SuccessWithMessage 带自定义消息的成功响应
func SuccessWithMessage(c *gin.Context, message string, data interface{}) {
c.JSON(http.StatusOK, Response{
Code: 0,
Message: message,
Data: data,
TraceID: getTraceID(c),
})
}
// Error 错误响应
func Error(c *gin.Context, err error) {
var businessErr *errors.BusinessError
var httpStatus int
var response Response
// 检查是否为业务错误
if bizErr, ok := err.(*errors.BusinessError); ok {
businessErr = bizErr
httpStatus = businessErr.HTTPStatus()
response = Response{
Code: businessErr.Code,
Message: businessErr.Message,
TraceID: getTraceID(c),
}
// 如果有详细信息,添加到响应中
if businessErr.Detail != "" {
response.Data = map[string]string{"detail": businessErr.Detail}
}
} else {
// 默认内部错误
businessErr = errors.ErrInternalError
httpStatus = http.StatusInternalServerError
response = Response{
Code: businessErr.Code,
Message: businessErr.Message,
Data: map[string]string{"detail": err.Error()},
TraceID: getTraceID(c),
}
}
c.JSON(httpStatus, response)
}
// ErrorWithCode 指定错误码的错误响应
func ErrorWithCode(c *gin.Context, code int, message string) {
businessErr := errors.New(code, message)
c.JSON(businessErr.HTTPStatus(), Response{
Code: code,
Message: message,
TraceID: getTraceID(c),
})
}
// Forbidden 403响应
func Forbidden(c *gin.Context, message string) {
if message == "" {
message = "权限不足"
}
c.JSON(http.StatusForbidden, Response{
Code: errors.ErrCodeForbidden,
Message: message,
TraceID: getTraceID(c),
})
}
// Unauthorized 401响应
func Unauthorized(c *gin.Context, message string) {
if message == "" {
message = "未授权访问"
}
c.JSON(http.StatusUnauthorized, Response{
Code: errors.ErrCodeUnauthorized,
Message: message,
TraceID: getTraceID(c),
})
}
// NotFound 404响应
func NotFound(c *gin.Context, message string) {
if message == "" {
message = "资源不存在"
}
c.JSON(http.StatusNotFound, Response{
Code: errors.ErrCodeNotFound,
Message: message,
TraceID: getTraceID(c),
})
}
// BadRequest 400响应
func BadRequest(c *gin.Context, message string) {
if message == "" {
message = "请求参数错误"
}
c.JSON(http.StatusBadRequest, Response{
Code: errors.ErrCodeInvalidParam,
Message: message,
TraceID: getTraceID(c),
})
}
// getTraceID 从上下文中获取追踪ID
func getTraceID(c *gin.Context) string {
if traceID := c.GetHeader("X-Trace-ID"); traceID != "" {
return traceID
}
if traceID := c.GetString("trace_id"); traceID != "" {
return traceID
}
return ""
}

View File

@@ -0,0 +1,28 @@
package routers
import (
"fmt"
"bsm/full/module/base/fts/internal/logic"
"github.com/gin-gonic/gin"
swaggerfiles "github.com/swaggo/files"
ginSwagger "github.com/swaggo/gin-swagger"
)
func Register(srvKey string, engine *gin.Engine) {
v1_key := fmt.Sprintf("/%s/%s", srvKey, "v1")
registerAnonymous(v1_key, engine)
registerUploader(v1_key, engine)
}
// registerAnonymous 不需要auth的接口
func registerAnonymous(v1_key string, engine *gin.Engine) {
// Anonymous router.
anonymous := engine.Group(v1_key)
{
anonymous.GET("/ping", logic.Ping)
anonymous.GET("/config", logic.Config)
anonymous.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerfiles.Handler)) // 文档
}
}

View File

@@ -0,0 +1,17 @@
package routers
import (
"bsm/full/module/base/fts/internal/logic"
"git.apinb.com/bsm-sdk/core/middleware"
"github.com/gin-gonic/gin"
)
// registerUploader
func registerUploader(v1_key string, engine *gin.Engine) {
auth := engine.Group(v1_key)
{
auth.Use(middleware.JwtAuth(true))
auth.POST("/uploader", logic.Handler)
}
}

View File

@@ -0,0 +1,32 @@
filebeat.inputs:
- type: log
enabled: true
# 开启json解析
json.keys_under_root: true
json.add_error_key: true
# 日志文件路径
paths:
- ./logs/fts/error.log
- ./logs/fts/slow.log
setup.template.settings:
index.number_of_shards: 1
# 定义kafka topic field
fields:
log_topic: scf.license.dev
# 输出到kafka
output.kafka:
hosts: ["127.0.0.1:9092"]
topic: '%{[fields.log_topic]}'
partition.round_robin:
reachable_only: false
required_acks: 1
keep_alive: 10s
# ================================= Processors =================================
processors:
- decode_json_fields:
fields: ['@timestamp','level','content','trace','span','duration']
target: ""

View File

@@ -0,0 +1,12 @@
go env -w CGO_ENABLED=0
go env -w GOOS="linux"
go env -w GOARCH=arm64
go build -o bsm-apps-fts ./cmd/main/main.go
go env -w GOARCH=amd64
go env -w GOOS="windows"
go env -w GOOS="linux"
go build -o bsm-apps-fts ./cmd/main/main.go
go env -w GOOS="windows"
CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -o fts ./cmd/main/main.go

View File

@@ -0,0 +1,167 @@
//go:build integration
package test
import (
"bytes"
"encoding/json"
"fmt"
"io"
"mime/multipart"
"net/http"
"os"
"testing"
)
type PushData struct {
uploaderUrl string
fileName string
bucket string
ossName string
purpose string
}
// 华为
var ossData = PushData{
uploaderUrl: "http://127.0.0.1:12214/fts/Transfer/Uploader",
fileName: "hello.txt",
bucket: "makw-cdn-file",
ossName: "huawei",
purpose: "uploader", //文件夹名称(用途)
}
// minio
//var ossData = PushData{
// uploaderUrl: "http://127.0.0.1:12214/fts/Transfer/Uploader",
// fileName: "hello.txt",
// bucket: "uploader",
// ossName: "minio",
// purpose: "uploader",
//}
var token = os.Getenv("BSM_FTS_TOKEN")
//var token = ""
func postFile(ossName, filename, bucket, key string) (string, *bytes.Buffer) {
body := &bytes.Buffer{}
writer := multipart.NewWriter(body)
_ = writer.WriteField("provider", ossName)
_ = writer.WriteField("bucket", bucket)
_ = writer.WriteField("purpose", key)
uploadWriter, _ := writer.CreateFormFile("file", filename)
fileData, _ := os.Open(filename)
defer fileData.Close()
io.Copy(uploadWriter, fileData)
_ = writer.Close()
return writer.FormDataContentType(), body
}
// 测试上传请求
func TestUploader(t *testing.T) {
// 组装请求内容
contextType, body := postFile(ossData.ossName, ossData.fileName, ossData.bucket, ossData.purpose)
//fmt.Println(body.String())
req, err := http.NewRequest("POST", ossData.uploaderUrl, body)
if err != nil {
panic(err)
}
req.Header.Set("Content-Type", contextType)
req.Header.Set("Authorization", token)
// 发起请求
client := &http.Client{}
resp, err := client.Do(req)
//resp, err := http.Post(ossData.uploaderUrl, context, body)
if err != nil {
panic(err)
}
content, err := io.ReadAll(resp.Body)
defer resp.Body.Close()
fmt.Printf("%s", content)
}
// allows 获取传输配置信息
func TestGetConfig(t *testing.T) {
resp, err := http.Get("http://127.0.0.1:12214/fts/Get/Config")
if err != nil {
panic(err)
}
content, err := io.ReadAll(resp.Body)
defer resp.Body.Close()
fmt.Printf("%s", content)
}
// 闪传
func TestPrepare(t *testing.T) {
var data = map[string]string{
"hash": "04d6af2bff8ae74b7ac7d42b56a5ca795f56a4d00f4f4229fdae3561d46b912c",
"file_name": "xx.png",
}
jsonData, _ := json.Marshal(data)
var body = bytes.NewBuffer([]byte(jsonData))
fmt.Println(body)
resp, err := http.Post("http://127.0.0.1:12214/fts/Transfer/Prepare", "application/json", body)
if err != nil {
panic(err)
}
content, err := io.ReadAll(resp.Body)
fmt.Printf("%s", content)
}
// 列表
func TestList(t *testing.T) {
// 组装请求内容
var data = map[string]int{
"size": 2,
//"page": 33,
}
jsonData, _ := json.Marshal(data)
var body = bytes.NewBuffer(jsonData)
fmt.Println(body)
req, err := http.NewRequest("POST", "http://127.0.0.1:12214/fts/Transfer/FileList", body)
if err != nil {
panic(err)
}
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Authorization", token)
// 发起请求
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
panic(err)
}
content, err := io.ReadAll(resp.Body)
defer resp.Body.Close()
fmt.Printf("%s", content)
}
// 详情
func TestDetails(t *testing.T) {
// 组装请求内容
var data = map[string]string{
"identity": "5fc04e7f-1b82-4069-9ea7-c29414722e98",
//"file_name": "hello.txt",
}
jsonData, _ := json.Marshal(data)
var body = bytes.NewBuffer([]byte(jsonData))
req, err := http.NewRequest("POST", "http://127.0.0.1:12214/fts/Transfer/Details", body)
if err != nil {
panic(err)
}
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Authorization", token)
// 发起请求
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
panic(err)
}
content, err := io.ReadAll(resp.Body)
defer resp.Body.Close()
fmt.Printf("%s", content)
}

View File

@@ -0,0 +1,9 @@
POST http://127.0.0.1:16290/fts/v1/uploader
content-type:application/json
Authorization: {{BSM_TOKEN}}
{
"provider":"minio" ,
"bucket": "frc",
"file": "demo.txt"
}