Files
user_service/deploy/api/api_register/main.go
2025-10-09 18:19:57 +08:00

272 lines
7.5 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

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 (
"database/sql"
"fmt"
"net/http"
"os"
"time"
"github.com/gin-gonic/gin"
_ "github.com/lib/pq"
"golang.org/x/crypto/bcrypt"
)
// RegisterRequest 注册请求参数结构
type RegisterRequest struct {
Account string `json:"account" binding:"required"`
Password string `json:"password" binding:"required"`
}
// RegisterResponse 注册响应结构
type RegisterResponse struct {
Success bool `json:"success"`
Message string `json:"message"`
Data struct {
UserID string `json:"user_id,omitempty"`
Account string `json:"account,omitempty"`
} `json:"data,omitempty"`
}
var db *sql.DB
func main() {
// 初始化Gin引擎
fmt.Println("[主程序] 初始化Gin引擎")
r := gin.Default()
// 从环境变量获取数据库配置
fmt.Println("[主程序] 读取数据库配置环境变量")
dbHost := os.Getenv("DB_HOST")
dbPort := os.Getenv("DB_PORT")
dbUser := os.Getenv("DB_USER")
dbPassword := os.Getenv("DB_PASSWORD")
dbName := os.Getenv("DB_NAME")
// 构建数据库连接字符串
connStr := fmt.Sprintf(
"host=%s port=%s user=%s password=%s dbname=%s sslmode=disable",
dbHost, dbPort, dbUser, dbPassword, dbName,
)
fmt.Printf("[主程序] 数据库连接字符串构建完成: %s\n", maskPassword(connStr))
// 连接数据库
var err error
db, err = sql.Open("postgres", connStr)
if err != nil {
panic(fmt.Sprintf("[主程序] 无法连接数据库: %v", err))
}
defer db.Close()
fmt.Println("[主程序] 数据库连接对象创建成功")
// 验证数据库连接
if err := db.Ping(); err != nil {
panic(fmt.Sprintf("[主程序] 数据库连接失败: %v", err))
}
fmt.Println("[主程序] 数据库连接验证成功")
// 注册账号接口
r.POST("/user/register", registerHandler)
fmt.Println("[主程序] 注册接口路由已配置")
// 启动服务监听80端口
fmt.Println("[主程序] 服务启动在80端口")
r.Run(":80")
}
// 屏蔽连接字符串中的密码,避免日志泄露敏感信息
func maskPassword(connStr string) string {
// 简单处理替换password=后的内容直到下一个空格
start := false
result := ""
for _, c := range connStr {
if start && c == ' ' {
start = false
}
if start {
continue
}
result += string(c)
// 先检查长度是否足够,避免索引越界
if len(result) >= 10 && result[len(result)-10:] == "password=" {
start = true
result += "***"
}
}
return result
}
// registerHandler 处理用户注册逻辑
func registerHandler(c *gin.Context) {
startTime := time.Now()
reqID := fmt.Sprintf("req-%d", time.Now().UnixNano()) // 生成请求唯一标识
fmt.Printf("[%s] 收到注册请求,开始处理\n", reqID)
var req RegisterRequest
// 绑定并验证请求参数
if err := c.ShouldBindJSON(&req); err != nil {
fmt.Printf("[%s] 请求参数绑定失败: %v\n", reqID, err)
c.JSON(http.StatusBadRequest, RegisterResponse{
Success: false,
Message: "请求参数错误: " + err.Error(),
})
return
}
fmt.Printf("[%s] 请求参数绑定成功,账号: %s\n", reqID, req.Account)
// 1. 判断接口的账号和密码是否为空
if req.Account == "" || req.Password == "" {
fmt.Printf("[%s] 账号或密码为空,拒绝注册\n", reqID)
c.JSON(http.StatusBadRequest, RegisterResponse{
Success: false,
Message: "账号和密码不能为空",
})
return
}
// 2. 使用接口账号查询视图,检查账号是否已存在
var exists bool
query := `
SELECT EXISTS(
SELECT 1
FROM user_account_password_view
WHERE account = $1 AND deleted = false
)
`
fmt.Printf("[%s] 执行账号存在性查询: %s\n", reqID, query)
err := db.QueryRow(query, req.Account).Scan(&exists)
if err != nil {
fmt.Printf("[%s] 账号查询失败: %v\n", reqID, err)
c.JSON(http.StatusInternalServerError, RegisterResponse{
Success: false,
Message: "查询账号信息失败: " + err.Error(),
})
return
}
fmt.Printf("[%s] 账号存在性查询完成,存在: %v\n", reqID, exists)
// 3. 判断查询结果,若存在则提示账号已存在
if exists {
fmt.Printf("[%s] 账号已存在: %s\n", reqID, req.Account)
c.JSON(http.StatusOK, RegisterResponse{
Success: false,
Message: "账号已存在",
})
return
}
// 4. 开启数据库事务,确保数据一致性
tx, err := db.Begin()
if err != nil {
fmt.Printf("[%s] 开启事务失败: %v\n", reqID, err)
c.JSON(http.StatusInternalServerError, RegisterResponse{
Success: false,
Message: "开启事务失败: " + err.Error(),
})
return
}
defer func() {
if r := recover(); r != nil {
fmt.Printf("[%s] 发生恐慌,回滚事务: %v\n", reqID, r)
tx.Rollback()
}
}()
fmt.Printf("[%s] 数据库事务开启成功\n", reqID)
// 5. 在user表生成新用户ID
var userID string
insertUserQuery := `
INSERT INTO "user" DEFAULT VALUES
RETURNING id
`
fmt.Printf("[%s] 执行用户创建: %s\n", reqID, insertUserQuery)
err = tx.QueryRow(insertUserQuery).Scan(&userID)
if err != nil {
fmt.Printf("[%s] 创建用户失败: %v\n", reqID, err)
tx.Rollback()
c.JSON(http.StatusInternalServerError, RegisterResponse{
Success: false,
Message: "创建用户失败: " + err.Error(),
})
return
}
fmt.Printf("[%s] 用户创建成功用户ID: %s\n", reqID, userID)
// 6. 对密码进行加密处理
fmt.Printf("[%s] 开始密码加密\n", reqID)
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(req.Password), bcrypt.DefaultCost)
if err != nil {
fmt.Printf("[%s] 密码加密失败: %v\n", reqID, err)
tx.Rollback()
c.JSON(http.StatusInternalServerError, RegisterResponse{
Success: false,
Message: "密码加密失败: " + err.Error(),
})
return
}
fmt.Printf("[%s] 密码加密成功\n", reqID)
// 7. 插入user_account表
insertAccountQuery := `
INSERT INTO user_account (user_id, account)
VALUES ($1, $2)
`
fmt.Printf("[%s] 执行账号插入: %s, 参数: user_id=%s, account=%s\n",
reqID, insertAccountQuery, userID, req.Account)
_, err = tx.Exec(insertAccountQuery, userID, req.Account)
if err != nil {
fmt.Printf("[%s] 保存账号信息失败: %v\n", reqID, err)
tx.Rollback()
c.JSON(http.StatusInternalServerError, RegisterResponse{
Success: false,
Message: "保存账号信息失败: " + err.Error(),
})
return
}
fmt.Printf("[%s] 账号信息保存成功\n", reqID)
// 8. 插入user_password表
insertPasswordQuery := `
INSERT INTO user_password (user_id, password)
VALUES ($1, $2)
`
fmt.Printf("[%s] 执行密码插入: %s, 参数: user_id=%s\n",
reqID, insertPasswordQuery, userID)
_, err = tx.Exec(insertPasswordQuery, userID, string(hashedPassword))
if err != nil {
fmt.Printf("[%s] 保存密码信息失败: %v\n", reqID, err)
tx.Rollback()
c.JSON(http.StatusInternalServerError, RegisterResponse{
Success: false,
Message: "保存密码信息失败: " + err.Error(),
})
return
}
fmt.Printf("[%s] 密码信息保存成功\n", reqID)
// 9. 提交事务
if err := tx.Commit(); err != nil {
fmt.Printf("[%s] 提交事务失败: %v\n", reqID, err)
tx.Rollback()
c.JSON(http.StatusInternalServerError, RegisterResponse{
Success: false,
Message: "提交事务失败: " + err.Error(),
})
return
}
fmt.Printf("[%s] 事务提交成功\n", reqID)
// 10. 注册成功
response := RegisterResponse{
Success: true,
Message: "注册成功",
}
response.Data.UserID = userID
response.Data.Account = req.Account
duration := time.Since(startTime)
fmt.Printf("[%s] 注册成功,耗时: %v, 用户ID: %s, 账号: %s\n",
reqID, duration, userID, req.Account)
c.JSON(http.StatusOK, response)
}