feat: import services and standardize Go 1.26.5
This commit is contained in:
41
apps/base/fts/internal/config/config.go
Normal file
41
apps/base/fts/internal/config/config.go
Normal 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)
|
||||
}
|
||||
118
apps/base/fts/internal/errors/errors.go
Normal file
118
apps/base/fts/internal/errors/errors.go
Normal 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, "数据库删除失败")
|
||||
)
|
||||
25
apps/base/fts/internal/impl/impl.go
Normal file
25
apps/base/fts/internal/impl/impl.go
Normal file
@@ -0,0 +1,25 @@
|
||||
package impl
|
||||
|
||||
import (
|
||||
"git.apinb.com/bsm-apps/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
|
||||
}
|
||||
11
apps/base/fts/internal/logic/config.go
Normal file
11
apps/base/fts/internal/logic/config.go
Normal file
@@ -0,0 +1,11 @@
|
||||
package logic
|
||||
|
||||
import (
|
||||
"git.apinb.com/bsm-apps/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)
|
||||
}
|
||||
30
apps/base/fts/internal/logic/fetch.go
Normal file
30
apps/base/fts/internal/logic/fetch.go
Normal file
@@ -0,0 +1,30 @@
|
||||
package logic
|
||||
|
||||
// import (
|
||||
// "context"
|
||||
// "net/http"
|
||||
|
||||
// "git.apinb.com/bsm-apps/fts/internal/models"
|
||||
// "git.apinb.com/bsm-apps/fts/internal/svc"
|
||||
// "git.apinb.com/bsm-apps/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
|
||||
// }
|
||||
134
apps/base/fts/internal/logic/handler.go
Normal file
134
apps/base/fts/internal/logic/handler.go
Normal file
@@ -0,0 +1,134 @@
|
||||
package logic
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"io"
|
||||
"log"
|
||||
"mime/multipart"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"git.apinb.com/bsm-apps/fts/internal/config"
|
||||
"git.apinb.com/bsm-apps/fts/internal/impl"
|
||||
"git.apinb.com/bsm-apps/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
|
||||
}
|
||||
11
apps/base/fts/internal/logic/ping.go
Normal file
11
apps/base/fts/internal/logic/ping.go
Normal 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"})
|
||||
}
|
||||
116
apps/base/fts/internal/logic/provider.go
Normal file
116
apps/base/fts/internal/logic/provider.go
Normal file
@@ -0,0 +1,116 @@
|
||||
package logic
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"mime/multipart"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"git.apinb.com/bsm-apps/fts/internal/config"
|
||||
"git.apinb.com/bsm-apps/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]
|
||||
}
|
||||
98
apps/base/fts/internal/models/fts_record.go
Normal file
98
apps/base/fts/internal/models/fts_record.go
Normal 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
|
||||
// }
|
||||
6
apps/base/fts/internal/models/query.go
Normal file
6
apps/base/fts/internal/models/query.go
Normal file
@@ -0,0 +1,6 @@
|
||||
package models
|
||||
|
||||
// 初始化数据
|
||||
func InitData() {
|
||||
|
||||
}
|
||||
144
apps/base/fts/internal/response/response.go
Normal file
144
apps/base/fts/internal/response/response.go
Normal file
@@ -0,0 +1,144 @@
|
||||
/**
|
||||
* @Author: FTS Team
|
||||
* @Date: 2024-10-02
|
||||
* @Description: 统一响应处理
|
||||
*/
|
||||
package response
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"git.apinb.com/bsm-apps/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 ""
|
||||
}
|
||||
28
apps/base/fts/internal/routers/register.go
Normal file
28
apps/base/fts/internal/routers/register.go
Normal file
@@ -0,0 +1,28 @@
|
||||
package routers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"git.apinb.com/bsm-apps/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)) // 文档
|
||||
|
||||
}
|
||||
}
|
||||
17
apps/base/fts/internal/routers/uploader.go
Normal file
17
apps/base/fts/internal/routers/uploader.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package routers
|
||||
|
||||
import (
|
||||
"git.apinb.com/bsm-apps/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)
|
||||
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user