Merge branch 'develop' of https://gitee.com/fusenpack/fusenapi into develop

This commit is contained in:
eson
2023-07-06 18:23:43 +08:00
60 changed files with 1575 additions and 242 deletions

View File

@@ -1,5 +1,24 @@
package basic
import (
"fmt"
"io"
"net/http"
"reflect"
"github.com/zeromicro/go-zero/core/logx"
)
const UploadFileLimitSize = 200 << 20
// File uploadfile 文件(multipart...)
type File struct {
Filename string
Header map[string][]string
Size int64
Data []byte
}
// StatusResponse 公司自定义状态码
type StatusResponse struct {
Code int // 状态码
@@ -136,3 +155,75 @@ func (resp *Response) SetStatusAddMessage(sr *StatusResponse, msg string, data .
}
return newResp
}
var fileType = reflect.TypeOf(File{})
func RequestFileParse(r *http.Request, req any) error {
vreq := reflect.ValueOf(req)
if vreq.Kind() != reflect.Ptr {
panic("req must &req pass")
}
reqValue := vreq.Elem()
reqType := reqValue.Type()
for i := 0; i < reqType.NumField(); i++ {
if tname, ok := reqType.Field(i).Tag.Lookup("file"); ok {
file, fheader, err := r.FormFile(tname)
if err != nil {
logx.Info("upload file error")
return err
}
if fheader.Size > UploadFileLimitSize {
err = fmt.Errorf("upload file size over limit %d", UploadFileLimitSize)
logx.Info(err)
return err
}
data, err := io.ReadAll(file)
if err != nil {
logx.Info("upload file data error")
return err
}
err = file.Close()
if err != nil {
logx.Info("file close error")
return err
}
field := reqValue.Field(i)
// aa1:
// field.Field(0).Set(fheader.Filename)
// field.Field(1).Set( fheader.Header)
// field.Field(2).Set( fheader.Size)
// field.Field(3).Set( data)
if field.Kind() == reflect.Ptr {
if field.IsNil() {
fsfile := reflect.New(field.Type().Elem())
fsfile = fsfile.Elem()
fsfile.Field(0).Set(reflect.ValueOf(fheader.Filename))
fsfile.Field(1).Set(reflect.ValueOf(fheader.Header))
fsfile.Field(2).Set(reflect.ValueOf(fheader.Size))
fsfile.Field(3).Set(reflect.ValueOf(data))
field.Set(fsfile.Addr())
} else {
field = field.Elem()
field.Field(0).Set(reflect.ValueOf(fheader.Filename))
field.Field(1).Set(reflect.ValueOf(fheader.Header))
field.Field(2).Set(reflect.ValueOf(fheader.Size))
field.Field(3).Set(reflect.ValueOf(data))
}
} else {
field.Field(0).Set(reflect.ValueOf(fheader.Filename))
field.Field(1).Set(reflect.ValueOf(fheader.Header))
field.Field(2).Set(reflect.ValueOf(fheader.Size))
field.Field(3).Set(reflect.ValueOf(data))
}
}
}
return nil
}

61
utils/check/check.go Normal file
View File

@@ -0,0 +1,61 @@
package check
import (
"errors"
"regexp"
"strings"
"unicode/utf8"
)
var category = map[string]bool{
"personalization": true,
}
// CheckCategory 检查是否存在该类型
func CheckCategory(key string) bool {
_, ok := category[key]
return ok
}
// CheckValidS3Key 检查s3的文件名是否符合要求
func CheckValidS3Key(key string) (bool, error) {
if utf8.RuneCountInString(key) > 1024 {
return false, errors.New("对象键长度超过 1024 字节")
}
if strings.HasPrefix(key, "/") || strings.HasPrefix(key, ".") {
return false, errors.New("对象键不应以 / 或 . 开头")
}
if !utf8.ValidString(key) {
return false, errors.New("对象键必须是 UTF-8 字符串")
}
if hasControlCharacters(key) {
return false, errors.New("对象键包含控制字符")
}
if hasInvalidPatterns(key) {
return false, errors.New("对象键包含无效的字符或模式")
}
return true, nil
}
func hasControlCharacters(key string) bool {
controlCharPattern := "[\\x00-\\x1F\\x7F]"
match, _ := regexp.MatchString(controlCharPattern, key)
return match
}
func hasInvalidPatterns(key string) bool {
invalidPatterns := []string{"[\\s]+$", "^\\s+", "\\\\", "//", "\\.\\."}
for _, pattern := range invalidPatterns {
match, _ := regexp.MatchString(pattern, key)
if match {
return true
}
}
return false
}

