package main import ( "bytes" "encoding/json" "fmt" "io" "net/http" "os" "strconv" "strings" "time" ) const ( // 服务器地址 - 根据实际情况修改 baseURL = "http://localhost:8080" // 管理员认证信息 - 需要根据实际情况修改 adminEmail = "admin@example.com" adminPassword = "admin123" // 测试用户邮箱前缀,用于识别测试数据 testEmailPrefix = "test_" ) // 管理员登录响应 type AdminLoginResponse struct { Code int `json:"code"` Data struct { Token string `json:"token"` } `json:"data"` Message string `json:"message"` } // 用户列表响应 type UserListResponse struct { Code int `json:"code"` Data struct { List []struct { ID int64 `json:"id"` Email string `json:"email"` } `json:"list"` Total int64 `json:"total"` } `json:"data"` Message string `json:"message"` } // 通用响应结构 type CommonResponse struct { Code int `json:"code"` Message string `json:"message"` } // HTTP客户端 var client = &http.Client{ Timeout: 30 * time.Second, } // 管理员token var adminToken string func main() { // 检查是否显示帮助信息 if len(os.Args) > 1 && os.Args[1] == "--help" { showHelp() return } fmt.Println("=== 测试数据清理脚本 ===") fmt.Printf("服务器地址: %s\n", baseURL) fmt.Printf("管理员邮箱: %s\n", adminEmail) fmt.Println() // 1. 管理员登录获取token fmt.Println("步骤 1: 管理员登录...") if err := adminLogin(); err != nil { fmt.Printf("❌ 管理员登录失败: %v\n", err) fmt.Println("\n请检查:") fmt.Println("- 服务器是否正在运行") fmt.Println("- 管理员邮箱和密码是否正确") fmt.Println("- 服务器地址是否正确") return } fmt.Println("✅ 管理员登录成功") // 2. 清理测试用户数据 fmt.Println("\n步骤 2: 清理测试用户...") if err := cleanupTestUsers(); err != nil { fmt.Printf("❌ 清理测试用户失败: %v\n", err) } // 3. 清理孤立的设备数据(如果有相应的API) fmt.Println("\n步骤 3: 检查数据一致性...") checkDataConsistency() fmt.Println("\n=== 清理完成 ===") } // 显示帮助信息 func showHelp() { fmt.Println(`测试数据清理脚本使用说明: 📋 功能说明: 本脚本用于清理测试过程中产生的用户数据,解决数据不一致问题 🔧 配置修改: 请在脚本中修改以下配置: - baseURL: 服务器地址 (当前: http://localhost:8080) - adminEmail: 管理员邮箱 - adminPassword: 管理员密码 - testEmailPrefix: 测试用户邮箱前缀 (当前: test_) 🚀 运行方式: go run cleanup_test_data.go 📊 清理规则: - 删除邮箱包含 "test" 的用户 - 删除邮箱包含 "example.com" 的用户 - 删除邮箱以 "test_" 开头的用户 - 检查数据一致性问题 🔗 使用的API接口: - POST /v1/auth/login - 管理员登录 - GET /v1/admin/user - 获取用户列表 - DELETE /v1/admin/user/batch - 批量删除用户 - GET /v1/admin/user/{id} - 检查用户详情 ⚠️ 安全提示: - 仅在测试环境中使用 - 生产环境请谨慎操作 - 建议先备份数据库 - 确认管理员账户信息正确 🐛 问题排查: 如果遇到 "record not found" 错误: 1. 运行此脚本清理测试数据 2. 检查数据库外键约束 3. 确保测试流程的数据清理完整性`) } // 管理员登录 func adminLogin() error { loginData := map[string]string{ "email": adminEmail, "password": adminPassword, } jsonData, _ := json.Marshal(loginData) resp, err := client.Post(baseURL+"/v1/auth/login", "application/json", bytes.NewBuffer(jsonData)) if err != nil { return fmt.Errorf("登录请求失败: %v", err) } defer resp.Body.Close() body, err := io.ReadAll(resp.Body) if err != nil { return fmt.Errorf("读取响应失败: %v", err) } var loginResp AdminLoginResponse if err := json.Unmarshal(body, &loginResp); err != nil { return fmt.Errorf("解析登录响应失败: %v", err) } if loginResp.Code != 200 { return fmt.Errorf("登录失败 (Code: %d): %s", loginResp.Code, loginResp.Message) } adminToken = loginResp.Data.Token return nil } // 清理测试用户 func cleanupTestUsers() error { // 获取用户列表 users, err := getUserList() if err != nil { return fmt.Errorf("获取用户列表失败: %v", err) } fmt.Printf("📊 总用户数: %d\n", len(users)) var testUserIDs []int64 var testUsers []string for _, user := range users { // 识别测试用户的规则 isTestUser := strings.HasPrefix(user.Email, testEmailPrefix) || strings.Contains(user.Email, "test") || strings.Contains(user.Email, "example.com") if isTestUser { testUserIDs = append(testUserIDs, user.ID) testUsers = append(testUsers, fmt.Sprintf("ID=%d, Email=%s", user.ID, user.Email)) } } if len(testUserIDs) == 0 { fmt.Println("✅ 未发现测试用户,数据已清理") return nil } fmt.Printf("🔍 发现 %d 个测试用户:\n", len(testUserIDs)) for _, userInfo := range testUsers { fmt.Printf(" - %s\n", userInfo) } // 批量删除测试用户 fmt.Printf("\n🗑️ 正在删除 %d 个测试用户...\n", len(testUserIDs)) if err := batchDeleteUsers(testUserIDs); err != nil { return fmt.Errorf("批量删除用户失败: %v", err) } fmt.Printf("✅ 成功删除 %d 个测试用户\n", len(testUserIDs)) return nil } // 检查数据一致性 func checkDataConsistency() { fmt.Println("🔍 检查数据一致性...") // 获取用户列表,检查是否还有问题用户 users, err := getUserList() if err != nil { fmt.Printf("❌ 无法获取用户列表: %v\n", err) return } problemUsers := 0 for _, user := range users { // 检查用户详情是否可以正常获取 if !checkUserDetail(user.ID) { problemUsers++ fmt.Printf("⚠️ 用户 ID=%d Email=%s 可能存在数据问题\n", user.ID, user.Email) } } if problemUsers == 0 { fmt.Println("✅ 数据一致性检查通过") } else { fmt.Printf("⚠️ 发现 %d 个可能有问题的用户记录\n", problemUsers) } } // 获取用户列表 func getUserList() ([]struct { ID int64 `json:"id"` Email string `json:"email"` }, error) { req, err := http.NewRequest("GET", baseURL+"/v1/admin/user?page=1&size=1000", nil) if err != nil { return nil, err } req.Header.Set("Authorization", "Bearer "+adminToken) req.Header.Set("Content-Type", "application/json") resp, err := client.Do(req) if err != nil { return nil, err } defer resp.Body.Close() body, err := io.ReadAll(resp.Body) if err != nil { return nil, err } var userResp UserListResponse if err := json.Unmarshal(body, &userResp); err != nil { return nil, err } if userResp.Code != 200 { return nil, fmt.Errorf("获取用户列表失败 (Code: %d): %s", userResp.Code, userResp.Message) } return userResp.Data.List, nil } // 批量删除用户 func batchDeleteUsers(userIDs []int64) error { deleteData := map[string][]int64{ "ids": userIDs, } jsonData, _ := json.Marshal(deleteData) req, err := http.NewRequest("DELETE", baseURL+"/v1/admin/user/batch", bytes.NewBuffer(jsonData)) if err != nil { return err } req.Header.Set("Authorization", "Bearer "+adminToken) req.Header.Set("Content-Type", "application/json") resp, err := client.Do(req) if err != nil { return err } defer resp.Body.Close() body, err := io.ReadAll(resp.Body) if err != nil { return err } if resp.StatusCode != 200 { return fmt.Errorf("删除用户失败,状态码: %d, 响应: %s", resp.StatusCode, string(body)) } var commonResp CommonResponse if err := json.Unmarshal(body, &commonResp); err == nil && commonResp.Code != 200 { return fmt.Errorf("删除用户失败 (Code: %d): %s", commonResp.Code, commonResp.Message) } return nil } // 检查用户详情 func checkUserDetail(userID int64) bool { req, err := http.NewRequest("GET", baseURL+"/v1/admin/user/"+strconv.FormatInt(userID, 10), nil) if err != nil { return false } req.Header.Set("Authorization", "Bearer "+adminToken) req.Header.Set("Content-Type", "application/json") resp, err := client.Do(req) if err != nil { return false } defer resp.Body.Close() return resp.StatusCode == 200 }