daShangDao_psiServer/utils/helper.go
2026-06-23 14:26:46 +08:00

345 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 utils
import (
"bytes"
"crypto/md5"
"encoding/hex"
"fmt"
"github.com/gin-gonic/gin"
"io"
"log"
"math/rand"
"mime/multipart"
"net/http"
"net/url"
"sort"
"strings"
"time"
"unicode/utf8"
)
// GenerateEmployeeID 生成工号 (5位数字不足补零)
func GenerateEmployeeID(seq int) string {
return fmt.Sprintf("%05d", seq)
}
// GenerateUsername 生成用户名 (init_工号)
func GenerateUsername(employeeID string) string {
return "dl_" + employeeID
}
// GeneratePoNo 生成采购单号: PO + 年月日时分秒 + 随机数
func GeneratePoNo() string {
return fmt.Sprintf("PO%s%04d", time.Now().Format("20060102150405"), GenerateRandomNumber(4))
}
// GenerateTaskNo 生成任务编号: TK + 年月日时分秒 + 随机数
func GenerateTaskNo() string {
return fmt.Sprintf("TK%s%04d", time.Now().Format("20060102150405"), GenerateRandomNumber(4))
}
// GenerateTaskDetailNo 生成批次号: TD + 年月日时分秒 + 随机数
func GenerateTaskDetailNo() string {
return fmt.Sprintf("TD%s%04d", time.Now().Format("20060102150405"), GenerateRandomNumber(4))
}
// GenerateExcelImportBatchNo 生成Excel表格入库批次号: TE(TableExcel) + 年月日时分秒 + 随机数
func GenerateExcelImportBatchNo() string {
return fmt.Sprintf("TE%s%04d", time.Now().Format("20060102150405"), GenerateRandomNumber(4))
}
// GenerateReceivingNo 生成入库单号: RC + 年月日时分秒 + 随机数
func GenerateReceivingNo() string {
return fmt.Sprintf("RC%s%04d", time.Now().Format("20060102150405"), GenerateRandomNumber(4))
}
// GenerateSalesNo 生成销售单号: SO + 年月日时分秒 + 随机数
func GenerateSalesNo() string {
return fmt.Sprintf("SO%s%04d", time.Now().Format("20060102150405"), GenerateRandomNumber(4))
}
// GenerateShippingNo 生成发货单号: SH + 年月日时分秒 + 随机数
func GenerateShippingNo() string {
return fmt.Sprintf("SH%s%04d", time.Now().Format("20060102150405"), GenerateRandomNumber(4))
}
// GenerateStockCheckNo 生成盘库调整单号: SC + 年月日时分秒 + 随机数
func GenerateStockCheckNo() string {
return fmt.Sprintf("SC%s%04d", time.Now().Format("20060102150405"), GenerateRandomNumber(4))
}
// GenerateReturnNo 生成退货单号: RT + 年月日时分秒 + 随机数
func GenerateReturnNo() string {
return fmt.Sprintf("RT%s%04d", time.Now().Format("20060102150405"), GenerateRandomNumber(4))
}
// GenerateOutNo 生成出库单号: OB + 年月日时分秒 + 随机数
func GenerateOutNo() string {
return fmt.Sprintf("OB%s%04d", time.Now().Format("20060102150405"), GenerateRandomNumber(4))
}
// GenerateRandomNumber 生成指定位数的随机数字
func GenerateRandomNumber(digits int) int {
if digits <= 0 {
return 0
}
end := 1
for i := 0; i < digits; i++ {
end *= 10
}
return rand.Intn(end)
}
// ParsePublicationTime 支持的格式:"2013-12", "2013", "2013-12-25", "2013 年 12 月" 等
func ParsePublicationTime(timeStr string) int64 {
if timeStr == "" {
return 0
}
// 清理字符串中的空格和特殊字符
timeStr = strings.TrimSpace(timeStr)
timeStr = strings.ReplaceAll(timeStr, "年", "-")
timeStr = strings.ReplaceAll(timeStr, "月", "")
timeStr = strings.TrimSpace(timeStr)
// 定义多种可能的日期格式
formats := []string{
"2006-1", // "2013-12"
"2006-01", // "2013-12" (补零)
"2006", // "2013"
"2006-1-2", // "2013-12-25"
"2006-01-02", // "2013-12-25" (补零)
}
var t time.Time
var err error
for _, format := range formats {
t, err = time.ParseInLocation(format, timeStr, time.Local)
if err == nil {
break
}
}
// 如果所有格式都失败,返回 0
if err != nil {
fmt.Printf("出版时间解析失败:%s\n", timeStr)
return 0
}
return t.Unix()
}
// GetClientIP 获取客户端IP
func GetClientIP(c *gin.Context) string {
ip := c.ClientIP()
if ip == "::1" {
ip = "127.0.0.1"
}
return ip
}
type UserInfo struct {
ID int64
Role int64
Username string
AboutID int64
Fid int64
}
func GetUserInfo(c *gin.Context) UserInfo {
return UserInfo{
ID: c.GetInt64("id"),
Role: c.GetInt64("role"),
Username: c.GetString("username"),
AboutID: c.GetInt64("about_id"),
Fid: c.GetInt64("fid"),
}
}
func Ternary[T any](condition bool, trueVal, falseVal T) T {
if condition {
return trueVal
}
return falseVal
}
// SignParams 对请求参数进行MD5签名用于接口安全校验
// 参数 params: 待签名的原始参数键值对
// 返回值: 计算出的签名字符串大写MD5
func SignParams(params map[string]string) string {
// 定义签名密钥(实际使用中应从配置或环境变量获取,避免硬编码)
SecretKey := "jRQdCh52Z55Kzh1hADaA2ZtdTKetj2PXk60Tz5Yc0iz9aD8Wafbk7CwAZ8cz69A9zb9caZ3k9dnR3Ys06J5nYFPrZ0xE9p6TY8DCD538ryiRjW81YTPmk41tCEnXizPh"
// 创建过滤后的参数映射表,用于存放需要参与签名的参数
filteredParams := make(map[string]string)
// 遍历原始参数过滤掉空值、sign字段和sign_type字段
for k, v := range params {
// 如果参数值不为空且参数名不是sign且参数名不是sign_type则保留
if v != "" && k != "sign" && k != "sign_type" {
filteredParams[k] = v
}
}
// 提取过滤后参数的所有键名,用于排序
keys := make([]string, 0, len(filteredParams))
for k := range filteredParams {
keys = append(keys, k)
}
// 对键名进行字典序排序(保证签名结果的一致性)
sort.Strings(keys)
// 使用strings.Builder高效拼接字符串
var builder strings.Builder
// 遍历排序后的键名,按"key=value"格式拼接,并用"&"连接
for i, k := range keys {
// 从第二个参数开始,前面添加"&"分隔符
if i > 0 {
builder.WriteString("&")
}
builder.WriteString(k) // 写入参数名
builder.WriteString("=") // 写入等号
builder.WriteString(filteredParams[k]) // 写入参数值
}
// 将拼接好的参数字符串末尾加上"&key=密钥",形成待签名字符串
signStr := builder.String() + "&key=" + SecretKey
// 计算待签名字符串的MD5哈希值
hash := md5.Sum([]byte(signStr))
// 将哈希结果转换为十六进制字符串,并转为大写形式返回
return strings.ToUpper(hex.EncodeToString(hash[:]))
}
// SubmitMultiBody 发送多个 body 字段
func SubmitMultiBody(url string, taskID string, bodyList []string, sign string) (string, error) {
body := &bytes.Buffer{}
writer := multipart.NewWriter(body)
// 发送多个独立 body业务用
for _, b := range bodyList {
_ = writer.WriteField("body", b)
}
_ = writer.WriteField("task_id", taskID)
_ = writer.WriteField("sign", sign)
writer.Close()
req, _ := http.NewRequest("POST", url, body)
req.Header.Set("Content-Type", writer.FormDataContentType())
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return "", err
}
defer resp.Body.Close()
respBody, _ := io.ReadAll(resp.Body)
return string(respBody), nil
}
// SubmitFormData 提交表单数据
// @param url 请求地址
// @param params 表单数据
// @return error 错误信息
func SubmitFormData(url string, params map[string]string) (string, error) {
body := &bytes.Buffer{}
writer := multipart.NewWriter(body)
for key, value := range params {
err := writer.WriteField(key, value)
if err != nil {
return "", fmt.Errorf("write field error: %v", err)
}
}
writer.Close()
req, err := http.NewRequest("POST", url, body)
if err != nil {
return "", fmt.Errorf("create request error: %v", err)
}
req.Header.Set("Content-Type", writer.FormDataContentType())
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return "", fmt.Errorf("send request error: %v", err)
}
defer resp.Body.Close()
respBody, err := io.ReadAll(resp.Body)
if err != nil {
return "", fmt.Errorf("read response error: %v", err)
}
return string(respBody), nil
}
// SubmitFormDataWithTimeout 提交表单数据(application/x-www-form-urlencoded)
func SubmitFormDataWithTimeout(requestURL string, params map[string]string, timeoutSeconds int) (string, error) {
form := url.Values{}
for key, value := range params {
form.Set(key, value)
}
log.Printf("[旺店通推送] 发送form-urlencoded:\n%s", form.Encode())
req, err := http.NewRequest("POST", requestURL, strings.NewReader(form.Encode()))
if err != nil {
return "", fmt.Errorf("create request error: %v", err)
}
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
timeout := time.Duration(timeoutSeconds) * time.Second
if timeout <= 0 {
timeout = 30 * time.Second
}
client := &http.Client{Timeout: timeout}
resp, err := client.Do(req)
if err != nil {
return "", fmt.Errorf("send request error: %v", err)
}
defer resp.Body.Close()
respBody, err := io.ReadAll(resp.Body)
if err != nil {
return "", fmt.Errorf("read response error: %v", err)
}
return string(respBody), nil
}
// WangdianSign 旺店通接口签名算法
// 与PHP SDK保持一致使用UTF-8字符长度(iconv_strlen)而非字节长度
func WangdianSign(params map[string]string, appSecret string) string {
keys := make([]string, 0, len(params))
for k := range params {
if k == "sign" {
continue
}
keys = append(keys, k)
}
sort.Strings(keys)
var builder strings.Builder
for idx, key := range keys {
value := params[key]
keyLen := utf8.RuneCountInString(key)
valLen := utf8.RuneCountInString(value)
builder.WriteString(fmt.Sprintf("%02d", keyLen))
builder.WriteString("-")
builder.WriteString(key)
builder.WriteString(":")
builder.WriteString(fmt.Sprintf("%04d", valLen))
builder.WriteString("-")
builder.WriteString(value)
if idx < len(keys)-1 {
builder.WriteString(";")
}
}
builder.WriteString(appSecret)
hash := md5.Sum([]byte(builder.String()))
return hex.EncodeToString(hash[:])
}