2025-12-04 18:10:14 +08:00
|
|
|
|
package pdf
|
|
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
|
"crypto/md5"
|
|
|
|
|
|
"encoding/hex"
|
|
|
|
|
|
"fmt"
|
|
|
|
|
|
"os"
|
|
|
|
|
|
"path/filepath"
|
|
|
|
|
|
"sync"
|
|
|
|
|
|
"time"
|
|
|
|
|
|
|
|
|
|
|
|
"go.uber.org/zap"
|
|
|
|
|
|
)
|
|
|
|
|
|
|
2026-01-27 16:26:48 +08:00
|
|
|
|
// PDFCacheManager PDF缓存管理器(统一实现)
|
|
|
|
|
|
// 支持两种缓存键生成方式:
|
|
|
|
|
|
// 1. 基于姓名+身份证(用于PDFG报告)
|
|
|
|
|
|
// 2. 基于产品ID+版本(用于产品文档)
|
2025-12-04 18:10:14 +08:00
|
|
|
|
type PDFCacheManager struct {
|
|
|
|
|
|
logger *zap.Logger
|
|
|
|
|
|
cacheDir string
|
|
|
|
|
|
ttl time.Duration // 缓存过期时间
|
2026-01-27 16:26:48 +08:00
|
|
|
|
maxSize int64 // 最大缓存大小(字节,0表示不限制)
|
2025-12-04 18:10:14 +08:00
|
|
|
|
mu sync.RWMutex // 保护并发访问
|
|
|
|
|
|
cleanupOnce sync.Once // 确保清理任务只启动一次
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// NewPDFCacheManager 创建PDF缓存管理器
|
2026-01-27 16:26:48 +08:00
|
|
|
|
// cacheDir: 缓存目录(空则使用默认目录)
|
|
|
|
|
|
// ttl: 缓存过期时间
|
|
|
|
|
|
// maxSize: 最大缓存大小(字节,0表示不限制)
|
2025-12-04 18:10:14 +08:00
|
|
|
|
func NewPDFCacheManager(logger *zap.Logger, cacheDir string, ttl time.Duration, maxSize int64) (*PDFCacheManager, error) {
|
2026-01-27 16:26:48 +08:00
|
|
|
|
// 如果缓存目录为空,使用项目根目录的storage/pdfg-cache目录
|
2025-12-04 18:10:14 +08:00
|
|
|
|
if cacheDir == "" {
|
2025-12-25 12:40:40 +08:00
|
|
|
|
wd, err := os.Getwd()
|
|
|
|
|
|
if err != nil {
|
2026-01-27 16:26:48 +08:00
|
|
|
|
cacheDir = filepath.Join(os.TempDir(), "tyapi_pdfg_cache")
|
2025-12-25 12:40:40 +08:00
|
|
|
|
} else {
|
2026-01-27 16:26:48 +08:00
|
|
|
|
cacheDir = filepath.Join(wd, "storage", "pdfg-cache")
|
2025-12-25 12:40:40 +08:00
|
|
|
|
}
|
2025-12-04 18:10:14 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 确保缓存目录存在
|
|
|
|
|
|
if err := os.MkdirAll(cacheDir, 0755); err != nil {
|
|
|
|
|
|
return nil, fmt.Errorf("创建缓存目录失败: %w", err)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
manager := &PDFCacheManager{
|
|
|
|
|
|
logger: logger,
|
|
|
|
|
|
cacheDir: cacheDir,
|
|
|
|
|
|
ttl: ttl,
|
|
|
|
|
|
maxSize: maxSize,
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 启动定期清理任务
|
|
|
|
|
|
manager.startCleanupTask()
|
|
|
|
|
|
|
|
|
|
|
|
logger.Info("PDF缓存管理器已初始化",
|
|
|
|
|
|
zap.String("cache_dir", cacheDir),
|
|
|
|
|
|
zap.Duration("ttl", ttl),
|
|
|
|
|
|
zap.Int64("max_size", maxSize),
|
|
|
|
|
|
)
|
2026-01-27 16:26:48 +08:00
|
|
|
|
|
2025-12-04 18:10:14 +08:00
|
|
|
|
return manager, nil
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-27 16:26:48 +08:00
|
|
|
|
// GetCacheKey 生成缓存键(基于姓名+身份证)
|
|
|
|
|
|
func (m *PDFCacheManager) GetCacheKey(name, idCard string) string {
|
|
|
|
|
|
key := fmt.Sprintf("%s:%s", name, idCard)
|
|
|
|
|
|
hash := md5.Sum([]byte(key))
|
|
|
|
|
|
return hex.EncodeToString(hash[:])
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// GetCacheKeyByProduct 生成缓存键(基于产品ID+版本)
|
|
|
|
|
|
func (m *PDFCacheManager) GetCacheKeyByProduct(productID, version string) string {
|
2025-12-04 18:10:14 +08:00
|
|
|
|
key := fmt.Sprintf("%s:%s", productID, version)
|
|
|
|
|
|
hash := md5.Sum([]byte(key))
|
|
|
|
|
|
return hex.EncodeToString(hash[:])
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// GetCachePath 获取缓存文件路径
|
|
|
|
|
|
func (m *PDFCacheManager) GetCachePath(cacheKey string) string {
|
|
|
|
|
|
return filepath.Join(m.cacheDir, fmt.Sprintf("%s.pdf", cacheKey))
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-27 16:26:48 +08:00
|
|
|
|
// Get 从缓存获取PDF文件(基于姓名+身份证)
|
|
|
|
|
|
// 返回PDF字节流、是否命中缓存、文件创建时间、错误
|
|
|
|
|
|
func (m *PDFCacheManager) Get(name, idCard string) ([]byte, bool, time.Time, error) {
|
|
|
|
|
|
cacheKey := m.GetCacheKey(name, idCard)
|
|
|
|
|
|
return m.getByKey(cacheKey, name, idCard)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// GetByProduct 从缓存获取PDF文件(基于产品ID+版本)
|
|
|
|
|
|
// 返回PDF字节流、是否命中缓存、错误
|
|
|
|
|
|
func (m *PDFCacheManager) GetByProduct(productID, version string) ([]byte, bool, error) {
|
|
|
|
|
|
cacheKey := m.GetCacheKeyByProduct(productID, version)
|
|
|
|
|
|
pdfBytes, hit, _, err := m.getByKey(cacheKey, productID, version)
|
|
|
|
|
|
return pdfBytes, hit, err
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// getByKey 内部方法:根据缓存键获取文件
|
|
|
|
|
|
func (m *PDFCacheManager) getByKey(cacheKey string, key1, key2 string) ([]byte, bool, time.Time, error) {
|
2025-12-04 18:10:14 +08:00
|
|
|
|
cachePath := m.GetCachePath(cacheKey)
|
|
|
|
|
|
|
|
|
|
|
|
m.mu.RLock()
|
|
|
|
|
|
defer m.mu.RUnlock()
|
|
|
|
|
|
|
|
|
|
|
|
// 检查文件是否存在
|
|
|
|
|
|
info, err := os.Stat(cachePath)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
if os.IsNotExist(err) {
|
2026-01-27 16:26:48 +08:00
|
|
|
|
return nil, false, time.Time{}, nil // 缓存未命中
|
2025-12-04 18:10:14 +08:00
|
|
|
|
}
|
2026-01-27 16:26:48 +08:00
|
|
|
|
return nil, false, time.Time{}, fmt.Errorf("检查缓存文件失败: %w", err)
|
2025-12-04 18:10:14 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-27 16:26:48 +08:00
|
|
|
|
// 检查文件是否过期(从文件生成时间开始算24小时)
|
2025-12-04 18:10:14 +08:00
|
|
|
|
createdAt := info.ModTime()
|
|
|
|
|
|
expiresAt := createdAt.Add(m.ttl)
|
|
|
|
|
|
if time.Now().After(expiresAt) {
|
|
|
|
|
|
// 缓存已过期,删除文件
|
|
|
|
|
|
m.logger.Debug("缓存已过期,删除文件",
|
2026-01-27 16:26:48 +08:00
|
|
|
|
zap.String("key1", key1),
|
|
|
|
|
|
zap.String("key2", key2),
|
2025-12-04 18:10:14 +08:00
|
|
|
|
zap.String("cache_key", cacheKey),
|
|
|
|
|
|
zap.Time("expires_at", expiresAt),
|
|
|
|
|
|
)
|
|
|
|
|
|
_ = os.Remove(cachePath)
|
2026-01-27 16:26:48 +08:00
|
|
|
|
return nil, false, time.Time{}, nil
|
2025-12-04 18:10:14 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 读取缓存文件
|
|
|
|
|
|
pdfBytes, err := os.ReadFile(cachePath)
|
|
|
|
|
|
if err != nil {
|
2026-01-27 16:26:48 +08:00
|
|
|
|
return nil, false, time.Time{}, fmt.Errorf("读取缓存文件失败: %w", err)
|
2025-12-04 18:10:14 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
m.logger.Debug("缓存命中",
|
2026-01-27 16:26:48 +08:00
|
|
|
|
zap.String("key1", key1),
|
|
|
|
|
|
zap.String("key2", key2),
|
2025-12-04 18:10:14 +08:00
|
|
|
|
zap.String("cache_key", cacheKey),
|
|
|
|
|
|
zap.Int64("file_size", int64(len(pdfBytes))),
|
|
|
|
|
|
zap.Time("expires_at", expiresAt),
|
|
|
|
|
|
)
|
|
|
|
|
|
|
2026-01-27 16:26:48 +08:00
|
|
|
|
return pdfBytes, true, createdAt, nil
|
2025-12-04 18:10:14 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-27 16:26:48 +08:00
|
|
|
|
// Set 将PDF文件保存到缓存(基于姓名+身份证)
|
|
|
|
|
|
func (m *PDFCacheManager) Set(name, idCard string, pdfBytes []byte) error {
|
|
|
|
|
|
cacheKey := m.GetCacheKey(name, idCard)
|
|
|
|
|
|
return m.setByKey(cacheKey, pdfBytes, name, idCard)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// SetByProduct 将PDF文件保存到缓存(基于产品ID+版本)
|
|
|
|
|
|
func (m *PDFCacheManager) SetByProduct(productID, version string, pdfBytes []byte) error {
|
|
|
|
|
|
cacheKey := m.GetCacheKeyByProduct(productID, version)
|
|
|
|
|
|
return m.setByKey(cacheKey, pdfBytes, productID, version)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// setByKey 内部方法:根据缓存键保存文件
|
|
|
|
|
|
func (m *PDFCacheManager) setByKey(cacheKey string, pdfBytes []byte, key1, key2 string) error {
|
2025-12-04 18:10:14 +08:00
|
|
|
|
cachePath := m.GetCachePath(cacheKey)
|
|
|
|
|
|
|
|
|
|
|
|
m.mu.Lock()
|
|
|
|
|
|
defer m.mu.Unlock()
|
|
|
|
|
|
|
|
|
|
|
|
// 检查缓存大小限制
|
|
|
|
|
|
if m.maxSize > 0 {
|
|
|
|
|
|
currentSize, err := m.getCacheDirSize()
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
m.logger.Warn("获取缓存目录大小失败", zap.Error(err))
|
|
|
|
|
|
} else {
|
2026-01-27 16:26:48 +08:00
|
|
|
|
// 检查是否已存在文件
|
|
|
|
|
|
var oldFileSize int64
|
|
|
|
|
|
if info, err := os.Stat(cachePath); err == nil {
|
|
|
|
|
|
oldFileSize = info.Size()
|
2025-12-04 18:10:14 +08:00
|
|
|
|
}
|
2026-01-27 16:26:48 +08:00
|
|
|
|
sizeToAdd := int64(len(pdfBytes)) - oldFileSize
|
2025-12-04 18:10:14 +08:00
|
|
|
|
|
|
|
|
|
|
if currentSize+sizeToAdd > m.maxSize {
|
|
|
|
|
|
// 缓存空间不足,清理过期文件
|
|
|
|
|
|
m.logger.Warn("缓存空间不足,开始清理过期文件",
|
|
|
|
|
|
zap.Int64("current_size", currentSize),
|
|
|
|
|
|
zap.Int64("max_size", m.maxSize),
|
|
|
|
|
|
zap.Int64("required_size", sizeToAdd),
|
|
|
|
|
|
)
|
2026-01-27 16:26:48 +08:00
|
|
|
|
if err := m.cleanExpiredFiles(); err != nil {
|
2025-12-04 18:10:14 +08:00
|
|
|
|
m.logger.Warn("清理过期文件失败", zap.Error(err))
|
|
|
|
|
|
}
|
|
|
|
|
|
// 再次检查
|
|
|
|
|
|
currentSize, _ = m.getCacheDirSize()
|
|
|
|
|
|
if currentSize+sizeToAdd > m.maxSize {
|
|
|
|
|
|
m.logger.Error("缓存空间不足,无法保存文件",
|
|
|
|
|
|
zap.Int64("current_size", currentSize),
|
|
|
|
|
|
zap.Int64("max_size", m.maxSize),
|
|
|
|
|
|
zap.Int64("required_size", sizeToAdd),
|
|
|
|
|
|
)
|
|
|
|
|
|
return fmt.Errorf("缓存空间不足,无法保存文件")
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-27 16:26:48 +08:00
|
|
|
|
// 写入文件
|
2025-12-04 18:10:14 +08:00
|
|
|
|
if err := os.WriteFile(cachePath, pdfBytes, 0644); err != nil {
|
|
|
|
|
|
return fmt.Errorf("写入缓存文件失败: %w", err)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-27 16:26:48 +08:00
|
|
|
|
m.logger.Debug("PDF已保存到缓存",
|
|
|
|
|
|
zap.String("key1", key1),
|
|
|
|
|
|
zap.String("key2", key2),
|
|
|
|
|
|
zap.String("cache_key", cacheKey),
|
|
|
|
|
|
zap.Int64("file_size", int64(len(pdfBytes))),
|
|
|
|
|
|
)
|
2025-12-04 18:10:14 +08:00
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-27 16:26:48 +08:00
|
|
|
|
// getCacheDirSize 获取缓存目录总大小
|
|
|
|
|
|
func (m *PDFCacheManager) getCacheDirSize() (int64, error) {
|
|
|
|
|
|
var totalSize int64
|
|
|
|
|
|
err := filepath.Walk(m.cacheDir, func(path string, info os.FileInfo, err error) error {
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return err
|
|
|
|
|
|
}
|
|
|
|
|
|
if !info.IsDir() {
|
|
|
|
|
|
totalSize += info.Size()
|
|
|
|
|
|
}
|
|
|
|
|
|
return nil
|
|
|
|
|
|
})
|
|
|
|
|
|
return totalSize, err
|
|
|
|
|
|
}
|
2025-12-04 18:10:14 +08:00
|
|
|
|
|
2026-01-27 16:26:48 +08:00
|
|
|
|
// startCleanupTask 启动定期清理任务
|
|
|
|
|
|
func (m *PDFCacheManager) startCleanupTask() {
|
|
|
|
|
|
m.cleanupOnce.Do(func() {
|
|
|
|
|
|
go func() {
|
|
|
|
|
|
ticker := time.NewTicker(1 * time.Hour) // 每小时清理一次
|
|
|
|
|
|
defer ticker.Stop()
|
|
|
|
|
|
|
|
|
|
|
|
for range ticker.C {
|
|
|
|
|
|
if err := m.cleanExpiredFiles(); err != nil {
|
|
|
|
|
|
m.logger.Warn("清理过期缓存文件失败", zap.Error(err))
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}()
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
2025-12-04 18:10:14 +08:00
|
|
|
|
|
2026-01-27 16:26:48 +08:00
|
|
|
|
// cleanExpiredFiles 清理过期的缓存文件
|
|
|
|
|
|
func (m *PDFCacheManager) cleanExpiredFiles() error {
|
2025-12-04 18:10:14 +08:00
|
|
|
|
m.mu.Lock()
|
|
|
|
|
|
defer m.mu.Unlock()
|
|
|
|
|
|
|
2026-01-27 16:26:48 +08:00
|
|
|
|
entries, err := os.ReadDir(m.cacheDir)
|
2025-12-04 18:10:14 +08:00
|
|
|
|
if err != nil {
|
2026-01-27 16:26:48 +08:00
|
|
|
|
return fmt.Errorf("读取缓存目录失败: %w", err)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
now := time.Now()
|
|
|
|
|
|
cleanedCount := 0
|
|
|
|
|
|
|
|
|
|
|
|
for _, entry := range entries {
|
|
|
|
|
|
if entry.IsDir() {
|
|
|
|
|
|
continue
|
2025-12-04 18:10:14 +08:00
|
|
|
|
}
|
2026-01-27 16:26:48 +08:00
|
|
|
|
|
|
|
|
|
|
// 只处理PDF文件
|
|
|
|
|
|
if filepath.Ext(entry.Name()) != ".pdf" {
|
|
|
|
|
|
continue
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
filePath := filepath.Join(m.cacheDir, entry.Name())
|
|
|
|
|
|
info, err := entry.Info()
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
continue
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 检查文件是否过期
|
|
|
|
|
|
createdAt := info.ModTime()
|
|
|
|
|
|
expiresAt := createdAt.Add(m.ttl)
|
|
|
|
|
|
if now.After(expiresAt) {
|
|
|
|
|
|
if err := os.Remove(filePath); err != nil {
|
|
|
|
|
|
m.logger.Warn("删除过期缓存文件失败",
|
|
|
|
|
|
zap.String("file_path", filePath),
|
|
|
|
|
|
zap.Error(err),
|
|
|
|
|
|
)
|
|
|
|
|
|
} else {
|
|
|
|
|
|
cleanedCount++
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if cleanedCount > 0 {
|
|
|
|
|
|
m.logger.Info("清理过期缓存文件完成",
|
|
|
|
|
|
zap.Int("cleaned_count", cleanedCount),
|
2025-12-04 18:10:14 +08:00
|
|
|
|
)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-27 16:26:48 +08:00
|
|
|
|
return nil
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Invalidate 使缓存失效(基于产品ID+版本)
|
|
|
|
|
|
func (m *PDFCacheManager) Invalidate(productID, version string) error {
|
|
|
|
|
|
cacheKey := m.GetCacheKeyByProduct(productID, version)
|
|
|
|
|
|
cachePath := m.GetCachePath(cacheKey)
|
|
|
|
|
|
|
|
|
|
|
|
m.mu.Lock()
|
|
|
|
|
|
defer m.mu.Unlock()
|
2025-12-04 18:10:14 +08:00
|
|
|
|
|
|
|
|
|
|
if err := os.Remove(cachePath); err != nil {
|
2026-01-27 16:26:48 +08:00
|
|
|
|
if os.IsNotExist(err) {
|
|
|
|
|
|
return nil // 文件不存在,视为已失效
|
|
|
|
|
|
}
|
2025-12-04 18:10:14 +08:00
|
|
|
|
return fmt.Errorf("删除缓存文件失败: %w", err)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-27 16:26:48 +08:00
|
|
|
|
// InvalidateByNameIDCard 使缓存失效(基于姓名+身份证)
|
|
|
|
|
|
func (m *PDFCacheManager) InvalidateByNameIDCard(name, idCard string) error {
|
|
|
|
|
|
cacheKey := m.GetCacheKey(name, idCard)
|
|
|
|
|
|
cachePath := m.GetCachePath(cacheKey)
|
|
|
|
|
|
|
2025-12-04 18:10:14 +08:00
|
|
|
|
m.mu.Lock()
|
|
|
|
|
|
defer m.mu.Unlock()
|
|
|
|
|
|
|
2026-01-27 16:26:48 +08:00
|
|
|
|
if err := os.Remove(cachePath); err != nil {
|
|
|
|
|
|
if os.IsNotExist(err) {
|
|
|
|
|
|
return nil // 文件不存在,视为已失效
|
2025-12-04 18:10:14 +08:00
|
|
|
|
}
|
2026-01-27 16:26:48 +08:00
|
|
|
|
return fmt.Errorf("删除缓存文件失败: %w", err)
|
2025-12-04 18:10:14 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Clear 清空所有缓存
|
|
|
|
|
|
func (m *PDFCacheManager) Clear() error {
|
|
|
|
|
|
m.mu.Lock()
|
|
|
|
|
|
defer m.mu.Unlock()
|
|
|
|
|
|
|
2026-01-27 16:26:48 +08:00
|
|
|
|
entries, err := os.ReadDir(m.cacheDir)
|
2025-12-04 18:10:14 +08:00
|
|
|
|
if err != nil {
|
|
|
|
|
|
return fmt.Errorf("读取缓存目录失败: %w", err)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
count := 0
|
2026-01-27 16:26:48 +08:00
|
|
|
|
for _, entry := range entries {
|
|
|
|
|
|
if !entry.IsDir() && filepath.Ext(entry.Name()) == ".pdf" {
|
|
|
|
|
|
filePath := filepath.Join(m.cacheDir, entry.Name())
|
2025-12-04 18:10:14 +08:00
|
|
|
|
if err := os.Remove(filePath); err == nil {
|
|
|
|
|
|
count++
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
m.logger.Info("已清空所有缓存", zap.Int("deleted_count", count))
|
|
|
|
|
|
return nil
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// GetCacheStats 获取缓存统计信息
|
|
|
|
|
|
func (m *PDFCacheManager) GetCacheStats() (map[string]interface{}, error) {
|
|
|
|
|
|
m.mu.RLock()
|
|
|
|
|
|
defer m.mu.RUnlock()
|
|
|
|
|
|
|
2026-01-27 16:26:48 +08:00
|
|
|
|
entries, err := os.ReadDir(m.cacheDir)
|
2025-12-04 18:10:14 +08:00
|
|
|
|
if err != nil {
|
|
|
|
|
|
return nil, fmt.Errorf("读取缓存目录失败: %w", err)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var totalSize int64
|
|
|
|
|
|
var fileCount int
|
|
|
|
|
|
var expiredCount int
|
|
|
|
|
|
now := time.Now()
|
|
|
|
|
|
|
2026-01-27 16:26:48 +08:00
|
|
|
|
for _, entry := range entries {
|
|
|
|
|
|
if entry.IsDir() {
|
2025-12-04 18:10:14 +08:00
|
|
|
|
continue
|
|
|
|
|
|
}
|
2026-01-27 16:26:48 +08:00
|
|
|
|
if filepath.Ext(entry.Name()) == ".pdf" {
|
|
|
|
|
|
filePath := filepath.Join(m.cacheDir, entry.Name())
|
2025-12-04 18:10:14 +08:00
|
|
|
|
info, err := os.Stat(filePath)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
continue
|
|
|
|
|
|
}
|
|
|
|
|
|
totalSize += info.Size()
|
|
|
|
|
|
fileCount++
|
|
|
|
|
|
// 检查是否过期
|
|
|
|
|
|
expiresAt := info.ModTime().Add(m.ttl)
|
|
|
|
|
|
if now.After(expiresAt) {
|
|
|
|
|
|
expiredCount++
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return map[string]interface{}{
|
|
|
|
|
|
"total_size": totalSize,
|
|
|
|
|
|
"file_count": fileCount,
|
|
|
|
|
|
"expired_count": expiredCount,
|
|
|
|
|
|
"cache_dir": m.cacheDir,
|
|
|
|
|
|
"ttl": m.ttl.String(),
|
|
|
|
|
|
"max_size": m.maxSize,
|
|
|
|
|
|
}, nil
|
|
|
|
|
|
}
|
|
|
|
|
|
|