package commonservice import ( "crypto/rand" "encoding/hex" "fmt" "strconv" "strings" "time" "nl-pms-api/internal/config" ) // AdminUserID 是 code_count 体系的超级管理员账号(users.id=1)。 const AdminUserID int64 = 1 // ParseID 把字符串解析为非负 int64;非法或负数视为 0。 func ParseID(s string) int64 { n, _ := strconv.ParseInt(s, 10, 64) if n < 0 { return 0 } return n } // Clip 按字节截断字符串到最多 n 字节。 func Clip(s string, n int) string { if len(s) > n { return s[:n] } return s } // ClipRunes 按 rune 截断到最多 n 个字符。 func ClipRunes(s string, n int) string { r := []rune(strings.TrimSpace(s)) if len(r) > n { return string(r[:n]) } return string(r) } // NowRFC 返回当前 UTC 的 RFC3339 时间串(与 view 库惯例一致)。 func NowRFC() string { return time.Now().UTC().Format(time.RFC3339) } // PublicURL 拼接文件公开访问地址:优先配置的 base_url,否则用 requestHost(可含 scheme://host)。 func PublicURL(cfg *config.Config, requestHost, name string) string { base := cfg.BaseURL if base == "" { base = strings.TrimRight(requestHost, "/") if !strings.Contains(base, "://") { base = "http://" + base } } return base + "/files/" + name } // NormalizeKind 规范化文件用途:avatar | content,其余为空。 func NormalizeKind(k string) string { if k == "avatar" || k == "content" { return k } return "" } // StoredName 生成不可枚举的存储相对路径(日期目录 + 128 位随机 hex)。 func StoredName(ext string) string { b := make([]byte, 16) _, _ = rand.Read(b) return time.Now().UTC().Format("2006/01/02") + "/" + hex.EncodeToString(b) + ext } // MapStr 从通用 JSON map 取字符串字段。 func MapStr(m map[string]any, key string) string { v, ok := m[key] if !ok || v == nil { return "" } switch x := v.(type) { case string: return x case []byte: return string(x) case float64: if x == float64(int64(x)) { return strconv.FormatInt(int64(x), 10) } return strconv.FormatFloat(x, 'f', -1, 64) case bool: if x { return "1" } return "0" default: return fmt.Sprint(x) } } // MapInt64 从通用 JSON map 取 int64 字段。 func MapInt64(m map[string]any, key string) int64 { v, ok := m[key] if !ok || v == nil { return 0 } switch x := v.(type) { case float64: return int64(x) case int64: return x case int: return int64(x) case string: n, _ := strconv.ParseInt(x, 10, 64) return n case bool: if x { return 1 } return 0 default: return 0 } }