package dx import ( "crypto/md5" "encoding/hex" "sort" "strings" "time" ) const ( // DefaultBaseURL 电信API基础地址 DefaultBaseURL = "https://cmp-api.ctwing.cn:20164" ) // GetTimestampGMT 返回 GMT 时区的 yyyyMMddHHmmss 格式时间戳 func GetTimestampGMT() string { return time.Now().UTC().Format("20060102150405") } // OrderParams 将 URL params 按 key 字典序排序 func OrderParams(params string) (string, error) { if params == "" { return "", nil } kvs := strings.Split(params, "&") m := make(map[string]string, len(kvs)) for _, kv := range kvs { idx := strings.Index(kv, "=") if idx < 0 { continue } m[kv[:idx]] = kv[idx+1:] } keys := make([]string, 0, len(m)) for k := range m { keys = append(keys, k) } sort.Strings(keys) var parts []string for _, k := range keys { parts = append(parts, k+"="+m[k]) } return strings.Join(parts, "&"), nil } // SignMD5 对 reqStr 做 MD5 加密,返回小写十六进制字符串 func SignMD5(reqStr string) string { h := md5.Sum([]byte(reqStr)) return strings.ToLower(hex.EncodeToString(h[:])) } // BuildSign 构造签名:params + body + secretKey + timestamp → MD5 → hex func BuildSign(params, body, secretKey, timestamp string) (string, error) { ordered, err := OrderParams(params) if err != nil { return "", err } reqStr := ordered + body + secretKey + timestamp md5Str := SignMD5(reqStr) return hex.EncodeToString([]byte(md5Str)), nil } // BuildSignV3 follows the V3 Java sample: encode the lowercase MD5 digest // string as hex a second time, then uppercase that outer encoding. func BuildSignV3(params, body, secretKey, timestamp string) (string, error) { sign, err := BuildSign(params, body, secretKey, timestamp) return strings.ToUpper(sign), err }