fix
This commit is contained in:
@@ -105,13 +105,13 @@ type QYGL6F2DReq struct {
|
||||
IDCard string `json:"id_card" validate:"required,validIDCard"`
|
||||
}
|
||||
type QYGL45BDReq struct {
|
||||
EntName string `json:"ent_name" validate:"required,min=1,validName"`
|
||||
EntName string `json:"ent_name" validate:"required,min=1,validEnterpriseName"`
|
||||
LegalPerson string `json:"legal_person" validate:"required,min=1,validName"`
|
||||
EntCode string `json:"ent_code" validate:"required,validUSCI"`
|
||||
IDCard string `json:"id_card" validate:"required,validIDCard"`
|
||||
}
|
||||
type QYGL8261Req struct {
|
||||
EntName string `json:"ent_name" validate:"required,min=1,validName"`
|
||||
EntName string `json:"ent_name" validate:"required,min=1,validEnterpriseName"`
|
||||
}
|
||||
type QYGL8271Req struct {
|
||||
EntName string `json:"ent_name" validate:"required,min=1,validName"`
|
||||
@@ -221,7 +221,7 @@ type QCXG7A2BReq struct {
|
||||
}
|
||||
|
||||
type COMENT01Req struct {
|
||||
EntName string `json:"ent_name" validate:"required,min=1,validName"`
|
||||
EntName string `json:"ent_name" validate:"required,min=1,validEnterpriseName"`
|
||||
EntCode string `json:"ent_code" validate:"required,validUSCI"`
|
||||
}
|
||||
|
||||
|
||||
213
internal/shared/crypto/west_crypto_test.go
Normal file
213
internal/shared/crypto/west_crypto_test.go
Normal file
@@ -0,0 +1,213 @@
|
||||
package crypto
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestWestDexEncryptDecrypt(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
data string
|
||||
secretKey string
|
||||
}{
|
||||
{
|
||||
name: "简单文本",
|
||||
data: "hello world",
|
||||
secretKey: "mySecretKey123",
|
||||
},
|
||||
{
|
||||
name: "中文文本",
|
||||
data: "你好世界",
|
||||
secretKey: "中文密钥",
|
||||
},
|
||||
{
|
||||
name: "JSON数据",
|
||||
data: `{"name":"张三","age":30,"city":"北京"}`,
|
||||
secretKey: "jsonSecretKey",
|
||||
},
|
||||
{
|
||||
name: "长文本",
|
||||
data: "这是一个很长的文本,用来测试加密解密功能是否正常工作。包含各种字符:123456789!@#$%^&*()_+-=[]{}|;':\",./<>?",
|
||||
secretKey: "longTextKey",
|
||||
},
|
||||
{
|
||||
name: "空字符串",
|
||||
data: "",
|
||||
secretKey: "emptyDataKey",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
// 加密
|
||||
encrypted, err := WestDexEncrypt(tc.data, tc.secretKey)
|
||||
if err != nil {
|
||||
t.Fatalf("加密失败: %v", err)
|
||||
}
|
||||
|
||||
t.Logf("原始数据: %s", tc.data)
|
||||
t.Logf("密钥: %s", tc.secretKey)
|
||||
t.Logf("加密结果: %s", encrypted)
|
||||
|
||||
// 解密
|
||||
decrypted, err := WestDexDecrypt(encrypted, tc.secretKey)
|
||||
if err != nil {
|
||||
t.Fatalf("解密失败: %v", err)
|
||||
}
|
||||
|
||||
decryptedStr := string(decrypted)
|
||||
t.Logf("解密结果: %s", decryptedStr)
|
||||
|
||||
// 验证解密结果是否与原始数据一致
|
||||
if decryptedStr != tc.data {
|
||||
t.Errorf("解密结果不匹配: 期望 %s, 实际 %s", tc.data, decryptedStr)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestWestDexDecryptOutput(t *testing.T) {
|
||||
// 专门用来查看解密结果的测试
|
||||
testData := []struct {
|
||||
name string
|
||||
data string
|
||||
secretKey string
|
||||
encryptedData string // 预设的加密数据
|
||||
}{
|
||||
{
|
||||
name: "测试数据1",
|
||||
data: "DLrbtEki5o/5yTvQWR+dWWUZYEo5s58D8LTnhhlAl99SwZbECa34KpStmR+Qr0gbbKzh3y4t5+/vbFFZgv03DtnYlLQcQt+rSgtxkCN/PCBPaFE0QZRTufd7djJfUww0Eh6DMHD7NS9pcuCa0PHGVoE+Vwo2YSwOnh2gtx3Bt0Qhs+w76tfCwIeufZ8tcpFs/nb84HIZxk+0cH1bTfNE6VsXI6vMpKvnS02O3oE2642ozeHgglCNuiOFMcCL8Erw4FKPnfRCUYdeKc2dZ7OF2IZqt0t4WiJBxjB/6k4tgAj/HepE2gaulWU8RVvAF+vPF5i3ekHHq8T7226rNlVfuagodaRXiOqO5E1h6Mx9ygcDL0HXvQKsxxJdl/bUP+t/+rOjA+k/IR/vF1UJGrGrkSJVfkcWXPP85cgws18gE9rIs2Ji1HGjvOmnez370L0+",
|
||||
secretKey: "121a1e41fc1690dd6b90afbcacd80cf4",
|
||||
},
|
||||
{
|
||||
name: "中文数据",
|
||||
data: "用户数据",
|
||||
secretKey: "密钥123",
|
||||
},
|
||||
{
|
||||
name: "API数据",
|
||||
data: "api_call_data",
|
||||
secretKey: "production_key",
|
||||
},
|
||||
{
|
||||
name: "JSON格式",
|
||||
data: `{"user_id":12345,"name":"张三","status":"active"}`,
|
||||
secretKey: "json_key",
|
||||
},
|
||||
}
|
||||
|
||||
for i, td := range testData {
|
||||
decrypted, err := WestDexDecrypt(td.data, td.secretKey)
|
||||
if err != nil {
|
||||
t.Fatalf("解密失败: %v", err)
|
||||
}
|
||||
|
||||
t.Logf("测试 %d - %s:", i+1, td.name)
|
||||
t.Logf(" 原始数据: %s", td.data)
|
||||
t.Logf(" 使用密钥: %s", td.secretKey)
|
||||
t.Logf(" 解密结果: %s", string(decrypted))
|
||||
t.Logf(" 解密正确: %v", string(decrypted) == td.data)
|
||||
t.Log("---")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSpecificDecrypt(t *testing.T) {
|
||||
// 如果你有特定的加密数据想要解密,可以在这里测试
|
||||
specificTests := []struct {
|
||||
name string
|
||||
encryptedData string
|
||||
secretKey string
|
||||
expectedData string // 如果知道预期结果的话
|
||||
}{
|
||||
// 示例:如果你有具体的加密数据想要解密,可以添加到这里
|
||||
// {
|
||||
// name: "特定数据解密",
|
||||
// encryptedData: "你的加密数据",
|
||||
// secretKey: "你的密钥",
|
||||
// expectedData: "预期的解密结果",
|
||||
// },
|
||||
}
|
||||
|
||||
t.Log("=== 特定数据解密测试 ===")
|
||||
for _, test := range specificTests {
|
||||
decrypted, err := WestDexDecrypt(test.encryptedData, test.secretKey)
|
||||
if err != nil {
|
||||
t.Logf("%s - 解密失败: %v", test.name, err)
|
||||
continue
|
||||
}
|
||||
|
||||
result := string(decrypted)
|
||||
t.Logf("%s:", test.name)
|
||||
t.Logf(" 加密数据: %s", test.encryptedData)
|
||||
t.Logf(" 使用密钥: %s", test.secretKey)
|
||||
t.Logf(" 解密结果: %s", result)
|
||||
|
||||
if test.expectedData != "" {
|
||||
t.Logf(" 预期结果: %s", test.expectedData)
|
||||
t.Logf(" 解密正确: %v", result == test.expectedData)
|
||||
}
|
||||
t.Log("---")
|
||||
}
|
||||
}
|
||||
|
||||
func TestWestDexDecryptWithWrongKey(t *testing.T) {
|
||||
// 测试用错误密钥解密
|
||||
data := "sensitive data"
|
||||
correctKey := "correct_key"
|
||||
wrongKey := "wrong_key"
|
||||
|
||||
// 用正确密钥加密
|
||||
encrypted, err := WestDexEncrypt(data, correctKey)
|
||||
if err != nil {
|
||||
t.Fatalf("加密失败: %v", err)
|
||||
}
|
||||
|
||||
// 用错误密钥解密
|
||||
decrypted, err := WestDexDecrypt(encrypted, wrongKey)
|
||||
if err != nil {
|
||||
t.Logf("用错误密钥解密失败(这是预期的): %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
decryptedStr := string(decrypted)
|
||||
t.Logf("原始数据: %s", data)
|
||||
t.Logf("用错误密钥解密结果: %s", decryptedStr)
|
||||
|
||||
// 验证解密结果应该与原始数据不同
|
||||
if decryptedStr == data {
|
||||
t.Error("用错误密钥解密不应该得到正确结果")
|
||||
}
|
||||
}
|
||||
|
||||
// 基准测试
|
||||
func BenchmarkWestDexEncrypt(b *testing.B) {
|
||||
data := "这是一个用于基准测试的数据字符串"
|
||||
secretKey := "benchmarkKey"
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, err := WestDexEncrypt(data, secretKey)
|
||||
if err != nil {
|
||||
b.Fatalf("加密失败: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkWestDexDecrypt(b *testing.B) {
|
||||
data := "这是一个用于基准测试的数据字符串"
|
||||
secretKey := "benchmarkKey"
|
||||
|
||||
// 先加密一次获得密文
|
||||
encrypted, err := WestDexEncrypt(data, secretKey)
|
||||
if err != nil {
|
||||
b.Fatalf("预加密失败: %v", err)
|
||||
}
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, err := WestDexDecrypt(encrypted, secretKey)
|
||||
if err != nil {
|
||||
b.Fatalf("解密失败: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -89,6 +89,10 @@ func RegisterCustomValidators(validate *validator.Validate) {
|
||||
|
||||
// 回调地址验证器
|
||||
validate.RegisterValidation("validReturnURL", validateReturnURL)
|
||||
|
||||
// 企业名称验证器
|
||||
validate.RegisterValidation("enterprise_name", validateEnterpriseName)
|
||||
validate.RegisterValidation("validEnterpriseName", validateEnterpriseName)
|
||||
}
|
||||
|
||||
// validatePhone 手机号验证
|
||||
@@ -490,6 +494,76 @@ func validateReturnURL(fl validator.FieldLevel) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// validateEnterpriseName 企业名称验证器
|
||||
func validateEnterpriseName(fl validator.FieldLevel) bool {
|
||||
enterpriseName := fl.Field().String()
|
||||
|
||||
// 去除首尾空格
|
||||
trimmedName := strings.TrimSpace(enterpriseName)
|
||||
if trimmedName == "" {
|
||||
return false
|
||||
}
|
||||
|
||||
// 长度验证:2-100个字符
|
||||
if len(trimmedName) < 2 || len(trimmedName) > 100 {
|
||||
return false
|
||||
}
|
||||
|
||||
// 检查是否包含非法字符(允许中英文括号)
|
||||
invalidChars := []string{
|
||||
"`", "~", "!", "@", "#", "$", "%", "^", "&", "*",
|
||||
"+", "=", "{", "}", "[", "]", "\\", "|", ";", ":", "'", "\"", "<", ">", ",", ".", "?", "/",
|
||||
}
|
||||
for _, char := range invalidChars {
|
||||
if strings.Contains(trimmedName, char) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// 必须包含至少一个中文字符或英文字母
|
||||
hasValidChar := regexp.MustCompile(`[\p{Han}a-zA-Z]`).MatchString(trimmedName)
|
||||
if !hasValidChar {
|
||||
return false
|
||||
}
|
||||
|
||||
// 验证企业名称的基本格式(支持各种类型的企业)
|
||||
// 支持:有限公司、股份有限公司、工作室、个体工商户、合伙企业等
|
||||
validSuffixes := []string{
|
||||
"有限公司", "有限责任公司", "股份有限公司", "股份公司",
|
||||
"工作室", "个体工商户", "个人独资企业", "合伙企业",
|
||||
"集团有限公司", "集团股份有限公司",
|
||||
"Co.,Ltd", "Co., Ltd", "Ltd", "LLC", "Inc", "Corp",
|
||||
"Company", "Studio", "Workshop", "Enterprise",
|
||||
}
|
||||
|
||||
// 检查是否以合法的企业类型结尾(不强制要求,因为有些企业名称可能没有标准后缀)
|
||||
// 但如果有后缀,必须是合法的
|
||||
hasValidSuffix := false
|
||||
for _, suffix := range validSuffixes {
|
||||
if strings.HasSuffix(trimmedName, suffix) {
|
||||
hasValidSuffix = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// 如果名称中包含常见的企业类型关键词,则必须是合法的后缀
|
||||
enterpriseKeywords := []string{"公司", "工作室", "企业", "集团", "Co", "Ltd", "LLC", "Inc", "Corp", "Company", "Studio", "Workshop", "Enterprise"}
|
||||
containsKeyword := false
|
||||
for _, keyword := range enterpriseKeywords {
|
||||
if strings.Contains(trimmedName, keyword) {
|
||||
containsKeyword = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// 如果包含企业关键词但没有合法后缀,则验证失败
|
||||
if containsKeyword && !hasValidSuffix {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// ================ 统一的业务校验方法 ================
|
||||
|
||||
// ValidatePhone 验证手机号
|
||||
@@ -694,6 +768,80 @@ func ValidateSliceNotEmpty(slice interface{}, fieldName string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// ValidateEnterpriseName 验证企业名称
|
||||
func ValidateEnterpriseName(enterpriseName string) error {
|
||||
if enterpriseName == "" {
|
||||
return fmt.Errorf("企业名称不能为空")
|
||||
}
|
||||
|
||||
// 去除首尾空格
|
||||
trimmedName := strings.TrimSpace(enterpriseName)
|
||||
if trimmedName == "" {
|
||||
return fmt.Errorf("企业名称不能为空")
|
||||
}
|
||||
|
||||
// 长度验证:2-100个字符
|
||||
if len(trimmedName) < 2 {
|
||||
return fmt.Errorf("企业名称长度不能少于2个字符")
|
||||
}
|
||||
if len(trimmedName) > 100 {
|
||||
return fmt.Errorf("企业名称长度不能超过100个字符")
|
||||
}
|
||||
|
||||
// 检查是否包含非法字符(允许中英文括号)
|
||||
invalidChars := []string{
|
||||
"`", "~", "!", "@", "#", "$", "%", "^", "&", "*",
|
||||
"+", "=", "{", "}", "[", "]", "\\", "|", ";", ":", "'", "\"", "<", ">", ",", ".", "?", "/",
|
||||
}
|
||||
for _, char := range invalidChars {
|
||||
if strings.Contains(trimmedName, char) {
|
||||
return fmt.Errorf("企业名称不能包含特殊字符: %s", char)
|
||||
}
|
||||
}
|
||||
|
||||
// 必须包含至少一个中文字符或英文字母
|
||||
hasValidChar := regexp.MustCompile(`[\p{Han}a-zA-Z]`).MatchString(trimmedName)
|
||||
if !hasValidChar {
|
||||
return fmt.Errorf("企业名称必须包含中文字符或英文字母")
|
||||
}
|
||||
|
||||
// 验证企业名称的基本格式(支持各种类型的企业)
|
||||
// 支持:有限公司、股份有限公司、工作室、个体工商户、合伙企业等
|
||||
validSuffixes := []string{
|
||||
"有限公司", "有限责任公司", "股份有限公司", "股份公司",
|
||||
"工作室", "个体工商户", "个人独资企业", "合伙企业",
|
||||
"集团有限公司", "集团股份有限公司",
|
||||
"Co.,Ltd", "Co., Ltd", "Ltd", "LLC", "Inc", "Corp",
|
||||
"Company", "Studio", "Workshop", "Enterprise",
|
||||
}
|
||||
|
||||
// 检查是否以合法的企业类型结尾
|
||||
hasValidSuffix := false
|
||||
for _, suffix := range validSuffixes {
|
||||
if strings.HasSuffix(trimmedName, suffix) {
|
||||
hasValidSuffix = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// 如果名称中包含常见的企业类型关键词,则必须是合法的后缀
|
||||
enterpriseKeywords := []string{"公司", "工作室", "企业", "集团", "Co", "Ltd", "LLC", "Inc", "Corp", "Company", "Studio", "Workshop", "Enterprise"}
|
||||
containsKeyword := false
|
||||
for _, keyword := range enterpriseKeywords {
|
||||
if strings.Contains(trimmedName, keyword) {
|
||||
containsKeyword = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// 如果包含企业关键词但没有合法后缀,则验证失败
|
||||
if containsKeyword && !hasValidSuffix {
|
||||
return fmt.Errorf("企业名称格式不正确,请使用标准的企业类型后缀(如:有限公司、工作室等)")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ================ 便捷的校验器创建函数 ================
|
||||
|
||||
// NewBusinessValidator 创建业务验证器(保持向后兼容)
|
||||
|
||||
135
internal/shared/validator/enterprise_name_test.go
Normal file
135
internal/shared/validator/enterprise_name_test.go
Normal file
@@ -0,0 +1,135 @@
|
||||
package validator
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestValidateEnterpriseName(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
input string
|
||||
expectError bool
|
||||
errorMsg string
|
||||
}{
|
||||
{
|
||||
name: "有效的有限公司名称",
|
||||
input: "北京天远数据科技有限公司",
|
||||
expectError: false,
|
||||
},
|
||||
{
|
||||
name: "有效的工作室名称",
|
||||
input: "张三设计工作室",
|
||||
expectError: false,
|
||||
},
|
||||
{
|
||||
name: "有效的英文公司名称",
|
||||
input: "Apple Inc",
|
||||
expectError: false,
|
||||
},
|
||||
{
|
||||
name: "有效的英文有限公司",
|
||||
input: "Google LLC",
|
||||
expectError: false,
|
||||
},
|
||||
{
|
||||
name: "有效的股份有限公司",
|
||||
input: "中国移动股份有限公司",
|
||||
expectError: false,
|
||||
},
|
||||
{
|
||||
name: "有效的个体工商户",
|
||||
input: "李四个体工商户",
|
||||
expectError: false,
|
||||
},
|
||||
{
|
||||
name: "空字符串",
|
||||
input: "",
|
||||
expectError: true,
|
||||
errorMsg: "企业名称不能为空",
|
||||
},
|
||||
{
|
||||
name: "只有空格",
|
||||
input: " ",
|
||||
expectError: true,
|
||||
errorMsg: "企业名称不能为空",
|
||||
},
|
||||
{
|
||||
name: "长度过短",
|
||||
input: "A",
|
||||
expectError: true,
|
||||
errorMsg: "企业名称长度不能少于2个字符",
|
||||
},
|
||||
{
|
||||
name: "包含非法字符",
|
||||
input: "测试公司@#$",
|
||||
expectError: true,
|
||||
errorMsg: "企业名称不能包含特殊字符",
|
||||
},
|
||||
{
|
||||
name: "包含公司关键词但后缀不合法",
|
||||
input: "测试公司123",
|
||||
expectError: true,
|
||||
errorMsg: "企业名称格式不正确,请使用标准的企业类型后缀",
|
||||
},
|
||||
{
|
||||
name: "包含工作室关键词但后缀不合法",
|
||||
input: "设计工作室ABC",
|
||||
expectError: true,
|
||||
errorMsg: "企业名称格式不正确,请使用标准的企业类型后缀",
|
||||
},
|
||||
{
|
||||
name: "只包含数字和特殊字符",
|
||||
input: "12345-67890",
|
||||
expectError: true,
|
||||
errorMsg: "企业名称必须包含中文字符或英文字母",
|
||||
},
|
||||
{
|
||||
name: "没有企业类型关键词的个人名称",
|
||||
input: "张三理发店",
|
||||
expectError: false, // 没有企业关键词,所以不强制要求后缀
|
||||
},
|
||||
{
|
||||
name: "包含括号的企业名称",
|
||||
input: "北京天远数据科技有限公司(分公司)",
|
||||
expectError: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
err := ValidateEnterpriseName(tt.input)
|
||||
|
||||
if tt.expectError {
|
||||
if err == nil {
|
||||
t.Errorf("期望有错误,但没有返回错误")
|
||||
} else if tt.errorMsg != "" && !containsString(err.Error(), tt.errorMsg) {
|
||||
t.Errorf("期望错误消息包含 '%s',但得到 '%s'", tt.errorMsg, err.Error())
|
||||
}
|
||||
} else {
|
||||
if err != nil {
|
||||
t.Errorf("期望没有错误,但得到错误: %v", err)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// containsString 检查字符串是否包含子字符串
|
||||
func containsString(s, substr string) bool {
|
||||
return len(s) >= len(substr) &&
|
||||
(s == substr ||
|
||||
len(s) > len(substr) &&
|
||||
(s[:len(substr)] == substr ||
|
||||
s[len(s)-len(substr):] == substr ||
|
||||
containsSubstring(s, substr)))
|
||||
}
|
||||
|
||||
// containsSubstring 辅助函数检查子字符串
|
||||
func containsSubstring(s, substr string) bool {
|
||||
for i := 0; i <= len(s)-len(substr); i++ {
|
||||
if s[i:i+len(substr)] == substr {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
Reference in New Issue
Block a user