hi-server/scripts/cleanup_test_data.go
shanshanzhong 1bcfa321b7
All checks were successful
Build docker and publish / build (20.15.1) (push) Successful in 7m9s
feat: 添加测试数据清理脚本并改进设备登录逻辑
docs(scripts): 添加测试数据清理脚本的详细使用文档
fix(auth): 修复设备登录时处理孤立认证方法的问题
refactor(public): 改进邮箱绑定逻辑中的推荐码处理
2025-10-27 22:20:18 -07:00

339 lines
8.2 KiB
Go
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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
}