feat(config): 更新配置文件并新增自定义时间类型

- 修改 config.yaml 中的 API 地址、存储配置及设备信息
- 新增 Viid 配置项支持新功能模块
- 在 go.mod 和 go.sum 中更新依赖包版本,引入新的第三方库
- 添加 model/custom_time.go 文件实现自定义时间类型的 JSON 序列化与反序列化
- 调整 DTO 结构体以适配新的配置项
- 升级 OTLP 导出器相关依赖并移除旧的标准输出导出器
- 引入 Gin 框架及相关中间件提升服务性能
- 更新 Protobuf 和 gRPC 相关依赖至最新版本
- 增加 zap 日志库和 lumberjack 日志轮转支持
- 添加 sonic、json-iterator 等高性能 JSON 处理库优化数据解析效率
- 引入 testify 断言库增强测试代码可读性
- 更新 sync 包版本提高并发安全性
- 添加 mock 工具支持单元测试模拟对象
- 引入 validator/v10 实现请求参数校验功能
- 更新 crypto、net、text 等标准库依赖确保安全性和兼容性
- 增加 mimetype 库用于文件类型识别
- 引入 quic-go 支持 HTTP/3 协议通信
- 添加 base64x
This commit is contained in:
2025-11-24 16:12:25 +08:00
parent 4b1eb11986
commit 11f508342d
9 changed files with 698 additions and 54 deletions

96
util/image.go Normal file
View File

@@ -0,0 +1,96 @@
package util
import (
"bytes"
"fmt"
"image"
"image/draw"
"image/jpeg"
_ "image/png" // Register PNG decoder just in case
)
// GenerateThumbnail creates a thumbnail by cropping the original image.
// The thumbnail size is 1/2 of the original dimensions.
// The crop is centered on the provided coordinates (centerX, centerY), constrained to image bounds.
func GenerateThumbnail(srcData []byte, centerX, centerY int) ([]byte, error) {
// 1. Decode image
img, _, err := image.Decode(bytes.NewReader(srcData))
if err != nil {
return nil, fmt.Errorf("decode image failed: %v", err)
}
bounds := img.Bounds()
origW := bounds.Dx()
origH := bounds.Dy()
// 2. Calculate Target Dimensions (1/2 of original)
targetW := origW / 2
targetH := origH / 2
if targetW == 0 || targetH == 0 {
return nil, fmt.Errorf("image too small to generate half-size thumbnail")
}
// 3. Calculate Crop Origin (Top-Left)
// cropX = centerX - targetW / 2
cropX := centerX - targetW/2
cropY := centerY - targetH/2
// 4. Constrain to Bounds
if cropX < 0 {
cropX = 0
}
if cropY < 0 {
cropY = 0
}
if cropX+targetW > origW {
cropX = origW - targetW
}
if cropY+targetH > origH {
cropY = origH - targetH
}
// Double check after adjustment
if cropX < 0 {
cropX = 0
}
if cropY < 0 {
cropY = 0
}
// 5. Perform Crop
cropRect := image.Rect(cropX, cropY, cropX+targetW, cropY+targetH)
var dst image.Image
// Try to use SubImage if supported for performance
type SubImager interface {
SubImage(r image.Rectangle) image.Image
}
if sub, ok := img.(SubImager); ok {
dst = sub.SubImage(cropRect)
} else {
// Fallback: create new image and draw
rgba := image.NewRGBA(image.Rect(0, 0, targetW, targetH))
draw.Draw(rgba, rgba.Bounds(), img, cropRect.Min, draw.Src)
dst = rgba
}
// 6. Encode to JPEG
var buf bytes.Buffer
err = jpeg.Encode(&buf, dst, &jpeg.Options{Quality: 85})
if err != nil {
return nil, fmt.Errorf("encode thumbnail failed: %v", err)
}
return buf.Bytes(), nil
}
// Helper to process using LeftTop and RightBottom coordinates
func GenerateThumbnailFromCoords(srcData []byte, ltX, ltY, rbX, rbY int) ([]byte, error) {
// Calculate center based on the provided coordinates
centerX := (ltX + rbX) / 2
centerY := (ltY + rbY) / 2
return GenerateThumbnail(srcData, centerX, centerY)
}