227 lines
6.6 KiB
Go
227 lines
6.6 KiB
Go
package pdf
|
||
|
||
import (
|
||
"fmt"
|
||
"os"
|
||
"path/filepath"
|
||
"strings"
|
||
|
||
"go.uber.org/zap"
|
||
)
|
||
|
||
// GetDocumentationDir 获取接口文档文件夹路径
|
||
// 会在当前目录及其父目录中查找"接口文档"文件夹
|
||
func GetDocumentationDir() (string, error) {
|
||
// 获取当前工作目录
|
||
wd, err := os.Getwd()
|
||
if err != nil {
|
||
return "", fmt.Errorf("获取工作目录失败: %w", err)
|
||
}
|
||
|
||
// 搜索策略:从当前目录开始,向上查找"接口文档"文件夹
|
||
currentDir := wd
|
||
maxDepth := 10 // 增加搜索深度,确保能找到
|
||
|
||
var checkedDirs []string
|
||
for i := 0; i < maxDepth; i++ {
|
||
docDir := filepath.Join(currentDir, "接口文档")
|
||
checkedDirs = append(checkedDirs, docDir)
|
||
|
||
if info, err := os.Stat(docDir); err == nil && info.IsDir() {
|
||
// 直接返回相对路径,不转换为绝对路径
|
||
return docDir, nil
|
||
}
|
||
|
||
// 尝试父目录
|
||
parentDir := filepath.Dir(currentDir)
|
||
if parentDir == currentDir {
|
||
break // 已到达根目录
|
||
}
|
||
currentDir = parentDir
|
||
}
|
||
|
||
return "", fmt.Errorf("未找到接口文档文件夹。已检查的路径: %v,当前工作目录: %s", checkedDirs, wd)
|
||
}
|
||
|
||
// PDFFinder PDF文件查找服务
|
||
type PDFFinder struct {
|
||
documentationDir string
|
||
logger *zap.Logger
|
||
}
|
||
|
||
// NewPDFFinder 创建PDF查找服务
|
||
func NewPDFFinder(documentationDir string, logger *zap.Logger) *PDFFinder {
|
||
return &PDFFinder{
|
||
documentationDir: documentationDir,
|
||
logger: logger,
|
||
}
|
||
}
|
||
|
||
// FindPDFByProductCode 根据产品代码查找PDF文件
|
||
// 会在接口文档文件夹中递归搜索匹配的PDF文件
|
||
// 文件名格式应为: *_{产品代码}.pdf
|
||
func (f *PDFFinder) FindPDFByProductCode(productCode string) (string, error) {
|
||
if productCode == "" {
|
||
return "", fmt.Errorf("产品代码不能为空")
|
||
}
|
||
|
||
// 构建搜索模式:文件名以 _{产品代码}.pdf 结尾
|
||
searchPattern := fmt.Sprintf("*_%s.pdf", productCode)
|
||
|
||
f.logger.Info("开始搜索PDF文件",
|
||
zap.String("product_code", productCode),
|
||
zap.String("search_pattern", searchPattern),
|
||
zap.String("documentation_dir", f.documentationDir),
|
||
)
|
||
|
||
// 验证接口文档文件夹是否存在
|
||
if info, err := os.Stat(f.documentationDir); err != nil || !info.IsDir() {
|
||
f.logger.Error("接口文档文件夹不存在或无法访问",
|
||
zap.String("documentation_dir", f.documentationDir),
|
||
zap.Error(err),
|
||
)
|
||
return "", fmt.Errorf("接口文档文件夹不存在或无法访问: %s", f.documentationDir)
|
||
}
|
||
|
||
var foundPath string
|
||
var checkedFiles []string
|
||
err := filepath.Walk(f.documentationDir, func(path string, info os.FileInfo, err error) error {
|
||
if err != nil {
|
||
f.logger.Debug("访问文件/目录时出错,跳过",
|
||
zap.String("path", path),
|
||
zap.Error(err),
|
||
)
|
||
return nil // 忽略访问错误,继续搜索
|
||
}
|
||
|
||
// 只处理PDF文件
|
||
if info.IsDir() || !strings.HasSuffix(strings.ToLower(path), ".pdf") {
|
||
return nil
|
||
}
|
||
|
||
// 获取文件名(不包含路径)
|
||
fileName := info.Name()
|
||
checkedFiles = append(checkedFiles, fileName)
|
||
|
||
// 转换为小写进行大小写不敏感匹配
|
||
fileNameLower := strings.ToLower(fileName)
|
||
productCodeLower := strings.ToLower(productCode)
|
||
|
||
// 方式1: 检查文件名是否以 _{产品代码}.pdf 结尾(大小写不敏感)
|
||
suffixPattern := fmt.Sprintf("_%s.pdf", productCodeLower)
|
||
if strings.HasSuffix(fileNameLower, suffixPattern) {
|
||
foundPath = path
|
||
f.logger.Info("找到匹配的PDF文件(后缀匹配)",
|
||
zap.String("product_code", productCode),
|
||
zap.String("file_name", fileName),
|
||
zap.String("file_path", path),
|
||
)
|
||
return filepath.SkipAll // 找到后停止搜索
|
||
}
|
||
|
||
// 方式2: 使用filepath.Match进行模式匹配(作为备用)
|
||
matched, matchErr := filepath.Match(searchPattern, fileName)
|
||
if matchErr == nil && matched {
|
||
foundPath = path
|
||
f.logger.Info("找到匹配的PDF文件(模式匹配)",
|
||
zap.String("product_code", productCode),
|
||
zap.String("file_name", fileName),
|
||
zap.String("file_path", path),
|
||
)
|
||
return filepath.SkipAll // 找到后停止搜索
|
||
}
|
||
|
||
return nil
|
||
})
|
||
|
||
if err != nil {
|
||
f.logger.Error("搜索PDF文件时出错",
|
||
zap.String("product_code", productCode),
|
||
zap.Error(err),
|
||
)
|
||
return "", fmt.Errorf("搜索PDF文件时出错: %w", err)
|
||
}
|
||
|
||
if foundPath == "" {
|
||
// 查找包含产品编码前缀的类似文件,用于调试
|
||
var similarFiles []string
|
||
if len(productCode) >= 4 {
|
||
productCodePrefix := productCode[:4] // 取前4个字符作为前缀(如JRZQ)
|
||
for _, fileName := range checkedFiles {
|
||
fileNameLower := strings.ToLower(fileName)
|
||
if strings.Contains(fileNameLower, strings.ToLower(productCodePrefix)) {
|
||
similarFiles = append(similarFiles, fileName)
|
||
if len(similarFiles) >= 5 {
|
||
break // 只显示最多5个类似文件
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
f.logger.Warn("未找到匹配的PDF文件",
|
||
zap.String("product_code", productCode),
|
||
zap.String("search_pattern", searchPattern),
|
||
zap.String("documentation_dir", f.documentationDir),
|
||
zap.Int("checked_files_count", len(checkedFiles)),
|
||
zap.Strings("similar_files_with_same_prefix", similarFiles),
|
||
zap.Strings("sample_files", func() []string {
|
||
if len(checkedFiles) > 10 {
|
||
return checkedFiles[:10]
|
||
}
|
||
return checkedFiles
|
||
}()),
|
||
)
|
||
return "", fmt.Errorf("未找到产品代码为 %s 的PDF文档", productCode)
|
||
}
|
||
|
||
// 直接返回相对路径,不转换为绝对路径
|
||
f.logger.Info("成功找到PDF文件",
|
||
zap.String("product_code", productCode),
|
||
zap.String("file_path", foundPath),
|
||
)
|
||
|
||
return foundPath, nil
|
||
}
|
||
|
||
// FindPDFByProductCodeWithFallback 根据产品代码查找PDF文件,支持多个可能的命名格式
|
||
func (f *PDFFinder) FindPDFByProductCodeWithFallback(productCode string) (string, error) {
|
||
// 尝试多种可能的文件命名格式
|
||
patterns := []string{
|
||
fmt.Sprintf("*_%s.pdf", productCode), // 标准格式: 产品名称_{代码}.pdf
|
||
fmt.Sprintf("%s*.pdf", productCode), // 以代码开头
|
||
fmt.Sprintf("*%s*.pdf", productCode), // 包含代码
|
||
}
|
||
|
||
var foundPath string
|
||
for _, pattern := range patterns {
|
||
err := filepath.Walk(f.documentationDir, func(path string, info os.FileInfo, err error) error {
|
||
if err != nil {
|
||
return nil
|
||
}
|
||
|
||
if info.IsDir() || !strings.HasSuffix(strings.ToLower(path), ".pdf") {
|
||
return nil
|
||
}
|
||
|
||
fileName := info.Name()
|
||
if matched, _ := filepath.Match(pattern, fileName); matched {
|
||
foundPath = path
|
||
return filepath.SkipAll
|
||
}
|
||
|
||
return nil
|
||
})
|
||
|
||
if err == nil && foundPath != "" {
|
||
break
|
||
}
|
||
}
|
||
|
||
if foundPath == "" {
|
||
return "", fmt.Errorf("未找到产品代码为 %s 的PDF文档", productCode)
|
||
}
|
||
|
||
// 直接返回相对路径,不转换为绝对路径
|
||
return foundPath, nil
|
||
}
|