53
utils/format/s3keyname.go Normal file
View File

@@ -0,0 +1,53 @@
package format
import (
"fmt"
"strings"
"time"
)
type TypeFormatS3KeyName int
const (
TypeS3KeyUser TypeFormatS3KeyName = 1 // 登录用户
TypeS3KeyGuest TypeFormatS3KeyName = 2 // 游客
)
// FormatS3KeyName 需要输入选
func FormatS3KeyName(keytype TypeFormatS3KeyName, uid int64, now time.Time, env, category, name string) string {
if keytype == TypeS3KeyUser {
return FormatS3KeyNameUser(uid, now, env, category, name)
} else if keytype == TypeS3KeyGuest {
return FormatS3KeyNameGuest(uid, now, env, category, name)
} else {
panic("key type error")
}
}
// FormatS3KeyNameUser
func FormatS3KeyNameUser(userid int64, now time.Time, env, category, name string) string {
year, month, _ := now.Date()
names := strings.Split(name, ".")
var ext string
if len(names) == 1 {
name = names[0]
} else if len(names) == 2 {
name = names[0]
ext = names[1]
}
return fmt.Sprintf("/%s/%s/%d/%04d%02d/%s_%d.%s", env, category, userid, year, int(month), name, now.Unix(), ext)
}
// FormatS3KeyNameGuest 游客的格式化存储
func FormatS3KeyNameGuest(guestid int64, now time.Time, env, category, name string) string {
year, month, _ := now.Date()
names := strings.Split(name, ".")
var ext string
if len(names) == 1 {
name = names[0]
} else if len(names) == 2 {
name = names[0]
ext = names[1]
}
return fmt.Sprintf("/%s/guest/%s/%d/%04d%02d/%s_%d.%s", env, category, guestid, year, int(month), name, now.Unix(), ext)
}

View File

