first commit

This commit is contained in:
2025-09-21 18:27:25 +08:00
commit 02e1c4bd63
528 changed files with 55690 additions and 0 deletions

View File

@@ -0,0 +1,670 @@
package service
import (
"context"
"database/sql"
"errors"
"fmt"
"hm-server/app/main/api/internal/config"
"hm-server/app/main/model"
"os"
"path/filepath"
"strings"
"testing"
"time"
"github.com/Masterminds/squirrel"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
"github.com/zeromicro/go-zero/core/stores/sqlx"
)
// mockResult 模拟sql.Result
type mockResult struct {
lastInsertId int64
rowsAffected int64
}
func (m *mockResult) LastInsertId() (int64, error) {
return m.lastInsertId, nil
}
func (m *mockResult) RowsAffected() (int64, error) {
return m.rowsAffected, nil
}
// MockAuthorizationDocumentModel 模拟授权书模型
type MockAuthorizationDocumentModel struct {
mock.Mock
}
func (m *MockAuthorizationDocumentModel) Insert(ctx context.Context, session sqlx.Session, data *model.AuthorizationDocument) (sql.Result, error) {
args := m.Called(ctx, session, data)
return args.Get(0).(sql.Result), args.Error(1)
}
func (m *MockAuthorizationDocumentModel) FindOne(ctx context.Context, id int64) (*model.AuthorizationDocument, error) {
args := m.Called(ctx, id)
if args.Get(0) == nil {
return nil, args.Error(1)
}
return args.Get(0).(*model.AuthorizationDocument), args.Error(1)
}
func (m *MockAuthorizationDocumentModel) Update(ctx context.Context, session sqlx.Session, data *model.AuthorizationDocument) (sql.Result, error) {
args := m.Called(ctx, session, data)
return args.Get(0).(sql.Result), args.Error(1)
}
func (m *MockAuthorizationDocumentModel) UpdateWithVersion(ctx context.Context, session sqlx.Session, data *model.AuthorizationDocument) error {
args := m.Called(ctx, session, data)
return args.Error(0)
}
func (m *MockAuthorizationDocumentModel) Trans(ctx context.Context, fn func(context.Context, sqlx.Session) error) error {
args := m.Called(ctx, fn)
return args.Error(0)
}
func (m *MockAuthorizationDocumentModel) SelectBuilder() squirrel.SelectBuilder {
args := m.Called()
return args.Get(0).(squirrel.SelectBuilder)
}
func (m *MockAuthorizationDocumentModel) DeleteSoft(ctx context.Context, session sqlx.Session, data *model.AuthorizationDocument) error {
args := m.Called(ctx, session, data)
return args.Error(0)
}
func (m *MockAuthorizationDocumentModel) FindSum(ctx context.Context, sumBuilder squirrel.SelectBuilder, field string) (float64, error) {
args := m.Called(ctx, sumBuilder, field)
return args.Get(0).(float64), args.Error(1)
}
func (m *MockAuthorizationDocumentModel) FindCount(ctx context.Context, countBuilder squirrel.SelectBuilder, field string) (int64, error) {
args := m.Called(ctx, countBuilder, field)
return args.Get(0).(int64), args.Error(1)
}
func (m *MockAuthorizationDocumentModel) FindAll(ctx context.Context, rowBuilder squirrel.SelectBuilder, orderBy string) ([]*model.AuthorizationDocument, error) {
args := m.Called(ctx, rowBuilder, orderBy)
if args.Get(0) == nil {
return nil, args.Error(1)
}
return args.Get(0).([]*model.AuthorizationDocument), args.Error(1)
}
func (m *MockAuthorizationDocumentModel) FindPageListByPage(ctx context.Context, rowBuilder squirrel.SelectBuilder, page, pageSize int64, orderBy string) ([]*model.AuthorizationDocument, error) {
args := m.Called(ctx, rowBuilder, page, pageSize, orderBy)
if args.Get(0) == nil {
return nil, args.Error(1)
}
return args.Get(0).([]*model.AuthorizationDocument), args.Error(1)
}
func (m *MockAuthorizationDocumentModel) FindPageListByPageWithTotal(ctx context.Context, rowBuilder squirrel.SelectBuilder, page, pageSize int64, orderBy string) ([]*model.AuthorizationDocument, int64, error) {
args := m.Called(ctx, rowBuilder, page, pageSize, orderBy)
if args.Get(0) == nil {
return nil, args.Get(1).(int64), args.Error(2)
}
return args.Get(0).([]*model.AuthorizationDocument), args.Get(1).(int64), args.Error(2)
}
func (m *MockAuthorizationDocumentModel) FindPageListByIdDESC(ctx context.Context, rowBuilder squirrel.SelectBuilder, preMinId, pageSize int64) ([]*model.AuthorizationDocument, error) {
args := m.Called(ctx, rowBuilder, preMinId, pageSize)
if args.Get(0) == nil {
return nil, args.Error(1)
}
return args.Get(0).([]*model.AuthorizationDocument), args.Error(1)
}
func (m *MockAuthorizationDocumentModel) FindPageListByIdASC(ctx context.Context, rowBuilder squirrel.SelectBuilder, preMaxId, pageSize int64) ([]*model.AuthorizationDocument, error) {
args := m.Called(ctx, rowBuilder, preMaxId, pageSize)
if args.Get(0) == nil {
return nil, args.Error(1)
}
return args.Get(0).([]*model.AuthorizationDocument), args.Error(1)
}
func (m *MockAuthorizationDocumentModel) Delete(ctx context.Context, session sqlx.Session, id int64) error {
args := m.Called(ctx, session, id)
return args.Error(0)
}
func (m *MockAuthorizationDocumentModel) FindByOrderId(ctx context.Context, orderId int64) ([]*model.AuthorizationDocument, error) {
args := m.Called(ctx, orderId)
if args.Get(0) == nil {
return nil, args.Error(1)
}
return args.Get(0).([]*model.AuthorizationDocument), args.Error(1)
}
// MockResult 模拟数据库结果
type MockResult struct {
lastInsertId int64
}
func (m *MockResult) LastInsertId() (int64, error) {
return m.lastInsertId, nil
}
func (m *MockResult) RowsAffected() (int64, error) {
return 1, nil
}
// TestNewAuthorizationService 测试创建授权书服务
func TestNewAuthorizationService(t *testing.T) {
config := config.Config{
Authorization: config.AuthorizationConfig{
FileBaseURL: "https://test.com/api/v1/auth-docs",
},
}
mockModel := &MockAuthorizationDocumentModel{}
service := NewAuthorizationService(config, mockModel)
assert.NotNil(t, service)
assert.Equal(t, "data/authorization_docs", service.fileStoragePath)
assert.Equal(t, "https://test.com/api/v1/auth-docs", service.fileBaseURL)
}
// TestGetFullFileURL 测试获取完整文件URL
func TestGetFullFileURL(t *testing.T) {
config := config.Config{
Authorization: config.AuthorizationConfig{
FileBaseURL: "https://test.com/api/v1/auth-docs",
},
}
mockModel := &MockAuthorizationDocumentModel{}
service := NewAuthorizationService(config, mockModel)
tests := []struct {
name string
relativePath string
expected string
}{
{
name: "正常相对路径",
relativePath: "2025/09/auth_123_456_20250913_160800.pdf",
expected: "https://test.com/api/v1/auth-docs/2025/09/auth_123_456_20250913_160800.pdf",
},
{
name: "空路径",
relativePath: "",
expected: "",
},
{
name: "只有文件名",
relativePath: "test.pdf",
expected: "https://test.com/api/v1/auth-docs/test.pdf",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := service.GetFullFileURL(tt.relativePath)
assert.Equal(t, tt.expected, result)
})
}
}
// TestGenerateAuthorizationDocument 测试生成授权书
func TestGenerateAuthorizationDocument(t *testing.T) {
// 创建测试配置
config := config.Config{
Authorization: config.AuthorizationConfig{
FileBaseURL: "https://test.example.com/api/v1/auth-docs",
},
}
// 创建模拟的数据库模型
mockModel := &MockAuthorizationDocumentModel{}
// 创建授权书服务
service := NewAuthorizationService(config, mockModel)
// 准备测试数据
userInfo := map[string]interface{}{
"name": "张三",
"id_card": "110101199001011234",
"mobile": "13800138000",
}
// 模拟数据库插入成功
mockModel.On("Insert", mock.Anything, mock.Anything, mock.Anything).Return(
&mockResult{lastInsertId: 1, rowsAffected: 1}, nil)
// 执行测试
authDoc, err := service.GenerateAuthorizationDocument(
context.Background(),
1, // userID
2, // orderID
3, // queryID
userInfo,
)
// 验证结果
assert.NoError(t, err)
assert.NotNil(t, authDoc)
assert.Equal(t, int64(1), authDoc.UserId)
assert.Equal(t, int64(2), authDoc.OrderId)
assert.Equal(t, int64(3), authDoc.QueryId)
assert.Equal(t, "pdf", authDoc.FileType)
assert.Equal(t, "active", authDoc.Status)
assert.False(t, authDoc.ExpireTime.Valid) // 永久保留,不设置过期时间
// 验证文件路径格式兼容Windows和Unix路径分隔符
assert.True(t, strings.Contains(authDoc.FilePath, "data/authorization_docs") ||
strings.Contains(authDoc.FilePath, "data\\authorization_docs"))
assert.Contains(t, authDoc.FileName, "auth_")
assert.Contains(t, authDoc.FileName, ".pdf")
// 验证相对路径格式
assert.Regexp(t, `^\d{4}/\d{2}/auth_\d+_\d+_\d{8}_\d{6}\.pdf$`, authDoc.FileUrl)
// 验证文件大小
assert.Greater(t, authDoc.FileSize, int64(0))
// 验证数据库调用
mockModel.AssertExpectations(t)
// 验证文件是否真的被创建
if _, err := os.Stat(authDoc.FilePath); err == nil {
t.Logf("✅ 授权书文件已创建: %s", authDoc.FilePath)
t.Logf("📊 文件大小: %d 字节", authDoc.FileSize)
} else {
t.Logf("⚠️ 文件未找到: %s", authDoc.FilePath)
}
}
// TestGenerateAuthorizationDocument_DatabaseError 测试数据库错误
func TestGenerateAuthorizationDocument_DatabaseError(t *testing.T) {
// 创建测试配置
config := config.Config{
Authorization: config.AuthorizationConfig{
FileBaseURL: "https://test.example.com/api/v1/auth-docs",
},
}
// 创建模拟的数据库模型
mockModel := &MockAuthorizationDocumentModel{}
// 创建授权书服务
service := NewAuthorizationService(config, mockModel)
// 准备测试数据
userInfo := map[string]interface{}{
"name": "李四",
"id_card": "110101199001011235",
"mobile": "13800138001",
}
// 模拟数据库插入失败
mockModel.On("Insert", mock.Anything, mock.Anything, mock.Anything).Return(
(*mockResult)(nil), errors.New("数据库连接失败"))
// 执行测试
authDoc, err := service.GenerateAuthorizationDocument(
context.Background(),
1, // userID
2, // orderID
3, // queryID
userInfo,
)
// 验证结果
assert.Error(t, err)
assert.Nil(t, authDoc)
assert.Contains(t, err.Error(), "数据库连接失败")
// 验证数据库调用
mockModel.AssertExpectations(t)
}
// TestGeneratePDFContent 测试生成PDF内容
func TestGeneratePDFContent(t *testing.T) {
config := config.Config{
Authorization: config.AuthorizationConfig{
FileBaseURL: "https://test.com/api/v1/auth-docs",
},
}
mockModel := &MockAuthorizationDocumentModel{}
service := NewAuthorizationService(config, mockModel)
userInfo := map[string]interface{}{
"name": "张三",
"id_card": "110101199001011234",
}
pdfBytes, err := service.generatePDFContent(userInfo)
// 验证结果
assert.NoError(t, err)
assert.NotNil(t, pdfBytes)
assert.Greater(t, len(pdfBytes), 0)
// 验证PDF内容PDF是二进制格式只验证基本结构
assert.Contains(t, string(pdfBytes), "%PDF") // PDF文件头
// 按照 GenerateAuthorizationDocument 的方式保存文件到本地
// 1. 创建文件存储目录
year := time.Now().Format("2006")
month := time.Now().Format("01")
dirPath := filepath.Join("data", "authorization_docs", year, month)
if err := os.MkdirAll(dirPath, 0755); err != nil {
t.Fatalf("创建存储目录失败: %v", err)
}
// 2. 生成文件名和路径
fileName := fmt.Sprintf("test_auth_%s.pdf", time.Now().Format("20060102_150405"))
filePath := filepath.Join(dirPath, fileName)
relativePath := fmt.Sprintf("%s/%s/%s", year, month, fileName)
// 3. 保存PDF文件
if err := os.WriteFile(filePath, pdfBytes, 0644); err != nil {
t.Fatalf("保存PDF文件失败: %v", err)
}
// 4. 验证文件是否保存成功
if _, err := os.Stat(filePath); err == nil {
t.Logf("✅ PDF文件已保存到本地")
t.Logf("📄 文件名: %s", fileName)
t.Logf("📁 文件路径: %s", filePath)
t.Logf("🔗 相对路径: %s", relativePath)
t.Logf("📊 文件大小: %d 字节", len(pdfBytes))
// 获取绝对路径
absPath, _ := filepath.Abs(filePath)
t.Logf("📍 绝对路径: %s", absPath)
} else {
t.Errorf("❌ PDF文件保存失败: %v", err)
}
}
// TestSavePDFToLocal 专门测试保存PDF文件到本地
func TestSavePDFToLocal(t *testing.T) {
config := config.Config{
Authorization: config.AuthorizationConfig{
FileBaseURL: "https://test.com/api/v1/auth-docs",
},
}
mockModel := &MockAuthorizationDocumentModel{}
service := NewAuthorizationService(config, mockModel)
// 准备测试数据
userInfo := map[string]interface{}{
"name": "何志勇",
"id_card": "452528197907133014",
"mobile": "18276151590",
}
// 生成PDF内容
pdfBytes, err := service.generatePDFContent(userInfo)
assert.NoError(t, err)
assert.NotNil(t, pdfBytes)
assert.Greater(t, len(pdfBytes), 0)
// 按照 GenerateAuthorizationDocument 的方式保存文件到本地
// 1. 创建文件存储目录
year := time.Now().Format("2006")
month := time.Now().Format("01")
dirPath := filepath.Join("data", "authorization_docs", year, month)
if err := os.MkdirAll(dirPath, 0755); err != nil {
t.Fatalf("创建存储目录失败: %v", err)
}
// 2. 生成文件名和路径
fileName := fmt.Sprintf("local_test_auth_%s.pdf", time.Now().Format("20060102_150405"))
filePath := filepath.Join(dirPath, fileName)
relativePath := fmt.Sprintf("%s/%s/%s", year, month, fileName)
// 3. 保存PDF文件
if err := os.WriteFile(filePath, pdfBytes, 0644); err != nil {
t.Fatalf("保存PDF文件失败: %v", err)
}
// 4. 验证文件是否保存成功
if _, err := os.Stat(filePath); err == nil {
t.Logf("✅ PDF文件已保存到本地")
t.Logf("📄 文件名: %s", fileName)
t.Logf("📁 文件路径: %s", filePath)
t.Logf("🔗 相对路径: %s", relativePath)
t.Logf("📊 文件大小: %d 字节", len(pdfBytes))
// 获取绝对路径
absPath, _ := filepath.Abs(filePath)
t.Logf("📍 绝对路径: %s", absPath)
// 验证文件内容
fileInfo, err := os.Stat(filePath)
assert.NoError(t, err)
assert.Greater(t, fileInfo.Size(), int64(1000)) // 文件应该大于1KB
t.Logf("🎉 文件保存验证通过!")
} else {
t.Errorf("❌ PDF文件保存失败: %v", err)
}
}
// TestGeneratePDFContent_EmptyUserInfo 测试空用户信息
func TestGeneratePDFContent_EmptyUserInfo(t *testing.T) {
config := config.Config{
Authorization: config.AuthorizationConfig{
FileBaseURL: "https://test.com/api/v1/auth-docs",
},
}
mockModel := &MockAuthorizationDocumentModel{}
service := NewAuthorizationService(config, mockModel)
userInfo := map[string]interface{}{}
pdfBytes, err := service.generatePDFContent(userInfo)
// 验证结果
assert.NoError(t, err)
assert.NotNil(t, pdfBytes)
assert.Greater(t, len(pdfBytes), 0)
// 验证PDF内容PDF是二进制格式只验证基本结构
assert.Contains(t, string(pdfBytes), "%PDF") // PDF文件头
}
// TestGetUserInfoString 测试获取用户信息字符串
func TestGetUserInfoString(t *testing.T) {
userInfo := map[string]interface{}{
"name": "张三",
"id_card": "110101199001011234",
"age": 30,
"empty": "",
}
tests := []struct {
name string
key string
expected string
}{
{
name: "正常字符串",
key: "name",
expected: "张三",
},
{
name: "身份证号",
key: "id_card",
expected: "110101199001011234",
},
{
name: "空字符串",
key: "empty",
expected: "",
},
{
name: "不存在的键",
key: "not_exist",
expected: "",
},
{
name: "非字符串类型",
key: "age",
expected: "",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := getUserInfoString(userInfo, tt.key)
assert.Equal(t, tt.expected, result)
})
}
}
// BenchmarkGeneratePDFContent 性能测试
func BenchmarkGeneratePDFContent(b *testing.B) {
config := config.Config{
Authorization: config.AuthorizationConfig{
FileBaseURL: "https://test.com/api/v1/auth-docs",
},
}
mockModel := &MockAuthorizationDocumentModel{}
service := NewAuthorizationService(config, mockModel)
userInfo := map[string]interface{}{
"name": "张三",
"id_card": "110101199001011234",
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, err := service.generatePDFContent(userInfo)
if err != nil {
b.Fatal(err)
}
}
}
// TestAuthorizationService_Integration 集成测试(需要真实文件系统)
func TestAuthorizationService_Integration(t *testing.T) {
// 创建测试配置
config := config.Config{
Authorization: config.AuthorizationConfig{
FileBaseURL: "https://test.example.com/api/v1/auth-docs",
},
}
// 创建模拟的数据库模型
mockModel := &MockAuthorizationDocumentModel{}
// 创建授权书服务
service := NewAuthorizationService(config, mockModel)
// 准备测试数据
userInfo := map[string]interface{}{
"name": "王五",
"id_card": "110101199001011236",
"mobile": "13800138002",
}
// 模拟数据库插入成功
mockModel.On("Insert", mock.Anything, mock.Anything, mock.Anything).Return(
&mockResult{lastInsertId: 1, rowsAffected: 1}, nil)
// 执行测试
authDoc, err := service.GenerateAuthorizationDocument(
context.Background(),
1, // userID
2, // orderID
3, // queryID
userInfo,
)
// 验证结果
assert.NoError(t, err)
assert.NotNil(t, authDoc)
// 测试GetFullFileURL方法
fullURL := service.GetFullFileURL(authDoc.FileUrl)
expectedURL := fmt.Sprintf("%s/%s", config.Authorization.FileBaseURL, authDoc.FileUrl)
assert.Equal(t, expectedURL, fullURL)
// 验证文件是否真的被创建
if _, err := os.Stat(authDoc.FilePath); err == nil {
t.Logf("✅ 集成测试成功 - 授权书文件已创建")
t.Logf("📄 文件名: %s", authDoc.FileName)
t.Logf("📁 文件路径: %s", authDoc.FilePath)
t.Logf("🔗 相对路径: %s", authDoc.FileUrl)
t.Logf("🌐 完整URL: %s", fullURL)
t.Logf("📊 文件大小: %d 字节", authDoc.FileSize)
} else {
t.Logf("⚠️ 集成测试 - 文件未找到: %s", authDoc.FilePath)
}
// 验证数据库调用
mockModel.AssertExpectations(t)
}
// TestGeneratePDFFile 专门测试PDF文件生成
func TestGeneratePDFFile(t *testing.T) {
// 创建测试配置
config := config.Config{
Authorization: config.AuthorizationConfig{
FileBaseURL: "https://test.example.com/api/v1/auth-docs",
},
}
// 创建模拟的数据库模型
mockModel := &MockAuthorizationDocumentModel{}
// 创建授权书服务
service := NewAuthorizationService(config, mockModel)
// 准备测试数据
userInfo := map[string]interface{}{
"name": "测试用户",
"id_card": "110101199001011237",
"mobile": "13800138003",
}
// 模拟数据库插入成功
mockModel.On("Insert", mock.Anything, mock.Anything, mock.Anything).Return(
&mockResult{lastInsertId: 1, rowsAffected: 1}, nil)
// 执行测试
authDoc, err := service.GenerateAuthorizationDocument(
context.Background(),
999, // userID
888, // orderID
777, // queryID
userInfo,
)
// 验证结果
assert.NoError(t, err)
assert.NotNil(t, authDoc)
// 验证文件是否真的被创建
if _, err := os.Stat(authDoc.FilePath); err == nil {
t.Logf("✅ PDF文件生成成功")
t.Logf("📄 文件名: %s", authDoc.FileName)
t.Logf("📁 文件路径: %s", authDoc.FilePath)
t.Logf("🔗 相对路径: %s", authDoc.FileUrl)
t.Logf("📊 文件大小: %d 字节", authDoc.FileSize)
// 验证文件内容
fileInfo, err := os.Stat(authDoc.FilePath)
assert.NoError(t, err)
assert.Greater(t, fileInfo.Size(), int64(1000)) // 文件应该大于1KB
// 验证文件名格式
assert.Regexp(t, `^auth_999_888_\d{8}_\d{6}\.pdf$`, authDoc.FileName)
// 验证路径格式
assert.Regexp(t, `^\d{4}/\d{2}/auth_999_888_\d{8}_\d{6}\.pdf$`, authDoc.FileUrl)
t.Logf("🎉 所有验证通过!")
} else {
t.Errorf("❌ PDF文件未创建: %s", authDoc.FilePath)
}
// 验证数据库调用
mockModel.AssertExpectations(t)
}