241 lines
6.1 KiB
Go
241 lines
6.1 KiB
Go
package utils
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"github.com/gin-gonic/gin"
|
|
"math"
|
|
"os"
|
|
"runtime"
|
|
"sort"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
func Exit(err any, str string) {
|
|
switch arg := err.(type) {
|
|
case error:
|
|
if arg != nil {
|
|
fmt.Println(str, arg.Error())
|
|
os.Exit(1)
|
|
}
|
|
case bool:
|
|
if arg {
|
|
fmt.Println(str, err)
|
|
os.Exit(1)
|
|
}
|
|
}
|
|
}
|
|
|
|
func GetReqPath(c *gin.Context) string {
|
|
return c.Request.Method + " " + c.Request.URL.String()
|
|
}
|
|
|
|
func TimeFormat(t time.Time) string {
|
|
timeString := t.Format("2006/01/02 - 15:04:05")
|
|
return timeString
|
|
}
|
|
|
|
// GetMinutesFromTimeRange 接受时间范围字符串,返回开始分钟、结束分钟和总分钟数
|
|
func GetMinutesFromTimeRange(startTimeStr, endTimeStr string) (int, int, int, error) {
|
|
startTimeParts := strings.Split(startTimeStr, ":")
|
|
endTimeParts := strings.Split(endTimeStr, ":")
|
|
if len(startTimeParts) != 2 || len(endTimeParts) != 2 {
|
|
return 0, 0, 0, fmt.Errorf("时间格式不正确,应该是 HH:MM")
|
|
}
|
|
startHour, err := strconv.Atoi(startTimeParts[0])
|
|
if err != nil {
|
|
return 0, 0, 0, fmt.Errorf("无效的开始小时: %s", startTimeParts[0])
|
|
}
|
|
startMinute, err := strconv.Atoi(startTimeParts[1])
|
|
if err != nil {
|
|
return 0, 0, 0, fmt.Errorf("无效的开始分钟: %s", startTimeParts[1])
|
|
}
|
|
endHour, err := strconv.Atoi(endTimeParts[0])
|
|
if err != nil {
|
|
return 0, 0, 0, fmt.Errorf("无效的结束小时: %s", endTimeParts[0])
|
|
}
|
|
endMinute, err := strconv.Atoi(endTimeParts[1])
|
|
if err != nil {
|
|
return 0, 0, 0, fmt.Errorf("无效的结束分钟: %s", endTimeParts[1])
|
|
}
|
|
// 计算开始和结束的总分钟
|
|
startMinutes := startHour*60 + startMinute
|
|
endMinutes := endHour*60 + endMinute
|
|
// 判断开始时间和结束时间的大小关系
|
|
if startMinutes >= endMinutes {
|
|
return 0, 0, 0, fmt.Errorf("开始时间必须小于结束时间")
|
|
}
|
|
// 计算总分钟数
|
|
totalMinutes := endMinutes - startMinutes
|
|
return startMinutes, endMinutes, totalMinutes, nil
|
|
}
|
|
|
|
var (
|
|
dunno = []byte("???")
|
|
centerDot = []byte("·")
|
|
dot = []byte(".")
|
|
slash = []byte("/")
|
|
)
|
|
|
|
func Stack(skip int) {
|
|
var lines [][]byte
|
|
var lastFile string
|
|
for i := skip; ; i++ { // Skip the expected number of frames
|
|
pc, file, line, ok := runtime.Caller(i)
|
|
if !ok {
|
|
break
|
|
}
|
|
fmt.Printf("%s:%d (0x%x)\n", file, line, pc)
|
|
if file != lastFile {
|
|
data, err := os.ReadFile(file)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
lines = bytes.Split(data, []byte{'\n'})
|
|
lastFile = file
|
|
}
|
|
fmt.Printf("\t%s: %s\n", function(pc), source(lines, line))
|
|
}
|
|
}
|
|
|
|
func source(lines [][]byte, n int) []byte {
|
|
n-- // in stack trace, lines are 1-indexed but our array is 0-indexed
|
|
if n < 0 || n >= len(lines) {
|
|
return dunno
|
|
}
|
|
return bytes.TrimSpace(lines[n])
|
|
}
|
|
|
|
// function returns, if possible, the name of the function containing the PC.
|
|
func function(pc uintptr) []byte {
|
|
fn := runtime.FuncForPC(pc)
|
|
if fn == nil {
|
|
return dunno
|
|
}
|
|
name := []byte(fn.Name())
|
|
// The name includes the path name to the package, which is unnecessary
|
|
// since the file name is already included. Plus, it has center dots.
|
|
// That is, we see
|
|
// runtime/debug.*T·ptrmethod
|
|
// and want
|
|
// *T.ptrmethod
|
|
// Also the package path might contains dot (e.g. code.google.com/...),
|
|
// so first eliminate the path prefix
|
|
if lastSlash := bytes.LastIndex(name, slash); lastSlash >= 0 {
|
|
name = name[lastSlash+1:]
|
|
}
|
|
if period := bytes.Index(name, dot); period >= 0 {
|
|
name = name[period+1:]
|
|
}
|
|
name = bytes.Replace(name, centerDot, dot, -1)
|
|
return name
|
|
}
|
|
|
|
// ExtractNumber 提取数字并返回整数
|
|
func ExtractNumber(id string) (int, error) {
|
|
parts := strings.Split(id, "_")
|
|
if len(parts) == 0 {
|
|
return 0, fmt.Errorf("无效的 Id: %s", id)
|
|
}
|
|
numStr := parts[0]
|
|
num, err := strconv.Atoi(numStr)
|
|
if err != nil {
|
|
return 0, fmt.Errorf("转换错误: %v", err)
|
|
}
|
|
return num, nil
|
|
}
|
|
|
|
// GetSplitIntMaxMin 函数返回切片中的最大值和最小值
|
|
func GetSplitIntMaxMin(nums []int) (max int, min int, err error) {
|
|
if len(nums) == 0 {
|
|
return 0, 0, fmt.Errorf("切片为空")
|
|
}
|
|
|
|
// 初始化最大值和最小值
|
|
max = math.MinInt64 // 设置为最小整数
|
|
min = math.MaxInt64 // 设置为最大整数
|
|
|
|
// 遍历切片,找到最大值和最小值
|
|
for _, num := range nums {
|
|
if num > max {
|
|
max = num
|
|
}
|
|
if num < min {
|
|
min = num
|
|
}
|
|
}
|
|
|
|
return max, min, nil
|
|
}
|
|
|
|
// SplitIntoGroups 切片根据是否连续来进行分组
|
|
func SplitIntoGroups(nums []int) [][]int {
|
|
if len(nums) == 0 {
|
|
return nil
|
|
}
|
|
|
|
// 将切片排序
|
|
sort.Ints(nums)
|
|
|
|
var groups [][]int
|
|
var currentGroup []int
|
|
|
|
// 初始化第一组
|
|
currentGroup = append(currentGroup, nums[0])
|
|
|
|
for i := 1; i < len(nums); i++ {
|
|
// 检查当前数字与前一个数字的差值
|
|
if nums[i] == nums[i-1]+1 {
|
|
// 如果是连续的,添加到当前组
|
|
currentGroup = append(currentGroup, nums[i])
|
|
} else {
|
|
// 如果不连续,保存当前组并开始新的一组
|
|
groups = append(groups, currentGroup)
|
|
currentGroup = []int{nums[i]} // 开始新的一组
|
|
}
|
|
}
|
|
|
|
// 添加最后一组
|
|
groups = append(groups, currentGroup)
|
|
|
|
return groups
|
|
}
|
|
|
|
// IsInRange 检查一个整数是否在指定的区间内
|
|
func IsInRange(number, lowerBound, upperBound int) bool {
|
|
return number >= lowerBound && number <= upperBound
|
|
}
|
|
|
|
// Interval 表示一个区间
|
|
type Interval struct {
|
|
start int
|
|
end int
|
|
}
|
|
|
|
// ByStart 实现 sort.Interface 接口,用于按区间的开始位置排序
|
|
type ByStart []Interval
|
|
|
|
func (a ByStart) Len() int { return len(a) }
|
|
func (a ByStart) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
|
func (a ByStart) Less(i, j int) bool { return a[i].start < a[j].start }
|
|
|
|
// HasOverlap 检查切片中的所有区间是否有重叠
|
|
func HasOverlap(timeGroup [][]int) bool {
|
|
// 将切片转换为 Interval 结构体切片
|
|
var intervals []Interval
|
|
for _, interval := range timeGroup {
|
|
intervals = append(intervals, Interval{start: interval[0], end: interval[1]})
|
|
}
|
|
// 按照区间的开始位置排序
|
|
sort.Sort(ByStart(intervals))
|
|
// 检查相邻区间是否重叠
|
|
for i := 0; i < len(intervals)-1; i++ {
|
|
if intervals[i].end > intervals[i+1].start {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|