@@ -7,11 +7,11 @@ import (
// 字符串切片转int切片
func StrSlicToIntSlice(input []string) ([]int, error) {
newSlic := make([]int, 0, len(input))
for _, p := range input {
if p == "" {
for _, element := range input {
if element == "" {
continue
}
val, err := strconv.Atoi(p)
val, err := strconv.Atoi(element)
if err != nil {
return nil, err
}
@@ -23,11 +23,11 @@ func StrSlicToIntSlice(input []string) ([]int, error) {
// 字符串切片转int64切片
func StrSlicToInt64Slice(input []string) ([]int64, error) {
newSlic := make([]int64, 0, len(input))
for _, p := range input {
if p == "" {
for _, element := range input {
if element == "" {
continue
}
val, err := strconv.ParseInt(p, 10, 64)
val, err := strconv.ParseInt(element, 10, 64)
if err != nil {
return nil, err
}

154
utils/fstests/basic.go Normal file
View File

@@ -0,0 +1,154 @@
package fstests
import (
"fmt"
"os"
"path/filepath"
"runtime"
"strings"
"testing"
"github.com/474420502/requests"
)
func GetEtcYamlPathAuto() string {
var currentFilePath string
var ok bool
for i := 1; i < 4; i++ {
_, currentFilePath, _, ok = runtime.Caller(i)
if !ok {
panic("Error: Unable to get the current file path.")
}
dirs := strings.Split(currentFilePath, "/")
if dirs[len(dirs)-2] != "fstests" {
break
}
}
curdir, err := filepath.Abs(currentFilePath)
if err != nil {
panic(err)
}
curdir = filepath.Dir(curdir)
var limitCount = 10
finfo, err := os.Stat(curdir + "/etc")
for err != nil || !finfo.IsDir() {
curdir = filepath.Dir(curdir)
finfo, err = os.Stat(curdir + "/etc")
limitCount--
if limitCount <= 0 {
panic("out limit")
}
}
lidx := strings.LastIndex(curdir, "/")
return fmt.Sprintf("%s/etc/%s.yaml", curdir, curdir[lidx+1:])
}
func GetCurrentServiceName() string {
_, currentFilePath, _, ok := runtime.Caller(1)
if !ok {
panic("Error: Unable to get the current file path.")
}
curdir, err := filepath.Abs(currentFilePath)
if err != nil {
panic(err)
}
curdir = filepath.Dir(curdir)
var limitCount = 10
finfo, err := os.Stat(curdir + "/etc")
for err != nil || !finfo.IsDir() {
curdir = filepath.Dir(curdir)
finfo, err = os.Stat(curdir + "/etc")
limitCount--
if limitCount <= 0 {
panic("out limit")
}
}
lidx := strings.LastIndex(curdir, "/")
// log.Println(curdir[lidx+1:])
return curdir[lidx+1:]
}
func GetSesssion() *requests.Session {
ses := requests.NewSession()
return ses
}
func GetSessionWithUserToken(t *testing.T, server requests.ITestServer, Host string, Port int) *requests.Session {
ses := requests.NewSession()
tp := ses.Post(fmt.Sprintf("http://%s:%d/user/login", Host, Port))
tp.SetBodyJson(map[string]interface{}{
"name": "9107058@qq.com",
"pwd": "$2y$13$2y4O4OIz/zcK5C0vlSc9LuSpjWySjInLBSe49yDkE.iURb.R1hDsy",
})
resp, err := tp.TestExecute(server)
if err != nil {
t.Error(err)
}
result := resp.Json()
code := result.Get("code").Int()
if code != 200 {
t.Error("code is not 200")
}
token := result.Get("data.token")
if !token.Exists() {
t.Error("data.token is not exists")
}
ses.Header.Add("Authorization", token.String())
return ses
}
func GetBackendSessionWithUserToken(t *testing.T, server requests.ITestServer, Host string, Port int) *requests.Session {
ses := requests.NewSession()
tp := ses.Post(fmt.Sprintf("http://%s:%d/backend-user/login", Host, Port))
tp.SetBodyJson(map[string]interface{}{
"name": "admin@admin.com",
"pwd": "ZnVzZW5fYmFja2VuZF8yMDIz47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU=",
})
resp, err := tp.TestExecute(server)
if err != nil {
t.Error(err)
}
result := resp.Json()
code := result.Get("code").Int()
if code != 200 {
t.Error("code is not 200")
}
token := result.Get("data.token")
if !token.Exists() {
t.Error("data.token is not exists")
}
ses.Header.Add("Authorization", token.String())
return ses
}
func GetSesssionWithGuestToken(t *testing.T, server requests.ITestServer, Host string, Port int) *requests.Session {
ses := requests.NewSession()
tp := ses.Post(fmt.Sprintf("http://%s:%d/accept/cookie", Host, Port))
resp, err := tp.TestExecute(server)
if err != nil {
t.Error(err)
}
result := resp.Json()
token := result.Get("data.token")
if !token.Exists() {
t.Error("data.token is not exists")
}
ses.Header.Add("Authorization", token.String())
return ses
}

View File

@@ -6,7 +6,11 @@ import (
"strings"
)
// html转 Pdf dataType = 1 为网页url dataType = 2为网页内容 outFile为空则不保存(使用该方法需要安装工具 sudo apt-get install wkhtmltopdf)
/*
html转 Pdf
dataType = 1 为网页url dataType = 2为网页内容
outFile为空则不保存(使用该方法需要安装工具 sudo apt-get install wkhtmltopdf)
*/
func HtmlToPdfBase64(content string, dataType int, outFile ...string) (string, error) {
pdfg, err := wkhtmltopdf.NewPDFGenerator()
if err != nil {

View File

@@ -0,0 +1 @@
package requestparser