This commit is contained in:
huangzhenpc
2025-05-23 23:08:02 +08:00
parent 9444bf4802
commit a8d25054fc
14 changed files with 2277 additions and 0 deletions

866
pkg/api/email_api.go Normal file
View File

@@ -0,0 +1,866 @@
package api
import (
"bytes"
"encoding/base64"
"encoding/json"
"fmt"
"io/ioutil"
"math/rand"
"net/http"
"strings"
"time"
)
// 初始化随机数生成器
func init() {
rand.Seed(time.Now().UnixNano())
}
// Client 表示Stalwart邮件API客户端
type Client struct {
Config *Config
APIBaseURL string
}
// NewClient 创建新的API客户端
func NewClient(config *Config) *Client {
client := &Client{
Config: config,
APIBaseURL: config.API.BaseURL,
}
if config.API.DisableProxy {
DisableProxy()
}
return client
}
// AuthType 认证类型
type AuthType string
const (
// BasicAuth 表示基本认证
BasicAuth AuthType = "basic"
// APIKeyAuth 表示API Key认证
APIKeyAuth AuthType = "apikey"
)
// User 表示用户信息
type User struct {
ID int `json:"id"`
Type string `json:"type"`
Name string `json:"name"`
Description string `json:"description"`
Quota int64 `json:"quota"`
Emails []string `json:"emails"`
MemberOf []string `json:"memberOf"`
Roles []string `json:"roles"`
Secrets []string `json:"secrets"`
}
// UserList 表示用户列表响应
type UserList struct {
Data struct {
Items []User `json:"items"`
Total int `json:"total"`
} `json:"data"`
}
// APIResponse 表示通用API响应
type APIResponse struct {
Data json.RawMessage `json:"data"`
Error string `json:"error"`
Message string `json:"message"`
Field string `json:"field"`
Value string `json:"value"`
}
// GenerateRandomUsername 生成随机用户名
func GenerateRandomUsername(length int) string {
// 常见的英文名
firstNames := []string{
"alex", "bob", "chris", "david", "eric", "frank", "gary", "henry",
"ian", "jack", "kevin", "leo", "mike", "nick", "oliver", "peter",
"ryan", "sam", "tom", "victor", "william", "zack",
"anna", "betty", "cathy", "diana", "emma", "fiona", "grace", "helen",
"irene", "jane", "kate", "lily", "mary", "nina", "olivia", "penny",
"queen", "rose", "sarah", "tina", "uma", "vicky", "wendy", "zoe",
}
// 常见的姓氏
lastNames := []string{
"smith", "johnson", "williams", "jones", "brown", "davis", "miller",
"wilson", "taylor", "clark", "hall", "lee", "allen", "young", "king",
"wright", "hill", "scott", "green", "adams", "baker", "carter", "cook",
}
// 随机选择名和姓
firstName := firstNames[rand.Intn(len(firstNames))]
lastName := lastNames[rand.Intn(len(lastNames))]
// 添加1-999的随机数字
number := rand.Intn(999) + 1
// 组合成用户名 - 不使用特殊字符,直接连接
username := fmt.Sprintf("%s%s%d", firstName, lastName, number)
// 如果指定了长度且用户名太长,就截断
if length > 0 && len(username) > length {
username = username[:length]
}
return username
}
// GenerateRandomPassword 生成随机密码
func GenerateRandomPassword(length int) string {
if length <= 0 {
length = 10 // 默认10位密码
}
// 简单词汇列表
words := []string{
"apple", "blue", "cat", "dog", "easy", "fish", "good", "home",
"idea", "jump", "king", "love", "moon", "nice", "open", "park",
"queen", "red", "star", "time", "user", "view", "work", "year",
}
// 随机选择一个词
word := words[rand.Intn(len(words))]
// 确保词不超过长度限制的一半
if len(word) > length/2 {
word = word[:length/2]
}
// 为剩余长度生成数字和特殊字符
remainingLength := length - len(word)
numbers := "0123456789"
specials := "@#$%&*!"
var password strings.Builder
password.WriteString(word)
// 添加至少一个特殊字符
password.WriteByte(specials[rand.Intn(len(specials))])
remainingLength--
// 添加至少一个数字
password.WriteByte(numbers[rand.Intn(len(numbers))])
remainingLength--
// 填充剩余长度
for i := 0; i < remainingLength; i++ {
// 使用大小写字母、数字组合
allChars := "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
password.WriteByte(allChars[rand.Intn(len(allChars))])
}
// 将密码字符打乱顺序
passwordBytes := []byte(password.String())
rand.Shuffle(len(passwordBytes), func(i, j int) {
passwordBytes[i], passwordBytes[j] = passwordBytes[j], passwordBytes[i]
})
return string(passwordBytes)
}
// ParseEmail 解析邮箱地址
func (c *Client) ParseEmail(email string) (string, string) {
parts := strings.Split(email, "@")
if len(parts) == 2 {
return parts[0], parts[1]
}
return email, c.Config.User.DefaultDomain
}
// PrepareUserData 准备用户数据
func (c *Client) PrepareUserData(email, description string, password string, quota int64) map[string]interface{} {
if password == "" {
// 不再使用默认密码,而是生成随机密码
password = GenerateRandomPassword(12)
}
if quota == 0 {
quota = c.Config.User.DefaultQuota
}
if description == "" {
description = fmt.Sprintf("%s account", email)
}
// 准备用户数据
return map[string]interface{}{
"type": "individual",
"name": email,
"description": description,
"quota": quota,
"emails": []string{email},
"memberOf": []string{},
"roles": []string{"user"},
"secrets": []string{password},
}
}
// GetAuthHeaders 获取认证头
func (c *Client) GetAuthHeaders(authType AuthType, username, password, apiKey string) map[string]string {
headers := map[string]string{
"Accept": "application/json",
"Content-Type": "application/json",
}
if authType == APIKeyAuth {
// API Key认证
if apiKey == "" {
apiKey = c.Config.Auth.APIKey
}
fmt.Printf("使用的API Key: %s\n", apiKey)
// 检查是否已经是完整的授权头
if strings.HasPrefix(apiKey, "Bearer ") {
headers["Authorization"] = apiKey
} else if strings.HasPrefix(apiKey, "api_") {
// 使用标准格式 api_XXX
headers["Authorization"] = fmt.Sprintf("Bearer %s", apiKey)
fmt.Println("使用标准API Key格式: Bearer api_XXX")
} else {
// 可能是纯Token尝试直接使用
headers["Authorization"] = fmt.Sprintf("Bearer %s", apiKey)
fmt.Println("使用Token格式: Bearer XXX")
}
} else {
// 基本认证
if username == "" {
username = c.Config.Auth.Basic.AdminUsername
}
if password == "" {
password = c.Config.Auth.Basic.AdminPassword
}
auth := base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%s:%s", username, password)))
headers["Authorization"] = fmt.Sprintf("Basic %s", auth)
}
fmt.Println("使用的认证头:", headers["Authorization"])
return headers
}
// MakeAPIRequest 发送API请求
func (c *Client) MakeAPIRequest(method, endpoint string, data interface{}, headers map[string]string) (*http.Response, []byte, error) {
url := fmt.Sprintf("%s/%s", c.APIBaseURL, strings.TrimPrefix(endpoint, "/"))
fmt.Printf("发送请求: %s %s\n", method, url)
if headers["Authorization"] != "" {
authType := strings.Split(headers["Authorization"], " ")[0]
fmt.Printf("认证方式: %s\n", authType)
}
var reqBody []byte
var err error
if data != nil {
reqBody, err = json.Marshal(data)
if err != nil {
return nil, nil, fmt.Errorf("序列化请求数据失败: %w", err)
}
fmt.Printf("请求数据: %s\n", string(reqBody))
}
req, err := http.NewRequest(method, url, bytes.NewBuffer(reqBody))
if err != nil {
return nil, nil, fmt.Errorf("创建HTTP请求失败: %w", err)
}
// 设置请求头
for key, value := range headers {
req.Header.Set(key, value)
}
// 发送请求
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return nil, nil, fmt.Errorf("发送HTTP请求失败: %w", err)
}
// 读取响应
respBody, err := ioutil.ReadAll(resp.Body)
defer resp.Body.Close()
if err != nil {
return resp, nil, fmt.Errorf("读取响应失败: %w", err)
}
fmt.Printf("响应状态码: %d\n", resp.StatusCode)
fmt.Printf("响应内容: %s\n", string(respBody))
// 检查认证错误
if resp.StatusCode == 401 || resp.StatusCode == 403 {
fmt.Println("认证失败,可能需要尝试其他认证方式")
}
return resp, respBody, nil
}
// CreateUser 创建邮箱用户
func (c *Client) CreateUser(email, password, description string, quota int64, authType AuthType, username, authPassword, apiKey string) (int, map[string]interface{}, error) {
// 准备用户数据
userData := c.PrepareUserData(email, description, password, quota)
fmt.Printf("创建用户 %s 并设置密码...\n", email)
// 获取认证头
headers := c.GetAuthHeaders(authType, username, authPassword, apiKey)
// 创建用户
resp, respBody, err := c.MakeAPIRequest("POST", "/principal", userData, headers)
if err != nil {
return 0, nil, fmt.Errorf("创建用户请求失败: %w", err)
}
if resp == nil {
return 0, nil, fmt.Errorf("创建用户失败:未收到响应")
}
// 解析响应
var apiResp APIResponse
if err := json.Unmarshal(respBody, &apiResp); err != nil {
return 0, nil, fmt.Errorf("解析响应失败: %w", err)
}
// 检查是否返回错误
if apiResp.Error != "" {
// 特殊处理如果用户已存在可以尝试获取已存在用户的ID
if apiResp.Error == "fieldAlreadyExists" && apiResp.Field == "name" {
existingName := apiResp.Value
fmt.Printf("用户 %s 已存在,尝试获取现有用户信息...\n", existingName)
// 构造一个模拟信息
userInfo := map[string]interface{}{
"name": email,
"emails": []string{email},
"description": description,
"quota": quota,
"_note": "This user already exists",
}
// 返回一个特殊标记的ID负数表示这是一个已存在的用户
return -1, userInfo, nil
}
return 0, nil, fmt.Errorf("创建用户失败: %s - %s", apiResp.Error, apiResp.Message)
}
// 获取用户ID
var userID int
if err := json.Unmarshal(apiResp.Data, &userID); err != nil {
// 尝试解析为对象
var objData map[string]interface{}
if err := json.Unmarshal(apiResp.Data, &objData); err != nil {
return 0, nil, fmt.Errorf("解析用户ID失败: %w", err)
}
// 检查是否有id字段
if id, ok := objData["id"].(float64); ok {
userID = int(id)
} else {
return 0, nil, fmt.Errorf("未能从响应中获取用户ID")
}
}
if userID == 0 {
return 0, nil, fmt.Errorf("创建用户失败未获取到用户ID")
}
fmt.Printf("用户创建成功ID: %d\n", userID)
// 尝试获取用户详情进行验证
userInfo, err := c.GetUserDetails(userID, authType, username, authPassword, apiKey)
if err != nil || userInfo == nil {
fmt.Println("获取用户详情失败,但用户创建成功")
// 创建一个包含基本信息的对象
userInfo = map[string]interface{}{
"name": email,
"emails": []string{email},
"description": description,
"quota": quota,
"_note": "This is a synthetic record as actual details could not be retrieved",
}
}
return userID, userInfo, nil
}
// GetUserDetails 获取用户详情
func (c *Client) GetUserDetails(userID int, authType AuthType, username, authPassword, apiKey string) (map[string]interface{}, error) {
fmt.Println("\n获取用户详情...")
// 获取认证头
headers := c.GetAuthHeaders(authType, username, authPassword, apiKey)
// 获取用户详情
endpoint := fmt.Sprintf("/principal/%d", userID)
resp, respBody, err := c.MakeAPIRequest("GET", endpoint, nil, headers)
if err != nil {
return nil, fmt.Errorf("获取用户详情请求失败: %w", err)
}
if resp == nil || resp.StatusCode != 200 {
return nil, fmt.Errorf("获取用户详情失败")
}
// 解析响应
var apiResp APIResponse
if err := json.Unmarshal(respBody, &apiResp); err != nil {
return nil, fmt.Errorf("解析响应失败: %w", err)
}
// 检查是否返回错误
if apiResp.Error != "" {
return nil, fmt.Errorf("获取用户详情失败: %s - %s", apiResp.Error, apiResp.Message)
}
// 解析用户数据
var userData map[string]interface{}
if err := json.Unmarshal(apiResp.Data, &userData); err != nil {
// 如果无法解析为对象,直接返回整个响应
var rawResp map[string]interface{}
if err := json.Unmarshal(respBody, &rawResp); err != nil {
return nil, fmt.Errorf("解析用户数据失败: %w", err)
}
return rawResp, nil
}
if userData == nil {
return nil, fmt.Errorf("获取用户详情失败:响应中没有用户数据")
}
// 打印关键信息
fmt.Printf("用户名: %v\n", userData["name"])
emails, _ := userData["emails"].([]interface{})
if len(emails) > 0 {
fmt.Printf("邮箱: %v\n", emails[0])
}
return userData, nil
}
// ListUsers 获取用户列表
func (c *Client) ListUsers(authType AuthType, username, authPassword, apiKey string, limit, page int) (*UserList, error) {
fmt.Println("获取用户列表...")
// 获取认证头
headers := c.GetAuthHeaders(authType, username, authPassword, apiKey)
// 构建查询参数
endpoint := fmt.Sprintf("/principal?limit=%d&page=%d", limit, page)
// 尝试不同的API端点路径
endpoints := []string{
endpoint, // 根据规范文档的主要端点
"/principals", // 原始尝试
"/users", // 通用users格式
"/user", // 单数形式
"/accounts", // 有些系统用accounts
"/directory", // 目录形式
}
var resp *http.Response
var respBody []byte
var err error
var success bool
for _, ep := range endpoints {
fmt.Printf("尝试端点: %s\n", ep)
resp, respBody, err = c.MakeAPIRequest("GET", ep, nil, headers)
if err == nil && resp != nil && resp.StatusCode == 200 {
fmt.Printf("成功的端点: %s\n", ep)
success = true
break
}
}
if !success {
return nil, fmt.Errorf("获取用户列表失败,尝试了所有可能的端点")
}
// 解析响应
fmt.Println("响应数据结构:")
fmt.Println(string(respBody))
var userList UserList
if err := json.Unmarshal(respBody, &userList); err != nil {
// 尝试解析为不同格式
var rawResp map[string]interface{}
if err := json.Unmarshal(respBody, &rawResp); err != nil {
return nil, fmt.Errorf("解析用户列表失败: %w", err)
}
// 尝试不同的数据格式
users := []User{}
// 检查data.items格式
if data, ok := rawResp["data"].(map[string]interface{}); ok {
if items, ok := data["items"].([]interface{}); ok {
for _, item := range items {
if user, ok := item.(map[string]interface{}); ok {
userObj := parseUserObject(user)
users = append(users, userObj)
}
}
userList.Data.Items = users
userList.Data.Total = len(users)
return &userList, nil
}
}
// 检查data数组格式
if data, ok := rawResp["data"].([]interface{}); ok {
for _, item := range data {
if user, ok := item.(map[string]interface{}); ok {
userObj := parseUserObject(user)
users = append(users, userObj)
}
}
userList.Data.Items = users
userList.Data.Total = len(users)
return &userList, nil
}
// 检查principals数组格式
if principals, ok := rawResp["principals"].([]interface{}); ok {
for _, item := range principals {
if user, ok := item.(map[string]interface{}); ok {
userObj := parseUserObject(user)
users = append(users, userObj)
}
}
userList.Data.Items = users
userList.Data.Total = len(users)
return &userList, nil
}
// 检查users数组格式
if usersArray, ok := rawResp["users"].([]interface{}); ok {
for _, item := range usersArray {
if user, ok := item.(map[string]interface{}); ok {
userObj := parseUserObject(user)
users = append(users, userObj)
}
}
userList.Data.Items = users
userList.Data.Total = len(users)
return &userList, nil
}
return nil, fmt.Errorf("未能从响应中提取用户列表")
}
fmt.Printf("找到 %d 个用户\n", len(userList.Data.Items))
return &userList, nil
}
// parseUserObject 解析用户对象
func parseUserObject(user map[string]interface{}) User {
userObj := User{}
// ID
if id, ok := user["id"].(float64); ok {
userObj.ID = int(id)
}
// Type
if typ, ok := user["type"].(string); ok {
userObj.Type = typ
}
// Name
if name, ok := user["name"].(string); ok {
userObj.Name = name
}
// Description
if desc, ok := user["description"].(string); ok {
userObj.Description = desc
}
// Quota
if quota, ok := user["quota"].(float64); ok {
userObj.Quota = int64(quota)
}
// Emails
if emails, ok := user["emails"].([]interface{}); ok {
for _, email := range emails {
if e, ok := email.(string); ok {
userObj.Emails = append(userObj.Emails, e)
}
}
} else if email, ok := user["emails"].(string); ok {
userObj.Emails = []string{email}
}
// MemberOf
if memberOf, ok := user["memberOf"].([]interface{}); ok {
for _, member := range memberOf {
if m, ok := member.(string); ok {
userObj.MemberOf = append(userObj.MemberOf, m)
}
}
}
// Roles
if roles, ok := user["roles"].([]interface{}); ok {
for _, role := range roles {
if r, ok := role.(string); ok {
userObj.Roles = append(userObj.Roles, r)
}
}
}
return userObj
}
// GetUserByEmail 通过邮箱地址查询用户信息
func (c *Client) GetUserByEmail(email string, authType AuthType, username, authPassword, apiKey string) (map[string]interface{}, error) {
fmt.Printf("通过邮箱查询用户: %s\n", email)
// 获取所有用户,然后过滤
userList, err := c.ListUsers(authType, username, authPassword, apiKey, 1000, 1)
if err != nil {
return nil, err
}
// 尝试在列表中查找匹配的邮箱
for _, user := range userList.Data.Items {
// 检查用户邮箱是否匹配
for _, userEmail := range user.Emails {
if userEmail == email {
fmt.Printf("找到匹配的用户ID: %d\n", user.ID)
// 获取详细信息
return c.GetUserDetails(user.ID, authType, username, authPassword, apiKey)
}
}
if user.Name == email {
fmt.Printf("找到匹配的用户ID: %d\n", user.ID)
// 获取详细信息
return c.GetUserDetails(user.ID, authType, username, authPassword, apiKey)
}
}
fmt.Printf("未找到邮箱为 %s 的用户\n", email)
return nil, fmt.Errorf("未找到用户")
}
// FormatUserList 格式化输出用户列表
func FormatUserList(userList *UserList) {
if userList == nil || len(userList.Data.Items) == 0 {
fmt.Println("没有用户数据可显示")
return
}
fmt.Println("\n=== 用户列表 ===")
fmt.Printf("找到 %d 个用户\n", len(userList.Data.Items))
fmt.Println(strings.Repeat("-", 60))
fmt.Printf("%-10s %-30s %-15s %-30s\n", "ID", "名称", "类型", "邮箱")
fmt.Println(strings.Repeat("-", 60))
for _, user := range userList.Data.Items {
email := "N/A"
if len(user.Emails) > 0 {
email = user.Emails[0]
}
// 截断过长的字符串
name := user.Name
if len(name) > 28 {
name = name[:28]
}
if len(email) > 28 {
email = email[:28]
}
fmt.Printf("%-10d %-30s %-15s %-30s\n", user.ID, name, user.Type, email)
}
fmt.Println(strings.Repeat("-", 60))
}
// FormatUserDetails 格式化输出单个用户的详细信息
func FormatUserDetails(userInfo map[string]interface{}) {
if userInfo == nil {
fmt.Println("没有用户数据可显示")
return
}
fmt.Println("\n=== 用户详细信息 ===")
fmt.Println(strings.Repeat("-", 60))
// 获取基本信息
id, _ := userInfo["id"]
name, _ := userInfo["name"]
description, _ := userInfo["description"]
typ, _ := userInfo["type"]
quota, _ := userInfo["quota"]
// 显示类型转换
var quotaFloat float64
switch q := quota.(type) {
case float64:
quotaFloat = q
case int64:
quotaFloat = float64(q)
case int:
quotaFloat = float64(q)
}
quotaMB := quotaFloat / (1024 * 1024)
fmt.Printf("ID: %v\n", id)
fmt.Printf("名称: %v\n", name)
fmt.Printf("描述: %v\n", description)
fmt.Printf("类型: %v\n", typ)
fmt.Printf("配额: %.2f MB (%.0f 字节)\n", quotaMB, quotaFloat)
// 获取邮箱
if emails, ok := userInfo["emails"].([]interface{}); ok && len(emails) > 0 {
fmt.Println("\n邮箱地址:")
for _, email := range emails {
fmt.Printf(" - %v\n", email)
}
} else if email, ok := userInfo["emails"].(string); ok {
fmt.Println("\n邮箱地址:")
fmt.Printf(" - %s\n", email)
}
// 获取成员组
if memberOf, ok := userInfo["memberOf"].([]interface{}); ok && len(memberOf) > 0 {
fmt.Println("\n所属组:")
for _, group := range memberOf {
fmt.Printf(" - %v\n", group)
}
}
// 其他属性
fmt.Println("\n其他属性:")
for key, value := range userInfo {
if key != "id" && key != "name" && key != "description" && key != "type" && key != "quota" && key != "emails" && key != "memberOf" {
// 跳过密码相关字段
if strings.Contains(strings.ToLower(key), "secret") || strings.Contains(strings.ToLower(key), "password") {
continue
}
fmt.Printf(" - %s: %v\n", key, value)
}
}
fmt.Println(strings.Repeat("-", 60))
}
// CreateEmailUser 创建邮箱用户的统一入口
func (c *Client) CreateEmailUser(emailAddress, password, description string, quota int64, authType AuthType, username, authPassword, apiKey string) (int, string, string, string, error) {
// 如果没有提供邮箱地址,则生成随机用户名
if emailAddress == "" {
randomUsername := GenerateRandomUsername(8)
// 确保用户名只包含字母和数字
for i := 0; i < len(randomUsername); i++ {
if !((randomUsername[i] >= 'a' && randomUsername[i] <= 'z') ||
(randomUsername[i] >= '0' && randomUsername[i] <= '9')) {
// 替换为字母
randomUsername = randomUsername[:i] + "x" + randomUsername[i+1:]
}
}
emailAddress = fmt.Sprintf("%s@%s", randomUsername, c.Config.User.DefaultDomain)
}
// 解析邮箱地址
usernameLocal, domain := c.ParseEmail(emailAddress)
// 确保用户名只包含字母和数字
cleanUsername := ""
for i := 0; i < len(usernameLocal); i++ {
if (usernameLocal[i] >= 'a' && usernameLocal[i] <= 'z') ||
(usernameLocal[i] >= '0' && usernameLocal[i] <= '9') {
cleanUsername += string(usernameLocal[i])
} else {
cleanUsername += "x" // 替换非法字符为x
}
}
usernameLocal = cleanUsername
email := fmt.Sprintf("%s@%s", usernameLocal, domain)
// 如果没有提供密码,生成随机密码
if password == "" {
password = GenerateRandomPassword(12)
fmt.Println("已生成随机密码")
}
// 设置认证类型消息
if authType == APIKeyAuth {
// 从API Key中提取用户名用于显示
if apiKey != "" && strings.HasPrefix(apiKey, "api_") {
decoded, err := base64.StdEncoding.DecodeString(apiKey[4:])
if err == nil {
decodedStr := string(decoded)
if strings.Contains(decodedStr, ":") {
parts := strings.SplitN(decodedStr, ":", 2)
fmt.Printf("使用API Key认证 (用户: %s)\n", parts[0])
} else {
fmt.Println("使用API Key认证")
}
} else {
fmt.Println("使用API Key认证")
}
} else {
fmt.Println("使用API Key认证")
}
} else {
actualUsername := username
if actualUsername == "" {
actualUsername = c.Config.Auth.Basic.AdminUsername
}
fmt.Printf("使用基本认证 (用户: %s)\n", actualUsername)
}
// 创建用户
userID, _, err := c.CreateUser(email, password, description, quota, authType, username, authPassword, apiKey)
if err != nil {
return 0, "", "", "", err
}
// 特殊处理如果用户已存在用户ID为负数
if userID < 0 {
fmt.Printf("用户 %s 已存在,使用现有用户信息\n", email)
userID = -userID // 转为正数便于显示
}
// 返回用户信息
return userID, usernameLocal, email, password, nil
}
// PrintUserResult 打印用户创建结果
func PrintUserResult(userID int, username, email, password string) bool {
if userID == 0 {
fmt.Println("\n创建用户失败")
return false
}
domainParts := strings.Split(email, "@")
domain := ""
if len(domainParts) > 1 {
domain = domainParts[1]
}
fmt.Println("\n=== 新创建的用户信息 ===")
fmt.Printf("用户ID: %d\n", userID)
fmt.Printf("登录名: %s\n", username)
fmt.Printf("邮箱地址: %s\n", email)
fmt.Printf("密码: %s\n", password)
fmt.Printf("\n登录信息:\n")
fmt.Printf(" - 登录名: %s\n", username)
fmt.Printf(" - 密码: %s\n", password)
fmt.Printf(" - 邮箱服务器: mail.%s\n", domain)
return true
}