daShangDao_planA/tool/tool.go

394 lines
9.7 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package tool
import (
"context"
"encoding/json"
"fmt"
"net/http"
"net/url"
"os"
"path/filepath"
"planA/initialization/golabl"
_type "planA/type"
"reflect"
"strconv"
"strings"
"time"
)
// CheckContext 检查上下文是否取消
func CheckContext(ctx context.Context) error {
select {
case <-ctx.Done():
return ctx.Err() // 返回取消原因
default:
return nil // 上下文仍然有效
}
}
// StructToMap 使用反射将结构体转换为map[string]interfaces{}
// @param obj 需要转换的数据
// @return map[string]interface{} 转换后的数据
// @return error 错误信息
func StructToMap(obj interface{}) (map[string]interface{}, error) {
result := make(map[string]interface{})
val := reflect.ValueOf(obj)
typ := reflect.TypeOf(obj)
// 如果是指针,获取指向的值
if val.Kind() == reflect.Ptr {
val = val.Elem()
typ = typ.Elem()
}
// 确保是结构体
if val.Kind() != reflect.Struct {
return nil, fmt.Errorf("参数必须是结构体")
}
// 遍历结构体字段
for i := 0; i < val.NumField(); i++ {
field := typ.Field(i)
fieldValue := val.Field(i)
// 跳过不可导出的字段
if !field.IsExported() {
continue
}
// 获取json标签作为字段名
jsonTag := field.Tag.Get("json")
fieldName := field.Name
if jsonTag != "" {
// 处理 json tag忽略 omitempty 等选项
if idx := strings.Index(jsonTag, ","); idx != -1 {
fieldName = jsonTag[:idx]
} else {
fieldName = jsonTag
}
// 跳过标记为 "-" 的字段
if fieldName == "-" {
continue
}
}
// 检查字段类型
switch fieldValue.Kind() {
case reflect.Struct:
// 处理结构体字段(嵌套结构体)
nestedValue := fieldValue.Interface()
// 检查是否是 time.Time 类型
if _, ok := nestedValue.(time.Time); ok {
// time.Time 类型转换为时间戳
result[fieldName] = nestedValue.(time.Time).Unix()
continue
}
// 检查是否是自定义类型
switch v := nestedValue.(type) {
case _type.TaskStatus:
// TaskStatus 转换为 int64
result[fieldName] = int64(v)
case _type.ShopMsg:
// ShopMsg 转换为 JSON 字符串
shopMsgJSON, err := json.Marshal(v)
if err != nil {
return nil, fmt.Errorf("序列化 ShopMsg 失败: %v", err)
}
result[fieldName] = string(shopMsgJSON)
case _type.PriceMod:
// PriceMod 转换为 JSON 字符串
priceModJSON, err := json.Marshal(v)
if err != nil {
return nil, fmt.Errorf("序列化 PriceMod 失败: %v", err)
}
result[fieldName] = string(priceModJSON)
default:
// 其他结构体转换为 JSON 字符串
nestedJSON, err := json.Marshal(nestedValue)
if err != nil {
return nil, fmt.Errorf("序列化字段 %s 失败: %v", fieldName, err)
}
result[fieldName] = string(nestedJSON)
}
default:
// 处理基础类型字段
if !fieldValue.CanInterface() {
continue
}
// 处理指针类型
if fieldValue.Kind() == reflect.Ptr && !fieldValue.IsNil() {
elemValue := fieldValue.Elem()
if elemValue.Kind() == reflect.Struct {
// 指针指向结构体,转换为 JSON 字符串
nestedJSON, err := json.Marshal(elemValue.Interface())
if err != nil {
return nil, fmt.Errorf("序列化指针字段 %s 失败: %v", fieldName, err)
}
result[fieldName] = string(nestedJSON)
} else {
// 指针指向基础类型
result[fieldName] = elemValue.Interface()
}
} else {
// 直接存储值,但处理自定义类型
switch v := fieldValue.Interface().(type) {
case _type.TaskStatus:
// TaskStatus 转换为 int64
result[fieldName] = int64(v)
default:
result[fieldName] = fieldValue.Interface()
}
}
}
}
return result, nil
}
// SetPage 分页处理
func SetPage(pageStr string, sizeStr string) (int, int) {
// 处理页码默认为1
page := 1
if pageStr != "" {
if p, err := strconv.Atoi(pageStr); err == nil && p > 0 {
page = p
}
}
// 处理每页条数默认为10
size := 10
if sizeStr != "" {
if s, err := strconv.Atoi(sizeStr); err == nil && s > 0 {
// 可以限制最大条数
if s > 100 {
size = 100
} else {
size = s
}
}
}
return page, size
}
// Success 成功响应
// @param httpMsg http.ResponseWriter
// @param data 返回的数据
func Success(httpMsg http.ResponseWriter, data any) {
ret := map[string]interface{}{
"code": "200",
"msg": "成功",
"data": data,
}
json.NewEncoder(httpMsg).Encode(ret)
}
// Error 错误响应
// @param httpMsg http.ResponseWriter
// @param msg 错误信息
// @param code 错误码
func Error(httpMsg http.ResponseWriter, msg string, code int) {
fmt.Println("错误:" + msg)
codeStr := strconv.FormatInt(int64(code), 10)
ret := map[string]interface{}{
"code": codeStr,
"msg": msg,
}
json.NewEncoder(httpMsg).Encode(ret)
}
// HttpBannedWordSubstitution 违禁词处理
func HttpBannedWordSubstitution(url string, reqData map[string]string) (_type.HttpBannedWordSubstitutionBookInfoRes, error) {
var resDta _type.HttpBannedWordSubstitutionBookInfoRes
// 构建带参数的 URL
reqUrl, err := BuildURLWithParams(url, reqData)
if err != nil {
return resDta, fmt.Errorf("构建URL失败: %v", err)
}
// 发送 GET请求
_, resStr, httpGetRequestErr := HttpGetRequest(reqUrl)
if httpGetRequestErr != nil {
return resDta, httpGetRequestErr
}
// 将字符串转换为结构体
jsonErr := json.Unmarshal([]byte(resStr), &resDta)
if jsonErr != nil {
return resDta, jsonErr
}
if resDta.Code != "200" {
return resDta, fmt.Errorf("请求违禁词接口错误 错误: url %s %s", reqUrl, resStr)
}
// 返回结果
return resDta, nil
}
// BuildURLWithParams 将map参数拼接到URL后面
func BuildURLWithParams(baseURL string, params map[string]string) (string, error) {
if len(params) == 0 {
return baseURL, nil
}
// 解析基础URL
parsedURL, err := url.Parse(baseURL)
if err != nil {
return "", fmt.Errorf("解析URL失败: %v", err)
}
// 获取现有的查询参数
query := parsedURL.Query()
// 添加新的参数
for key, value := range params {
query.Set(key, value)
}
// 重新编码查询参数
parsedURL.RawQuery = query.Encode()
return parsedURL.String(), nil
}
// FilterStrings 过滤掉数组中的空字符串
func FilterStrings(s []string) []string {
result := make([]string, 0, len(s))
// 固定写死要过滤的字符串
filterMap := map[string]bool{
"": true,
"图片格式错误图片格式非jpeg请自查图片格式": true,
}
for _, str := range s {
if !filterMap[str] {
result = append(result, str)
}
}
return result
}
// JsonResponse JSON响应工具函数
// 统一处理API响应的JSON格式化和发送
// 参数:
// - w: HTTP响应写入器
// - statusCode: HTTP状态码
// - resp: API响应数据
func JsonResponse(w http.ResponseWriter, statusCode int, resp _type.APIResponse) {
// 设置响应头为JSON格式
w.Header().Set("Content-Type", "application/json")
// 设置HTTP状态码
w.WriteHeader(statusCode)
// 编码JSON并写入响应体
err := json.NewEncoder(w).Encode(resp)
if err != nil {
// 编码失败时打印错误到控制台(不中断请求)
fmt.Printf("JSON编码错误: %v\n", err)
return
}
}
// StringToArray 将字符串根据/转为数组
func StringToArray(str string) []string {
// 1. 分割字符串
parts := strings.Split(str, "/")
// 2. 创建结果切片
result := make([]string, 0, len(parts))
// 3. 遍历转换
for _, part := range parts {
result = append(result, part)
}
return result
}
// GetTaskTypeName 获取任务类型名称
// @param taskType 任务类型
// @return string 任务类型名称
func GetTaskTypeName(taskType string) string {
switch taskType {
case "1":
return "新发布商品任务"
case "2":
return "新发布商品任务"
case "3":
return "新拉取商品任务"
}
return "未知任务类型"
}
// CleanOldFolders 删除指定目录中超过保留天数的日期文件夹
// dirPath: 要清理的目录路径
// keepDays: 保留的天数(保留最近 keepDays 天的文件夹)
func CleanOldFolders(dirPath string, keepDays int) error {
// 读取目录内容
entries, err := os.ReadDir(dirPath)
if err != nil {
return fmt.Errorf("读取目录失败: %v", err)
}
// 计算截止日期(保留最近 keepDays 天,即删除 keepDays 天之前的)
cutoffDate := time.Now().AddDate(0, 0, -keepDays)
deletedCount := 0
for _, entry := range entries {
// 只处理目录
if !entry.IsDir() {
continue
}
folderName := entry.Name()
// 尝试解析文件夹名为日期格式 (2006-01-02)
folderDate, err := time.Parse("2006-01-02", folderName)
if err != nil {
// 不是日期格式的文件夹,跳过
fmt.Printf("跳过非日期格式文件夹: %s\n", folderName)
continue
}
// 如果文件夹日期早于截止日期,则删除
if folderDate.Before(cutoffDate) {
folderPath := filepath.Join(dirPath, folderName)
fmt.Printf("删除过期文件夹: %s\n", folderPath)
err := os.RemoveAll(folderPath)
if err != nil {
fmt.Printf("删除失败 %s: %v\n", folderPath, err)
} else {
deletedCount++
}
}
}
fmt.Printf("共删除 %d 个过期文件夹\n", deletedCount)
return nil
}
// GetSubscriptionExpirationTime 根据用户id获取订阅到期时间
func GetSubscriptionExpirationTime(shopId string) (int64, error) {
url := golabl.Config.FileUrl.GetSubscriptionExpirationDateUrl + "?userId=" + shopId
_, dataStr, httpGetRequestErr := HttpGetRequest(url)
if httpGetRequestErr != nil {
return 0, httpGetRequestErr
}
var getSubscriptionExpirationDateUrl _type.GetSubscriptionExpirationDateUrl
jsonErr := json.Unmarshal([]byte(dataStr), &getSubscriptionExpirationDateUrl)
if jsonErr != nil {
return 0, jsonErr
}
return getSubscriptionExpirationDateUrl.Data.ExpirationDate, nil
}