集成PostgreSQL数据库并实现用户注册登录功能

主要改动:
- 集成 GORM 和 PostgreSQL 驱动
- 创建数据库配置模块 (pkg/config)
- 实现数据库连接和初始化 (internal/database)
- 更新用户模型支持 GORM 和 bcrypt 密码加密
- 重构用户注册和登录处理器使用数据库存储
- 删除旧的 users.json 文件存储方式
- 更新 README.md 和 CLAUDE.md 文档

技术栈:
- GORM v1.31.1 - ORM框架
- PostgreSQL - 数据库
- bcrypt - 密码加密

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
燕陇琪 2025-11-04 01:12:36 +08:00
parent 9540478583
commit 52d50b97aa
10 changed files with 232 additions and 104 deletions

View File

@ -55,9 +55,10 @@ func MiddlewareName() gin.HandlerFunc {
### 模块结构 ### 模块结构
- **main.go** - 应用程序入口点,服务器配置和路由设置 - **main.go** - 应用程序入口点,服务器配置和路由设置
- **internal/handlers/** - HTTP 请求处理器,全部使用 `*gin.Context` 并返回 JSON 响应 - **internal/handlers/** - HTTP 请求处理器,全部使用 `*gin.Context` 并返回 JSON 响应
- **internal/middleware/** - Gin 中间件链(当前:自定义日志记录器) - **internal/middleware/** - Gin 中间件链(当前:自定义日志记录器、CORS)
- **internal/models/** - 数据模型(目录已存在,准备用于数据库模型) - **internal/models/** - 数据模型(用户模型、问题模型等)
- **pkg/config/** - 配置管理(目录已存在但尚未填充) - **internal/database/** - 数据库连接和初始化
- **pkg/config/** - 配置管理(数据库配置等)
## 常用命令 ## 常用命令
@ -106,12 +107,15 @@ go test -v ./...
## 关键实现细节 ## 关键实现细节
- **框架**: 使用 Gin v1.11.0 - **框架**: 使用 Gin v1.11.0
- **服务器端口**: :8080 (在 [main.go:22](main.go#L22) 中配置) - **ORM**: 使用 GORM v1.31.1
- **数据库**: PostgreSQL (配置在 [pkg/config/config.go](pkg/config/config.go))
- **服务器端口**: :8080 (在 [main.go:42](main.go#L42) 中配置)
- **处理器签名**: 所有处理器使用 `func(c *gin.Context)` 模式 - **处理器签名**: 所有处理器使用 `func(c *gin.Context)` 模式
- **JSON 响应**: 使用 `c.JSON()` 方法配合 `gin.H{}` 或结构体 - **JSON 响应**: 使用 `c.JSON()` 方法配合 `gin.H{}` 或结构体
- **导入路径**: 使用模块名 `ankao` (在 go.mod 中定义) - **导入路径**: 使用模块名 `ankao` (在 go.mod 中定义)
- **路由注册**: 路由在 [main.go](main.go) 中使用 `r.GET()``r.POST()` 等注册 - **路由注册**: 路由在 [main.go](main.go) 中使用 `r.GET()``r.POST()` 等注册
- **中间件**: 使用 `r.Use()` 全局应用或通过路由分组应用到特定路由 - **中间件**: 使用 `r.Use()` 全局应用或通过路由分组应用到特定路由
- **密码加密**: 使用 bcrypt 加密存储用户密码
## 添加新功能 ## 添加新功能
@ -125,10 +129,18 @@ go test -v ./...
2. 使用 `r.Use(middleware.YourMiddleware())` 全局应用或应用到路由组 2. 使用 `r.Use(middleware.YourMiddleware())` 全局应用或应用到路由组
### 数据库集成 ### 数据库集成
项目已准备好进行数据库集成: 项目已集成 PostgreSQL 数据库:
- 在 `internal/models/` 中添加模型 - **配置**: 数据库配置在 [pkg/config/config.go](pkg/config/config.go)
- 考虑使用 GORM 或类似的 ORM - **初始化**: 数据库初始化在 [internal/database/database.go](internal/database/database.go)
- 在 main.go 或单独的包中添加数据库初始化 - **模型定义**: 在 `internal/models/` 中添加 GORM 模型
- **迁移**: 使用 `DB.AutoMigrate()` 自动迁移表结构
- **使用方式**: 通过 `database.GetDB()` 获取数据库实例
### 添加新的数据模型
1. 在 `internal/models/` 中创建模型文件
2. 定义结构体,使用 GORM 标签
3. 在 [internal/database/database.go](internal/database/database.go) 的 `InitDB()` 中添加 `AutoMigrate`
4. 在处理器中使用 `database.GetDB()` 进行数据库操作
## 前端开发规范 ## 前端开发规范

View File

@ -103,30 +103,35 @@ go build -o bin/server.exe main.go
- 自定义日志中间件 - 自定义日志中间件
- RESTful API 结构 - RESTful API 结构
- 健康检查端点 - 健康检查端点
- 用户登录系统(基于JSON文件存储 - 用户登录系统(基于PostgreSQL数据库
- 题目练习功能 - 题目练习功能
- 答题统计功能 - 答题统计功能
- React + TypeScript + Vite 前端 - React + TypeScript + Vite 前端
- Ant Design Mobile UI组件库 - Ant Design Mobile UI组件库
## 用户系统 ## 数据库配置
用户数据存储在 [users.json](users.json) 文件中,格式如下: 项目使用 PostgreSQL 数据库存储用户数据。
```json ### 数据库连接信息
{
"用户名": {
"password": "密码",
"avatar": "头像URL",
"nickname": "昵称"
}
}
```
### 测试账号 数据库配置位于 [pkg/config/config.go](pkg/config/config.go)
- 主机: pgsql.yuchat.top
- 端口: 5432
- 数据库: ankao
- 用户: postgres
- 用户名: `admin` / 密码: `123456` ### 数据库表结构
- 用户名: `test` / 密码: `test123`
**users 表**
- `id` - 主键
- `username` - 用户名(唯一索引)
- `password` - 密码bcrypt加密
- `avatar` - 头像URL
- `nickname` - 昵称
- `created_at` - 创建时间
- `updated_at` - 更新时间
- `deleted_at` - 软删除时间
## 前端开发 ## 前端开发
@ -166,7 +171,8 @@ yarn build
- 自定义日志中间件 - 自定义日志中间件
- RESTful API 结构 - RESTful API 结构
- 健康检查端点 - 健康检查端点
- 用户登录和注册系统基于JSON文件存储 - 用户登录和注册系统基于PostgreSQL数据库
- 密码bcrypt加密存储
- 题目练习功能 - 题目练习功能
- 答题统计功能 - 答题统计功能
@ -183,6 +189,9 @@ yarn build
### 后端 ### 后端
- **Go** 1.25.1 - **Go** 1.25.1
- **Gin** v1.11.0 - Web 框架 - **Gin** v1.11.0 - Web 框架
- **GORM** v1.31.1 - ORM框架
- **PostgreSQL** - 数据库
- **bcrypt** - 密码加密
### 前端 ### 前端
- **React** 18 - UI框架 - **React** 18 - UI框架

14
go.mod
View File

@ -2,7 +2,12 @@ module ankao
go 1.25.1 go 1.25.1
require github.com/gin-gonic/gin v1.11.0 require (
github.com/gin-gonic/gin v1.11.0
golang.org/x/crypto v0.43.0
gorm.io/driver/postgres v1.6.0
gorm.io/gorm v1.31.1
)
require ( require (
github.com/bytedance/gopkg v0.1.3 // indirect github.com/bytedance/gopkg v0.1.3 // indirect
@ -16,6 +21,12 @@ require (
github.com/go-playground/validator/v10 v10.28.0 // indirect github.com/go-playground/validator/v10 v10.28.0 // indirect
github.com/goccy/go-json v0.10.5 // indirect github.com/goccy/go-json v0.10.5 // indirect
github.com/goccy/go-yaml v1.18.0 // indirect github.com/goccy/go-yaml v1.18.0 // indirect
github.com/jackc/pgpassfile v1.0.0 // indirect
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
github.com/jackc/pgx/v5 v5.7.6 // indirect
github.com/jackc/puddle/v2 v2.2.2 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect
github.com/json-iterator/go v1.1.12 // indirect github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/cpuid/v2 v2.3.0 // indirect github.com/klauspost/cpuid/v2 v2.3.0 // indirect
github.com/leodido/go-urn v1.4.0 // indirect github.com/leodido/go-urn v1.4.0 // indirect
@ -29,7 +40,6 @@ require (
github.com/ugorji/go/codec v1.3.1 // indirect github.com/ugorji/go/codec v1.3.1 // indirect
go.uber.org/mock v0.6.0 // indirect go.uber.org/mock v0.6.0 // indirect
golang.org/x/arch v0.22.0 // indirect golang.org/x/arch v0.22.0 // indirect
golang.org/x/crypto v0.43.0 // indirect
golang.org/x/mod v0.29.0 // indirect golang.org/x/mod v0.29.0 // indirect
golang.org/x/net v0.46.0 // indirect golang.org/x/net v0.46.0 // indirect
golang.org/x/sync v0.17.0 // indirect golang.org/x/sync v0.17.0 // indirect

17
go.sum
View File

@ -30,6 +30,18 @@ github.com/goccy/go-yaml v1.18.0/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7Lk
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo=
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
github.com/jackc/pgx/v5 v5.7.6 h1:rWQc5FwZSPX58r1OQmkuaNicxdmExaEz5A2DO2hUuTk=
github.com/jackc/pgx/v5 v5.7.6/go.mod h1:aruU7o91Tc2q2cFp5h4uP3f6ztExVpyVv88Xl/8Vl8M=
github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo=
github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y= github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y=
@ -56,6 +68,7 @@ github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSS
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
@ -91,3 +104,7 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gorm.io/driver/postgres v1.6.0 h1:2dxzU8xJ+ivvqTRph34QX+WrRaJlmfyPqXmoGVjMBa4=
gorm.io/driver/postgres v1.6.0/go.mod h1:vUw0mrGgrTK+uPHEhAdV4sfFELrByKVGnaVRkXDhtWo=
gorm.io/gorm v1.31.1 h1:7CA8FTFz/gRfgqgpeKIBcervUn3xSyPUmr6B2WXJ7kg=
gorm.io/gorm v1.31.1/go.mod h1:XyQVbO2k6YkOis7C2437jSit3SsDK72s7n7rsSHd+Gs=

View File

@ -0,0 +1,45 @@
package database
import (
"ankao/internal/models"
"ankao/pkg/config"
"fmt"
"log"
"gorm.io/driver/postgres"
"gorm.io/gorm"
"gorm.io/gorm/logger"
)
var DB *gorm.DB
// InitDB 初始化数据库连接
func InitDB() error {
cfg := config.GetDatabaseConfig()
dsn := cfg.GetDSN()
var err error
DB, err = gorm.Open(postgres.Open(dsn), &gorm.Config{
Logger: logger.Default.LogMode(logger.Info), // 开启SQL日志
})
if err != nil {
return fmt.Errorf("failed to connect to database: %w", err)
}
log.Println("Database connected successfully")
// 自动迁移数据库表结构
err = DB.AutoMigrate(&models.User{})
if err != nil {
return fmt.Errorf("failed to migrate database: %w", err)
}
log.Println("Database migration completed")
return nil
}
// GetDB 获取数据库实例
func GetDB() *gorm.DB {
return DB
}

View File

@ -1,49 +1,18 @@
package handlers package handlers
import ( import (
"ankao/internal/database"
"ankao/internal/models" "ankao/internal/models"
"crypto/sha256" "crypto/sha256"
"encoding/hex" "encoding/hex"
"encoding/json"
"fmt" "fmt"
"net/http" "net/http"
"os"
"time" "time"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"gorm.io/gorm"
) )
const usersFilePath = "users.json"
// loadUsers 从JSON文件加载用户数据
func loadUsers() (models.UserData, error) {
file, err := os.ReadFile(usersFilePath)
if err != nil {
return nil, fmt.Errorf("读取用户文件失败: %w", err)
}
var users models.UserData
if err := json.Unmarshal(file, &users); err != nil {
return nil, fmt.Errorf("解析用户数据失败: %w", err)
}
return users, nil
}
// saveUsers 保存用户数据到JSON文件
func saveUsers(users models.UserData) error {
data, err := json.MarshalIndent(users, "", " ")
if err != nil {
return fmt.Errorf("序列化用户数据失败: %w", err)
}
if err := os.WriteFile(usersFilePath, data, 0644); err != nil {
return fmt.Errorf("保存用户文件失败: %w", err)
}
return nil
}
// generateToken 生成简单的token实际项目应使用JWT // generateToken 生成简单的token实际项目应使用JWT
func generateToken(username string) string { func generateToken(username string) string {
data := fmt.Sprintf("%s:%d", username, time.Now().Unix()) data := fmt.Sprintf("%s:%d", username, time.Now().Unix())
@ -64,29 +33,29 @@ func Login(c *gin.Context) {
return return
} }
// 加载用户数据 // 从数据库查找用户
users, err := loadUsers() var user models.User
if err != nil { db := database.GetDB()
result := db.Where("username = ?", req.Username).First(&user)
if result.Error != nil {
if result.Error == gorm.ErrRecordNotFound {
c.JSON(http.StatusUnauthorized, gin.H{
"success": false,
"message": "用户名或密码错误",
})
return
}
c.JSON(http.StatusInternalServerError, gin.H{ c.JSON(http.StatusInternalServerError, gin.H{
"success": false, "success": false,
"message": "服务器错误", "message": "服务器错误",
"error": err.Error(), "error": result.Error.Error(),
})
return
}
// 验证用户
user, exists := users[req.Username]
if !exists {
c.JSON(http.StatusUnauthorized, gin.H{
"success": false,
"message": "用户名或密码错误",
}) })
return return
} }
// 验证密码 // 验证密码
if user.Password != req.Password { if !user.CheckPassword(req.Password) {
c.JSON(http.StatusUnauthorized, gin.H{ c.JSON(http.StatusUnauthorized, gin.H{
"success": false, "success": false,
"message": "用户名或密码错误", "message": "用户名或密码错误",
@ -99,7 +68,7 @@ func Login(c *gin.Context) {
// 返回用户信息(不包含密码) // 返回用户信息(不包含密码)
userInfo := models.UserInfoResponse{ userInfo := models.UserInfoResponse{
Username: req.Username, Username: user.Username,
Avatar: user.Avatar, Avatar: user.Avatar,
Nickname: user.Nickname, Nickname: user.Nickname,
} }
@ -127,15 +96,12 @@ func Register(c *gin.Context) {
return return
} }
// 加载现有用户数据 db := database.GetDB()
users, err := loadUsers()
if err != nil {
// 如果文件不存在,创建新的用户数据
users = make(models.UserData)
}
// 检查用户名是否已存在 // 检查用户名是否已存在
if _, exists := users[req.Username]; exists { var existingUser models.User
result := db.Where("username = ?", req.Username).First(&existingUser)
if result.Error == nil {
c.JSON(http.StatusBadRequest, gin.H{ c.JSON(http.StatusBadRequest, gin.H{
"success": false, "success": false,
"message": "用户名已存在", "message": "用户名已存在",
@ -146,7 +112,6 @@ func Register(c *gin.Context) {
// 创建新用户 // 创建新用户
newUser := models.User{ newUser := models.User{
Username: req.Username, Username: req.Username,
Password: req.Password, // 实际项目应该加密存储
Nickname: req.Nickname, Nickname: req.Nickname,
Avatar: "https://api.dicebear.com/7.x/avataaars/svg?seed=" + req.Username, // 使用用户名生成默认头像 Avatar: "https://api.dicebear.com/7.x/avataaars/svg?seed=" + req.Username, // 使用用户名生成默认头像
} }
@ -156,11 +121,18 @@ func Register(c *gin.Context) {
newUser.Nickname = req.Username newUser.Nickname = req.Username
} }
// 添加新用户 // 加密密码
users[req.Username] = newUser if err := newUser.HashPassword(req.Password); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"success": false,
"message": "密码加密失败",
"error": err.Error(),
})
return
}
// 保存用户数据 // 保存到数据库
if err := saveUsers(users); err != nil { if err := db.Create(&newUser).Error; err != nil {
c.JSON(http.StatusInternalServerError, gin.H{ c.JSON(http.StatusInternalServerError, gin.H{
"success": false, "success": false,
"message": "注册失败", "message": "注册失败",

View File

@ -1,14 +1,41 @@
package models package models
import (
"time"
"golang.org/x/crypto/bcrypt"
"gorm.io/gorm"
)
// User 用户结构 // User 用户结构
type User struct { type User struct {
Username string `json:"username"` ID uint `gorm:"primaryKey" json:"id"`
Password string `json:"password"` Username string `gorm:"uniqueIndex;not null;size:50" json:"username"`
Avatar string `json:"avatar"` Password string `gorm:"not null;size:255" json:"-"` // json:"-" 表示在JSON响应中不返回密码
Nickname string `json:"nickname"` Avatar string `gorm:"size:255" json:"avatar"`
Nickname string `gorm:"size:50" json:"nickname"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
DeletedAt gorm.DeletedAt `gorm:"index" json:"-"`
} }
// UserData 存储所有用户的结构 // HashPassword 加密密码
func (u *User) HashPassword(password string) error {
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
if err != nil {
return err
}
u.Password = string(hashedPassword)
return nil
}
// CheckPassword 验证密码
func (u *User) CheckPassword(password string) bool {
err := bcrypt.CompareHashAndPassword([]byte(u.Password), []byte(password))
return err == nil
}
// UserData 存储所有用户的结构(用于兼容旧的文件存储方式)
type UserData map[string]User type UserData map[string]User
// LoginRequest 登录请求 // LoginRequest 登录请求

View File

@ -1,13 +1,21 @@
package main package main
import ( import (
"ankao/internal/database"
"ankao/internal/handlers" "ankao/internal/handlers"
"ankao/internal/middleware" "ankao/internal/middleware"
"log"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
) )
func main() { func main() {
// 初始化数据库连接
if err := database.InitDB(); err != nil {
log.Fatal("数据库初始化失败:", err)
}
log.Println("数据库连接成功")
// 创建Gin路由器 // 创建Gin路由器
r := gin.Default() r := gin.Default()

40
pkg/config/config.go Normal file
View File

@ -0,0 +1,40 @@
package config
import (
"fmt"
)
// DatabaseConfig 数据库配置结构
type DatabaseConfig struct {
Host string
Port int
User string
Password string
DBName string
SSLMode string
}
// GetDatabaseConfig 获取数据库配置
func GetDatabaseConfig() *DatabaseConfig {
return &DatabaseConfig{
Host: "pgsql.yuchat.top",
Port: 5432,
User: "postgres",
Password: "longqi@1314",
DBName: "ankao",
SSLMode: "disable",
}
}
// GetDSN 返回数据库连接字符串
func (c *DatabaseConfig) GetDSN() string {
return fmt.Sprintf(
"host=%s user=%s password=%s dbname=%s port=%d sslmode=%s",
c.Host,
c.User,
c.Password,
c.DBName,
c.Port,
c.SSLMode,
)
}

View File

@ -1,12 +0,0 @@
{
"admin": {
"password": "123456",
"avatar": "",
"nickname": "管理员"
},
"test": {
"password": "test123",
"avatar": "",
"nickname": "测试用户"
}
}