Browse Source

Initial commit: Add project structure and .gitignore

master
dark 2 months ago
commit
8a12ad04db
  1. 31
      .claude/settings.local.json
  2. 1
      .claude/skills/zero-skills
  3. 111
      .gitignore
  4. 707
      AI-TOOLS-INTEGRATION.md
  5. 187
      backend/ai-context.md
  6. 29
      backend/api/profile.api
  7. 50
      backend/api/user.api
  8. 125
      backend/base.api
  9. 34
      backend/base.go
  10. 7
      backend/etc/base-api.yaml
  11. 58
      backend/go-zero.md
  12. 59
      backend/go.mod
  13. 146
      backend/go.sum
  14. 15
      backend/internal/config/config.go
  15. 32
      backend/internal/handler/auth/loginhandler.go
  16. 32
      backend/internal/handler/auth/refreshtokenhandler.go
  17. 32
      backend/internal/handler/auth/registerhandler.go
  18. 32
      backend/internal/handler/profile/changepasswordhandler.go
  19. 25
      backend/internal/handler/profile/getprofilehandler.go
  20. 32
      backend/internal/handler/profile/updateprofilehandler.go
  21. 110
      backend/internal/handler/routes.go
  22. 32
      backend/internal/handler/user/createuserhandler.go
  23. 32
      backend/internal/handler/user/deleteuserhandler.go
  24. 32
      backend/internal/handler/user/getuserhandler.go
  25. 32
      backend/internal/handler/user/getuserlisthandler.go
  26. 32
      backend/internal/handler/user/updateuserhandler.go
  27. 69
      backend/internal/logic/auth/loginlogic.go
  28. 68
      backend/internal/logic/auth/refreshtokenlogic.go
  29. 77
      backend/internal/logic/auth/registerlogic.go
  30. 73
      backend/internal/logic/profile/changepasswordlogic.go
  31. 76
      backend/internal/logic/profile/getprofilelogic.go
  32. 121
      backend/internal/logic/profile/updateprofilelogic.go
  33. 76
      backend/internal/logic/user/createuserlogic.go
  34. 56
      backend/internal/logic/user/deleteuserlogic.go
  35. 57
      backend/internal/logic/user/getuserlistlogic.go
  36. 51
      backend/internal/logic/user/getuserlogic.go
  37. 81
      backend/internal/logic/user/updateuserlogic.go
  38. 48
      backend/internal/middleware/authmiddleware.go
  39. 22
      backend/internal/middleware/corsmiddleware.go
  40. 22
      backend/internal/middleware/logmiddleware.go
  41. 59
      backend/internal/svc/servicecontext.go
  42. 103
      backend/internal/types/types.go
  43. 59
      backend/internal/util/jwt/jwt.go
  44. 60
      backend/mcp-zero.md
  45. 20
      backend/model/profile_entity.go
  46. 52
      backend/model/profile_model.go
  47. 22
      backend/model/user_entity.go
  48. 94
      backend/model/user_model.go
  49. 333
      backend/tests/USER_MODULE_TEST_STANDARD.md
  50. 193
      backend/tests/profile/test_profile.sh
  51. 207
      backend/tests/profile/test_profile_no_jq.sh
  52. 121
      backend/tests/run_all_tests.sh
  53. 136
      backend/tests/user/test_user.sh
  54. 122
      backend/zero-skills.md
  55. 257
      frontend/react-native/ai-context.md
  56. 149
      frontend/react-shadcn/llms.txt
  57. 234
      frontend/vue-primevue/llms.txt
  58. 43
      readme.md

31
.claude/settings.local.json

@ -0,0 +1,31 @@
{
"permissions": {
"allow": [
"Bash(powershell:*)",
"Bash(tree:*)",
"Bash(go get:*)",
"Bash(go mod init:*)",
"Bash(go mod tidy:*)",
"Bash(ls:*)",
"Bash(go run:*)",
"Bash(go list:*)",
"Bash(go mod download:*)",
"Bash(git clone:*)",
"Bash(goctl:*)",
"Bash(go install:*)",
"Bash(~/go/bin/goctl version:*)",
"Bash(~/go/bin/goctl.exe:*)",
"Bash(~/go/bin/goctl.exe -v)",
"Bash(go build:*)",
"Bash(mysql:*)",
"Bash(claude:*)",
"Bash(npm view:*)",
"Bash(./user.exe:*)",
"Bash(timeout:*)",
"Bash(tasklist:*)",
"Bash(dir:*)",
"Bash(findstr:*)",
"Bash(netstat:*)"
]
}
}

1
.claude/skills/zero-skills

@ -0,0 +1 @@
Subproject commit 789a42248d8a81cec421a2bb4a1f2db7da63d65c

111
.gitignore

@ -0,0 +1,111 @@
# Go
*.exe
*.exe~
*.dll
*.so
*.dylib
*.test
*.out
go.work
vendor/
# Node.js / Web
node_modules/
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.env
.env.local
.env.development.local
.env.test.local
.env.production.local
dist/
build/
.next/
.nuxt/
.cache/
.parcel-cache/
# React Native
android/app/build/
android/build/
ios/build/
*.ipa
*.apk
.expo/
.expo-shared/
metro.config.js.map
# General
.DS_Store
Thumbs.db
*.log
*.tmp
.vscode/
.idea/
*.swp
*.swo
*~
# Logs
logs/
*.log
# Runtime data
pids/
*.pid
*.seed
*.pid.lock
# Coverage directory used by tools like istanbul
coverage/
# Dependency directories
jspm_packages/
# Optional npm cache directory
.npm
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variables file
.env.test
# parcel-bundler cache (https://parceljs.org/)
.cache
.parcel-cache
# next.js build output
.next
# nuxt.js build output
.nuxt
# vuepress build output
.vuepress/dist
# Serverless directories
.serverless
# FuseBox cache
.fusebox/
# DynamoDB Local files
.dynamodb/
# TernJS port file
.tern-port
# Stores VSCode versions used for testing VSCode extensions
.vscode-test
# Temporary folders
tmp/
temp/

707
AI-TOOLS-INTEGRATION.md

@ -0,0 +1,707 @@
# Go-Zero AI 工具集成指南
本文档详细说明 `zero-skills`、`ai-context` 和 `mcp-zero` 三个工具如何配合使用。
---
## 📊 三件套概览
| 工具 | 用途 | 大小 | 最适合工具 | 角色 |
|------|------|------|-----------|------|
| **ai-context** | 工作流指令和决策树 | ~5KB | GitHub Copilot, Cursor, Windsurf | 工作流层 |
| **zero-skills** | 完整知识库 | ~40KB | Claude Code,深度学习 | 知识层 |
| **mcp-zero** | 运行时工具(goctl 命令) | MCP Server | Claude Desktop/Code | 执行层 |
---
## 🎯 架构协作图
```
┌─────────────────────────────────────────────────────────────┐
│ AI 助手 │
│ (Claude Code, GitHub Copilot, Cursor, 等) │
└────────────┬─────────────────────┬──────────────────────────┘
│ │
├─ 工作流层 ──────────┤
│ ai-context │ "做什么" - 快速决策
│ (~5KB) │ 每次交互都加载
│ │
├─ 知识层 ────────────┤
│ zero-skills │ "如何和为什么" - 详细模式
│ (~40KB) │ 需要时加载
│ │
└─ 执行层 ────────────┘
mcp-zero "执行" - 运行 goctl 命令
(MCP Server) 生成实际代码文件
```
---
## 📦 工具详细说明
### 1. ai-context - 工作流层
**定位**: 快速决策工具,回答"做什么"
**功能**:
- 提供简洁的工作流指导
- 帮助 AI 快速做出开发决策
- 每次交互都加载,轻量级(~5KB)
**适用场景**:
- 快速生成代码片段
- 内联代码补全
- 需要快速决策的开发流程
**安装方式**:
```bash
# GitHub Copilot
git clone https://github.com/zeromicro/ai-context.git .github/copilot-instructions.md
# Cursor
git clone https://github.com/zeromicro/ai-context.git .cursorrules
# Windsurf
git clone https://github.com/zeromicro/ai-context.git .windsurfrules
```
**使用特点**:
- ⚡ 快速加载
- 📝 简洁的决策树
- 🎯 适合日常开发
---
### 2. zero-skills - 知识层
**定位**: 深度知识库,回答"如何和为什么"
**功能**:
- 完整的 go-zero 模式指南
- 三层架构(Handler → Logic → Model)详解
- 正确和错误示例对比
- 生产级最佳实践
**适用场景**:
- 深度学习和理解 go-zero
- 复杂的微服务架构设计
- 代码审查和问题排查
- 需要 AI Code 的高级功能
**安装方式**:
```bash
# 项目级别(推荐)
git clone https://github.com/zeromicro/zero-skills.git .claude/skills/zero-skills
# 个人级别
git clone https://github.com/zeromicro/zero-skills.git ~/.claude/skills/zero-skills
```
**在 Claude Code 中的使用**:
```bash
# 自动加载
# 处理 go-zero 文件(.api, .proto, go.mod)时自动加载
# 手动调用
/zero-skills
# 带参数调用
/zero-skills 创建用户管理 API
# 检查可用性
What skills are available?
```
**使用特点**:
- 📚 完整知识库(~40KB)
- 🎓 需要时加载,按需获取
- 🔍 详细模式和示例
- 🤖 支持子代理和动态上下文
---
### 3. mcp-zero - 执行层
**定位**: 代码执行工具,负责"执行"
**功能**:
- 执行 `goctl` 命令生成代码
- 生成实际的项目文件
- MCP Server 协议集成
**适用场景**:
- 自动化代码生成
- 批量创建服务文件
- Claude Desktop/Code 环境使用
**安装方式**:
1. 安装 MCP Server:
```bash
npm install -g @zeromicro/mcp-zero
```
2. 配置 Claude Desktop:
```json
{
"mcpServers": {
"mcp-zero": {
"command": "node",
"args": ["C:\\Users\\YourUser\\AppData\\Roaming\\npm\\node_modules\\@zeromicro\\mcp-zero\\dist\\index.js"],
"env": {
"GOCTL_PATH": "/path/to/goctl"
}
}
}
}
```
**使用特点**:
- ⚙️ 执行 goctl 命令
- 📁 生成实际代码文件
- 🔌 MCP 协议集成
---
## 🚀 使用场景
### 场景 1: GitHub Copilot 用户
**工具组合**: 仅 `ai-context`
**工作流程**:
```
用户编辑代码
加载 ai-context
AI 提供快速建议
用户手动运行 goctl 命令
```
**优点**:
- 快速内联建议
- 工作流指导
- 无需额外配置
**限制**:
- 无代码执行能力
- 需手动运行 goctl 命令
**示例使用**:
```
# .github/copilot-instructions.md
<!-- ai-context 内容 -->
当编辑 go-zero 文件时,遵循以下流程:
1. 先定义 .api 文件类型和路由
2. 使用 goctl 生成代码
3. 在 internal/logic/ 实现业务逻辑
4. 使用 sqlx 进行数据库操作
```
---
### 场景 2: Claude Code 用户(最佳体验)⭐
**工具组合**: `zero-skills` + `mcp-zero`
**工作流程**:
```
用户发起请求
加载 zero-skills (知识层)
AI 调用 mcp-zero 工具 (执行层)
执行 goctl 命令生成代码
返回生成的文件和说明
```
**优点**:
- 来自模式指南的深度知识
- 通过 goctl 自动生成代码
- 实时项目数据的动态上下文
- 复杂任务的子代理工作流
**完整示例**:
用户: `/zero-skills 创建用户管理 API`
AI 处理:
1. 加载 `zero-skills``rest-api-patterns.md`
2. 了解三层架构要求
3. 调用 `mcp-zero``generate_api` 工具:
```bash
goctl api go -api user.api -dir .
```
4. 生成文件结构:
```
.
├── user.api
├── etc/
├── internal/
│ ├── handler/
│ ├── logic/
│ ├── svc/
│ └── types/
└── main.go
```
5. 解释生成的代码结构
**高级功能**:
```markdown
# 子代理使用示例
/zero-skills 分析项目架构
AI 将:
1. Fork 子代理
2. 只读分析项目文件
3. 返回架构分析报告
```
---
### 场景 3: Cursor/Windsurf 用户
**工具组合**: `ai-context` + `zero-skills` 链接
**工作流程**:
```
用户在 IDE 中编码
加载 ai-context (快速决策)
必要时链接到 zero-skills (深度知识)
AI 提供建议和指导
```
**优点**:
- IDE 原生体验
- 快速建议 + 深度知识
- 灵活使用
**配置示例**:
`.cursorrules`:
```markdown
<!-- ai-context 内容 -->
当使用 go-zero 框架时:
- 遵循三层架构: Handler → Logic → Model
- 使用 goctl 生成代码
- 使用 httpx.Error() 处理错误
<!-- 链接到 zero-skills -->
详情参考: .claude/skills/zero-skills/references/rest-api-patterns.md
```
---
## 🔗 工具间的协作示例
### 示例 1: 创建完整的 REST API
**用户请求**: "创建用户管理的 REST API"
**三件套协作**:
```
Step 1: ai-context 决策 (工作流层)
------------------------------------
决定创建流程:
1. 定义 .api 文件
2. 生成代码
3. 实现业务逻辑
Step 2: zero-skills 指导 (知识层)
------------------------------------
生成 user.api 内容参考:
- 类型定义结构
- 路由定义规范
- handler 命名约定
- 验证规则
Step 3: mcp-zero 执行 (执行层)
------------------------------------
执行命令:
goctl api go -api user.api -dir .
生成文件:
├── user.api
├── etc/user-api.yaml
├── internal/handler/*.go
├── internal/logic/*.go
├── internal/svc/servicecontext.go
├── internal/types/types.go
└── user.go
```
---
### 示例 2: 修复数据库连接问题
**用户请求**: "数据库连接失败,怎么排查"
**三件套协作**:
```
Step 1: ai-context 决策
------------------------------------
分析可能的错误原因:
- 连接字符串错误
- 数据库服务未启动
- 防火墙阻止
- 驱动问题
Step 2: zero-skills 深度分析
------------------------------------
从 database-patterns.md 获取:
- 正确的连接字符串格式
- ServiceContext 注入方法
- sqlx 配置方式
- 错误处理最佳实践
Step 3: 提供解决方案
------------------------------------
参考配置:
```go
// internal/svc/servicecontext.go
type ServiceContext struct {
Config config.Config
SqlConn sqlx.SqlConn
}
func NewServiceContext(c config.Config) *ServiceContext {
return &ServiceContext{
Config: c,
SqlConn: sqlx.NewMysql(c.Database.DataSource),
}
}
```
检查清单:
- [ ] 数据库服务运行状态
- [ ] 连接字符串格式
- [ ] 防火墙设置
- [ ] sqlx 导入正确
```
---
## 📋 快速参考
### 工具选择决策树
```
开始
├─ 使用什么 AI 工具?
│ ├─ GitHub Copilot → 使用 ai-context
│ ├─ Cursor/Windsurf → 使用 ai-context + zero-skills
│ └─ Claude Code → 使用 zero-skills + mcp-zero
└─ 任务类型?
├─ 快速代码片段 → ai-context
├─ 深度学习/参考 → zero-skills
└─ 生成项目文件 → mcp-zero
```
### 命令速查
```bash
# ai-context 安装
git clone https://github.com/zeromicro/ai-context.git .github/copilot-instructions.md
# zero-skills 安装(项目级)
git clone https://github.com/zeromicro/zero-skills.git .claude/skills/zero-skills
# zero-skills 安装(个人级)
git clone https://github.com/zeromicro/zero-skills.git ~/.claude/skills/zero-skills
# mcp-zero 安装
npm install -g @zeromicro/mcp-zero
# 手动调用 zero-skills
/zero-skills
/zero-skills 创建用户管理 API
```
---
## 💡 最佳实践
### 1. Claude Code 环境推荐配置
```
项目级 zero-skills + mcp-zero
```
**为什么**:
- 自动加载,无需手动触发
- 完整知识库 + 代码执行
- 支持高级功能(子代理、动态上下文)
### 2. 多工具环境配置
```
~/.claude/skills/zero-skills # 所有项目可用
~/.claude/skills/ai-context # 可选
项目/.github/copilot-instructions.md # Copilot 专用
项目/.cursorrules # Cursor 专用
```
### 3. 性能优化
- **轻量级需求**: 仅用 ai-context(5KB)
- **深度学习**: zero-skills(40KB,按需加载)
- **代码生成**: 配合 mcp-zero 执行层
---
## 🔗 相关资源
- **[ai-context GitHub](https://github.com/zeromicro/ai-context)** - 工作流层
- **[zero-skills GitHub](https://github.com/zeromicro/zero-skills)** - 知识层
- **[mcp-zero GitHub](https://github.com/zeromicro/mcp-zero)** - 执行层
- **[go-zero 官方文档](https://go-zero.dev)** - 框架文档
- **[Agent Skills 规范](https://agentskills.io/)** - 技能规范
---
## 📝 总结
| 层级 | 工具 | 作用 | 大小 |
|------|------|------|------|
| 工作流层 | ai-context | 快速决策"做什么" | ~5KB |
| 知识层 | zero-skills | 深度知识"如何和为什么" | ~40KB |
| 执行层 | mcp-zero | 执行代码生成 | MCP Server |
**核心思想**: 根据不同的 AI 工具和任务需求,灵活组合三件套,实现最高效的开发体验。
---
## 📦 Go-Zero + GORM 模块开发实战指南
### 概述
使用 GORM 替代默认的 sqlx 时,goctl 生成的代码需要手动修复多处问题。本文档记录了用户模块开发过程中遇到的坑点和解决方案。
---
### 一、生成基础代码
```bash
# 1. 生成 API 定义
goctl api go -api user.api -dir backend
# 2. 生成 model 代码(如果使用 sqlx)
goctl model mysql ddl -src user.sql -dir backend/model
```
**注意:** 使用 GORM 时,`goctl model` 生成的代码可能不适用,需要手动定义 Model 和 CRUD 方法。
---
### 二、修复 ServiceContext (GORM 初始化)
文件:`backend/internal/svc/servicecontext.go`
#### 常见错误 1:gorm.Open 参数不足
❌ 错误:
```go
db, err := gorm.Open(mysql.Open(dsn))
```
✅ 正确:
```go
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
```
#### 常见错误 2:DB.Close() 方法不存在
❌ 错误:
```go
func (s *ServiceContext) Close() error {
if s.DB != nil {
return s.DB.Close() // *gorm.DB 没有 Close 方法
}
return nil
}
```
✅ 正确:
```go
func (s *ServiceContext) Close() error {
if s.DB != nil {
sqlDB, err := s.DB.DB() // 获取底层 *sql.DB
if err != nil {
return err
}
return sqlDB.Close()
}
return nil
}
```
#### 完整的 NewServiceContext
```go
func NewServiceContext(c config.Config) *ServiceContext {
dsn := "user:password@tcp(host:port)/dbname?charset=utf8mb4&parseTime=true&loc=Local"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
panic("Failed to connect database: " + err.Error())
}
// 自动迁移(必须在启动时调用)
err = db.AutoMigrate(&model.User{})
if err != nil {
panic("Failed to migrate database: " + err.Error())
}
return &ServiceContext{
Config: c,
DB: db,
// ...其他字段
}
}
```
---
### 三、实现 Model 层 (CRUD 操作)
**关键点:** `goctl` 生成的 Logic 文件会引用 `model.FindOne`、`model.Insert` 等方法,但默认的 `model` 包只包含实体定义,**缺少这些 CRUD 方法**。
需要手动创建:`backend/model/<module>_model.go`
#### 标准模板
```go
package model
import (
"context"
"errors"
"gorm.io/gorm"
)
var (
ErrNotFound = errors.New("record not found")
)
// Insert 插入记录
func Insert(ctx context.Context, db *gorm.DB, record interface{}) (int64, error) {
result := db.WithContext(ctx).Create(record)
if result.Error != nil {
return 0, result.Error
}
return record.(*User).Id, nil // 根据实际结构体调整
}
// FindOne 根据ID查询
func FindOne(ctx context.Context, db *gorm.DB, id int64) (*User, error) {
var user User
result := db.WithContext(ctx).First(&user, id)
if result.Error != nil {
if errors.Is(result.Error, gorm.ErrRecordNotFound) {
return nil, ErrNotFound
}
return nil, result.Error
}
return &user, nil
}
// FindOneByXxx 根据唯一字段查询
func FindOneByEmail(ctx context.Context, db *gorm.DB, email string) (*User, error) {
var user User
result := db.WithContext(ctx).Where("email = ?", email).First(&user)
if result.Error != nil {
if errors.Is(result.Error, gorm.ErrRecordNotFound) {
return nil, ErrNotFound
}
return nil, result.Error
}
return &user, nil
}
// Update 更新记录
func Update(ctx context.Context, db *gorm.DB, user *User) error {
return db.WithContext(ctx).Save(user).Error
}
// Delete 删除记录
func Delete(ctx context.Context, db *gorm.DB, id int64) error {
return db.WithContext(ctx).Delete(&User{}, id).Error
}
// FindList 分页查询列表
func FindList(ctx context.Context, db *gorm.DB, page, pageSize int64, keyword string, status int64) ([]User, int64, error) {
var users []User
var total int64
query := db.WithContext(ctx).Model(&User{})
// 关键字搜索
if keyword != "" {
query = query.Where("username LIKE ? OR email LIKE ?",
"%"+keyword+"%", "%"+keyword+"%")
}
// 状态筛选
if status > 0 {
query = query.Where("status = ?", status)
}
// 统计总数
if err := query.Count(&total).Error; err != nil {
return nil, 0, err
}
// 分页
offset := (page - 1) * pageSize
if offset < 0 {
offset = 0
}
err := query.Offset(int(offset)).Limit(int(pageSize)).Find(&users).Error
if err != nil {
return nil, 0, err
}
return users, total, nil
}
```
---
### 四、开发清单
- [ ] 修复 `gorm.Open` 调用,添加 `&gorm.Config{}`
- [ ] 修复 `Close()` 方法,使用 `db.DB().Close()`
- [ ] 在 `NewServiceContext` 中添加 `AutoMigrate`
- [ ] 为每个模块手动创建 `<module>_model.go`
- [ ] 实现 `Insert`, `FindOne`, `Update`, `Delete`, `FindList`
- [ ] 定义 `ErrNotFound` 用于逻辑层判断
---
### 五、参考文件结构
```
backend/
├── model/
│ ├── user_entity.go # 实体定义
│ └── user_model.go # CRUD 方法 (手动添加)
├── internal/
│ └── svc/
│ └── servicecontext.go # GORM 初始化
└── internal/logic/user/
├── createuserlogic.go # 引用 model.Insert
├── getuserlogic.go # 引用 model.FindOne
└── ...
```

187
backend/ai-context.md

@ -0,0 +1,187 @@
# AI Context - Go 上下文管理
> ai-context 是 Go 语言的 AI 上下文管理库,用于管理 AI 对话的上下文和状态。
## 官方资源
- [GitHub](https://github.com/zeromicro/ai-context)
- [文档](https://github.com/zeromicro/ai-context?tab=readme-ov-file)
## 概述
ai-context 提供了一套完整的 AI 上下文管理解决方案,支持对话历史、状态管理和上下文压缩等功能。
## 核心功能
### 对话历史管理
- 存储和管理完整的对话历史
- 支持多轮对话追踪
- 消息角色识别(用户、助手、系统)
### 上下文压缩
- 智能摘要生成
- 上下文窗口优化
- 历史消息归档
### 状态管理
- 会话状态持久化
- 分布式存储支持
- 状态恢复机制
## 基本使用
```go
package main
import (
"context"
"github.com/zeromicro/ai-context"
)
func main() {
// 创建上下文管理器
manager := context.NewManager(context.Config{
Storage: "memory", // 或 "redis"
MaxSize: 100, // 最大历史消息数
})
// 创建会话
sessionID := "session-123"
ctx := manager.CreateSession(sessionID)
// 添加消息
ctx.AddMessage(context.Message{
Role: "user",
Content: "你好,今天天气怎么样?",
})
ctx.AddMessage(context.Message{
Role: "assistant",
Content: "今天天气晴朗,温度适宜。",
})
// 获取历史消息
history := ctx.GetHistory()
for _, msg := range history {
fmt.Printf("%s: %s\n", msg.Role, msg.Content)
}
// 获取用于 AI 调用的上下文
aiContext := ctx.GetContextForAI()
// 使用 aiContext 调用 AI 模型...
// 保存会话状态
manager.SaveSession(sessionID)
}
```
## 配置选项
```go
type Config struct {
Storage string // 存储类型: memory, redis, database
MaxSize int // 最大消息数量
TTL time.Duration // 会话过期时间
Compression bool // 是否启用压缩
Compressor Compressor // 压缩器
StorageConf StorageConfig // 存储配置
}
```
## Redis 存储
```go
manager, err := context.NewManager(context.Config{
Storage: "redis",
StorageConf: context.StorageConfig{
Addr: "localhost:6379",
Password: "",
DB: 0,
},
MaxSize: 100,
})
```
## 上下文压缩
```go
// 启用自动压缩
manager, _ := context.NewManager(context.Config{
MaxSize: 100,
Compression: true,
Compressor: context.NewSummarizerCompressor(),
})
// 手动压缩
ctx.Compress()
```
## AI 上下文使用
```go
// 获取格式化的上下文用于 AI 模型
ctx := manager.GetSession("session-123")
// 获取完整上下文
prompt := ctx.FormatPrompt(context.FormatOptions{
IncludeSystem: true,
MaxTokens: 2000,
})
// 或者获取消息列表
messages := ctx.GetMessages()
// messages 可以直接传递给 OpenAI API 等
```
## 中间件支持
```go
// 使用中间件扩展功能
manager.Use(middleware.SessionLogger())
manager.Use(middleware.MetricsCollector())
manager.Use(middleware.RateLimiter(100))
```
## 与 go-zero 集成
```go
// 在 handler 中使用
type MyHandler struct {
ctxManager *context.Manager
}
func (h *MyHandler) ChatHandler(w http.ResponseWriter, r *http.Request) {
var req types.ChatRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
http.Error(w, err.Error(), 400)
return
}
// 获取或创建会话
session := h.ctxManager.GetOrCreateSession(req.SessionID)
// 添加用户消息
session.AddMessage(context.Message{
Role: "user",
Content: req.Message,
})
// 调用 AI 模型
aiContext := session.GetContextForAI()
response := callAI(aiContext)
// 添加助手消息
session.AddMessage(context.Message{
Role: "assistant",
Content: response,
})
// 保存会话
h.ctxManager.SaveSession(req.SessionID)
// 返回响应
json.NewEncoder(w).Encode(types.ChatResponse{
Response: response,
})
}
```

29
backend/api/profile.api

@ -0,0 +1,29 @@
syntax = "v1"
// ========== 类型定义 ==========
type (
// 获取个人信息响应
GetProfileResponse {
Id int64 `json:"id"` // 用户ID
Username string `json:"username"` // 用户名
Email string `json:"email"` // 邮箱
Phone string `json:"phone"` // 手机号
Avatar string `json:"avatar"` // 头像URL
Bio string `json:"bio"` // 个人简介
Status int `json:"status"` // 状态 1-正常 2-禁用
CreatedAt string `json:"createdAt"` // 创建时间
UpdatedAt string `json:"updatedAt"` // 更新时间
}
// 更新个人资料请求
UpdateProfileRequest {
Username string `json:"username,optional" validate:"min=3,max=32"` // 用户名
Phone string `json:"phone,optional"` // 手机号
Avatar string `json:"avatar,optional"` // 头像URL
Bio string `json:"bio,optional"` // 个人简介
}
// 修改密码请求
ChangePasswordRequest {
OldPassword string `json:"oldPassword" validate:"required,min=6,max=32"` // 旧密码
NewPassword string `json:"newPassword" validate:"required,min=6,max=32"` // 新密码
}
)

50
backend/api/user.api

@ -0,0 +1,50 @@
syntax = "v1"
// ========== 类型定义 ==========
type (
// 创建用户请求
CreateUserRequest {
Username string `json:"username" validate:"required,min=3,max=32"` // 用户名
Email string `json:"email" validate:"required,email"` // 邮箱
Password string `json:"password" validate:"required,min=6,max=32"` // 密码
Phone string `json:"phone,optional"` // 手机号
}
// 用户信息
UserInfo {
Id int64 `json:"id"` // 用户ID
Username string `json:"username"` // 用户名
Email string `json:"email"` // 邮箱
Phone string `json:"phone"` // 手机号
Status int `json:"status"` // 状态 1-正常 2-禁用
CreatedAt string `json:"createdAt"` // 创建时间
UpdatedAt string `json:"updatedAt"` // 更新时间
}
// 更新用户请求
UpdateUserRequest {
Id int64 `path:"id" validate:"required,min=1"` // 用户ID
Username string `json:"username,optional"` // 用户名
Email string `json:"email,optional"` // 邮箱
Phone string `json:"phone,optional"` // 手机号
Status int `json:"status,optional"` // 状态
}
// 用户列表查询请求
UserListRequest {
Page int `form:"page,default=1"` // 页码
PageSize int `form:"pageSize,default=10"` // 每页数量
Keyword string `form:"keyword,optional"` // 关键词搜索
Status int `form:"status,optional"` // 状态筛选
}
// 用户列表响应
UserListResponse {
Total int64 `json:"total"` // 总数
List []UserInfo `json:"list"` // 用户列表
}
// 删除用户请求
DeleteUserRequest {
Id int64 `path:"id" validate:"required,min=1"` // 用户ID
}
// 获取用户详情请求
GetUserRequest {
Id int64 `path:"id" validate:"required,min=1"` // 用户ID
}
)

125
backend/base.api

@ -0,0 +1,125 @@
syntax = "v1"
info (
title: "Base 统一服务"
desc: "Base 项目统一 API 接口"
author: "author@example.com"
version: "v1.0"
)
import "api/user.api"
import "api/profile.api"
// ========== 通用响应类型 ==========
type (
Response {
Code int `json:"code"` // 状态码
Message string `json:"message"` // 消息
Success bool `json:"success"` // 是否成功
Data interface{} `json:"data"` // 数据
}
// 登录响应
LoginResponse {
Code int `json:"code"` // 状态码
Message string `json:"message"` // 消息
Success bool `json:"success"` // 是否成功
Token string `json:"token"` // JWT Token
}
// 注册请求
RegisterRequest {
Username string `json:"username" validate:"required,min=3,max=32"` // 用户名
Email string `json:"email" validate:"required,email"` // 邮箱
Password string `json:"password" validate:"required,min=6,max=32"` // 密码
Phone string `json:"phone,optional"` // 手机号
}
// 登录请求
LoginRequest {
Email string `json:"email" validate:"required,email"` // 邮箱
Password string `json:"password" validate:"required,min=6,max=32"` // 密码
}
// 刷新Token请求
RefreshTokenRequest {
Token string `json:"token" validate:"required"` // Token
}
)
// ========== 服务定义 ==========
@server (
prefix: /api/v1
group: auth
middleware: Cors,Log
)
service base-api {
// ========== 认证接口 ==========
// 注册
@doc "用户注册"
@handler register
post /register (RegisterRequest) returns (UserInfo)
// 登录
@doc "用户登录"
@handler login
post /login (LoginRequest) returns (LoginResponse)
// 刷新Token
@doc "刷新Token"
@handler refreshToken
post /refresh (RefreshTokenRequest) returns (LoginResponse)
}
@server (
prefix: /api/v1
group: user
middleware: Cors,Log,Auth
)
service base-api {
// ========== 用户管理接口 ==========
// 创建用户
@doc "创建用户"
@handler createUser
post /user (CreateUserRequest) returns (UserInfo)
// 获取用户列表
@doc "获取用户列表"
@handler getUserList
get /users (UserListRequest) returns (UserListResponse)
// 获取用户详情
@doc "获取用户详情"
@handler getUser
get /user/:id (GetUserRequest) returns (UserInfo)
// 更新用户
@doc "更新用户信息"
@handler updateUser
put /user/:id (UpdateUserRequest) returns (UserInfo)
// 删除用户
@doc "删除用户"
@handler deleteUser
delete /user/:id (DeleteUserRequest) returns (Response)
}
@server (
prefix: /api/v1
group: profile
middleware: Cors,Log,Auth
)
service base-api {
// ========== 个人中心接口 ==========
// 获取个人信息
@doc "获取个人信息"
@handler getProfile
get /profile/me returns (GetProfileResponse)
// 更新个人资料
@doc "更新个人资料"
@handler updateProfile
put /profile/me (UpdateProfileRequest) returns (GetProfileResponse)
// 修改密码
@doc "修改密码"
@handler changePassword
post /profile/password (ChangePasswordRequest) returns (Response)
}

34
backend/base.go

@ -0,0 +1,34 @@
// Code scaffolded by goctl. Safe to edit.
// goctl 1.9.2
package main
import (
"flag"
"fmt"
"github.com/youruser/base/internal/config"
"github.com/youruser/base/internal/handler"
"github.com/youruser/base/internal/svc"
"github.com/zeromicro/go-zero/core/conf"
"github.com/zeromicro/go-zero/rest"
)
var configFile = flag.String("f", "etc/base-api.yaml", "the config file")
func main() {
flag.Parse()
var c config.Config
conf.MustLoad(*configFile, &c)
server := rest.MustNewServer(c.RestConf)
defer server.Stop()
ctx := svc.NewServiceContext(c)
handler.RegisterHandlers(server, ctx)
fmt.Printf("Starting server at %s:%d...\n", c.Host, c.Port)
server.Start()
}

7
backend/etc/base-api.yaml

@ -0,0 +1,7 @@
Name: base-api
Host: 0.0.0.0
Port: 8888
# MySQL 数据库配置
MySQL:
DSN: root:dev123456@tcp(219.159.132.177:17173)/base?charset=utf8mb4&parseTime=true&loc=Local

58
backend/go-zero.md

@ -0,0 +1,58 @@
# go-zero 框架
> go-zero 是一个高性能的微服务框架,简化了服务的开发和部署。
## 官方文档
- [官方网站](https://go-zero.dev/)
- [GitHub](https://github.com/zeromicro/zero)
- [入门指南](https://go-zero.dev/docs/introduction)
- [快速开始](https://go-zero.dev/docs/getting-started)
- [API 开发](https://go-zero.dev/docs/task/api)
- [gRPC 开发](https://go-zero.dev/docs/task/grpc)
## 核心特性
- 简单易用:内置代码生成工具,快速生成服务代码
- 性能强大:基于 Go 语言,支持高并发
- 完整工具链:提供 API 生成、gRPC 工具、微服务治理等
- 云原生:支持 Kubernetes、Docker 等云原生部署
## 项目结构
```
.
├── api # API 服务目录
│ ├── etc # 配置文件
│ ├── internal # 业务逻辑
│ │ ├── handler # HTTP 处理器
│ │ ├── logic # 业务逻辑
│ │ ├── svc // 服务上下文
│ │ └── types // 类型定义
│ └── main.go # 入口文件
└── rpc # gRPC 服务目录
├── etc # 配置文件
├── internal # 业务逻辑
│ ├── logic // 业务逻辑
│ └── svr // 服务器
└── *.proto # Protobuf 定义
```
## 安装
```bash
# 安装 goctl 工具
go install github.com/zeromicro/zero-tools/goctl@latest
# 创建 API 服务
goctl api new api
# 创建 gRPC 服务
goctl rpc new rpc
```
## 相关 AI 支持
- ai-context: https://github.com/zeromicro/ai-context
- mcp-zero : https://github.com/zeromicro/mcp-zero
- zero-skills : https://github.com/zeromicro/zero-skills

59
backend/go.mod

@ -0,0 +1,59 @@
module github.com/youruser/base
go 1.25.0
require (
github.com/golang-jwt/jwt/v5 v5.3.1
github.com/zeromicro/go-zero v1.9.4
gorm.io/driver/mysql v1.6.0
gorm.io/gorm v1.31.1
)
require (
filippo.io/edwards25519 v1.1.0 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/fatih/color v1.18.0 // indirect
github.com/go-logr/logr v1.4.2 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-sql-driver/mysql v1.9.3 // indirect
github.com/golang-jwt/jwt/v4 v4.5.2 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/grafana/pyroscope-go v1.2.7 // indirect
github.com/grafana/pyroscope-go/godeltaprof v0.1.9 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect
github.com/klauspost/compress v1.17.11 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/openzipkin/zipkin-go v0.4.3 // indirect
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
github.com/prometheus/client_golang v1.21.1 // indirect
github.com/prometheus/client_model v0.6.1 // indirect
github.com/prometheus/common v0.62.0 // indirect
github.com/prometheus/procfs v0.15.1 // indirect
github.com/spaolacci/murmur3 v1.1.0 // indirect
go.opentelemetry.io/otel v1.24.0 // indirect
go.opentelemetry.io/otel/exporters/jaeger v1.17.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.24.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.24.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.24.0 // indirect
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.24.0 // indirect
go.opentelemetry.io/otel/exporters/zipkin v1.24.0 // indirect
go.opentelemetry.io/otel/metric v1.24.0 // indirect
go.opentelemetry.io/otel/sdk v1.24.0 // indirect
go.opentelemetry.io/otel/trace v1.24.0 // indirect
go.opentelemetry.io/proto/otlp v1.3.1 // indirect
go.uber.org/automaxprocs v1.6.0 // indirect
golang.org/x/net v0.35.0 // indirect
golang.org/x/sys v0.30.0 // indirect
golang.org/x/text v0.33.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20240711142825-46eb208f015d // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 // indirect
google.golang.org/grpc v1.65.0 // indirect
google.golang.org/protobuf v1.36.5 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
)

146
backend/go.sum

@ -0,0 +1,146 @@
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/go-sql-driver/mysql v1.9.3 h1:U/N249h2WzJ3Ukj8SowVFjdtZKfu9vlLZxjPXV1aweo=
github.com/go-sql-driver/mysql v1.9.3/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU=
github.com/golang-jwt/jwt/v4 v4.5.2 h1:YtQM7lnr8iZ+j5q71MGKkNw9Mn7AjHM68uc9g5fXeUI=
github.com/golang-jwt/jwt/v4 v4.5.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
github.com/golang-jwt/jwt/v5 v5.3.1 h1:kYf81DTWFe7t+1VvL7eS+jKFVWaUnK9cB1qbwn63YCY=
github.com/golang-jwt/jwt/v5 v5.3.1/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/grafana/pyroscope-go v1.2.7 h1:VWBBlqxjyR0Cwk2W6UrE8CdcdD80GOFNutj0Kb1T8ac=
github.com/grafana/pyroscope-go v1.2.7/go.mod h1:o/bpSLiJYYP6HQtvcoVKiE9s5RiNgjYTj1DhiddP2Pc=
github.com/grafana/pyroscope-go/godeltaprof v0.1.9 h1:c1Us8i6eSmkW+Ez05d3co8kasnuOY813tbMN8i/a3Og=
github.com/grafana/pyroscope-go/godeltaprof v0.1.9/go.mod h1:2+l7K7twW49Ct4wFluZD3tZ6e0SjanjcUUBPVD/UuGU=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 h1:bkypFPDjIYGfCYD5mRBvpqxfYX1YCS1PXdKYWi8FsN0=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0/go.mod h1:P+Lt/0by1T8bfcF3z737NnSbmxQAppXMRziHUxPOC8k=
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw=
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI=
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/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc=
github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/openzipkin/zipkin-go v0.4.3 h1:9EGwpqkgnwdEIJ+Od7QVSEIH+ocmm5nPat0G7sjsSdg=
github.com/openzipkin/zipkin-go v0.4.3/go.mod h1:M9wCJZFWCo2RiY+o1eBCEMe0Dp2S5LDHcMZmk3RmK7c=
github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM=
github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g=
github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U=
github.com/prometheus/client_golang v1.21.1 h1:DOvXXTqVzvkIewV/CDPFdejpMCGeMcbGCQ8YOmu+Ibk=
github.com/prometheus/client_golang v1.21.1/go.mod h1:U9NM32ykUErtVBxdvD3zfi+EuFkkaBvMb09mIfe0Zgg=
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io=
github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I=
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=
github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
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.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/zeromicro/go-zero v1.9.4 h1:aRLFoISqAYijABtkbliQC5SsI5TbizJpQvoHc9xup8k=
github.com/zeromicro/go-zero v1.9.4/go.mod h1:a17JOTch25SWxBcUgJZYps60hygK3pIYdw7nGwlcS38=
go.opentelemetry.io/otel v1.24.0 h1:0LAOdjNmQeSTzGBzduGe/rU4tZhMwL5rWgtp9Ku5Jfo=
go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo=
go.opentelemetry.io/otel/exporters/jaeger v1.17.0 h1:D7UpUy2Xc2wsi1Ras6V40q806WM07rqoCWzXu7Sqy+4=
go.opentelemetry.io/otel/exporters/jaeger v1.17.0/go.mod h1:nPCqOnEH9rNLKqH/+rrUjiMzHJdV1BlpKcTwRTyKkKI=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.24.0 h1:t6wl9SPayj+c7lEIFgm4ooDBZVb01IhLB4InpomhRw8=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.24.0/go.mod h1:iSDOcsnSA5INXzZtwaBPrKp/lWu/V14Dd+llD0oI2EA=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.24.0 h1:Mw5xcxMwlqoJd97vwPxA8isEaIoxsta9/Q51+TTJLGE=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.24.0/go.mod h1:CQNu9bj7o7mC6U7+CA/schKEYakYXWr79ucDHTMGhCM=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.24.0 h1:Xw8U6u2f8DK2XAkGRFV7BBLENgnTGX9i4rQRxJf+/vs=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.24.0/go.mod h1:6KW1Fm6R/s6Z3PGXwSJN2K4eT6wQB3vXX6CVnYX9NmM=
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.24.0 h1:s0PHtIkN+3xrbDOpt2M8OTG92cWqUESvzh2MxiR5xY8=
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.24.0/go.mod h1:hZlFbDbRt++MMPCCfSJfmhkGIWnX1h3XjkfxZUjLrIA=
go.opentelemetry.io/otel/exporters/zipkin v1.24.0 h1:3evrL5poBuh1KF51D9gO/S+N/1msnm4DaBqs/rpXUqY=
go.opentelemetry.io/otel/exporters/zipkin v1.24.0/go.mod h1:0EHgD8R0+8yRhUYJOGR8Hfg2dpiJQxDOszd5smVO9wM=
go.opentelemetry.io/otel/metric v1.24.0 h1:6EhoGWWK28x1fbpA4tYTOWBkPefTDQnb8WSGXlc88kI=
go.opentelemetry.io/otel/metric v1.24.0/go.mod h1:VYhLe1rFfxuTXLgj4CBiyz+9WYBA8pNGJgDcSFRKBco=
go.opentelemetry.io/otel/sdk v1.24.0 h1:YMPPDNymmQN3ZgczicBY3B6sf9n62Dlj9pWD3ucgoDw=
go.opentelemetry.io/otel/sdk v1.24.0/go.mod h1:KVrIYw6tEubO9E96HQpcmpTKDVn9gdv35HoYiQWGDFg=
go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y1YELI=
go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU=
go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0=
go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8=
go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs=
go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8=
golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE=
golang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8=
google.golang.org/genproto/googleapis/api v0.0.0-20240711142825-46eb208f015d h1:kHjw/5UfflP/L5EbledDrcG4C2597RtymmGRZvHiCuY=
google.golang.org/genproto/googleapis/api v0.0.0-20240711142825-46eb208f015d/go.mod h1:mw8MG/Qz5wfgYr6VqVCiZcHe/GJEfI+oGGDCohaVgB0=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 h1:BwIjyKYGsK9dMCBOorzRri8MQwmi7mT9rGHsCEinZkA=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY=
google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc=
google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ=
google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM=
google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/h2non/gock.v1 v1.1.2 h1:jBbHXgGBK/AoPVfJh5x4r/WxIrElvbLel8TCZkkZJoY=
gopkg.in/h2non/gock.v1 v1.1.2/go.mod h1:n7UGz/ckNChHiK05rDoiC4MYSunEC/lyaUm2WWaDva0=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
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/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gorm.io/driver/mysql v1.6.0 h1:eNbLmNTpPpTOVZi8MMxCi2aaIm0ZpInbORNXDwyLGvg=
gorm.io/driver/mysql v1.6.0/go.mod h1:D/oCC2GWK3M/dqoLxnOlaNKmXz8WNTfcS9y5ovaSqKo=
gorm.io/gorm v1.31.1 h1:7CA8FTFz/gRfgqgpeKIBcervUn3xSyPUmr6B2WXJ7kg=
gorm.io/gorm v1.31.1/go.mod h1:XyQVbO2k6YkOis7C2437jSit3SsDK72s7n7rsSHd+Gs=
k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 h1:pUdcCO1Lk/tbT5ztQWOBi5HBgbBP1J8+AsQnQCKsi8A=
k8s.io/utils v0.0.0-20240711033017-18e509b52bc8/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=

15
backend/internal/config/config.go

@ -0,0 +1,15 @@
// Code scaffolded by goctl. Safe to edit.
// goctl 1.9.2
package config
import "github.com/zeromicro/go-zero/rest"
type Config struct {
rest.RestConf
// MySQL 数据库配置
MySQL struct {
DSN string
}
}

32
backend/internal/handler/auth/loginhandler.go

@ -0,0 +1,32 @@
// Code generated by goctl. Safe to edit.
// goctl 1.9.2
package auth
import (
"net/http"
"github.com/youruser/base/internal/logic/auth"
"github.com/youruser/base/internal/svc"
"github.com/youruser/base/internal/types"
"github.com/zeromicro/go-zero/rest/httpx"
)
// 用户登录
func LoginHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.LoginRequest
if err := httpx.Parse(r, &req); err != nil {
httpx.ErrorCtx(r.Context(), w, err)
return
}
l := auth.NewLoginLogic(r.Context(), svcCtx)
resp, err := l.Login(&req)
if err != nil {
httpx.ErrorCtx(r.Context(), w, err)
} else {
httpx.OkJsonCtx(r.Context(), w, resp)
}
}
}

32
backend/internal/handler/auth/refreshtokenhandler.go

@ -0,0 +1,32 @@
// Code generated by goctl. Safe to edit.
// goctl 1.9.2
package auth
import (
"net/http"
"github.com/youruser/base/internal/logic/auth"
"github.com/youruser/base/internal/svc"
"github.com/youruser/base/internal/types"
"github.com/zeromicro/go-zero/rest/httpx"
)
// 刷新Token
func RefreshTokenHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.RefreshTokenRequest
if err := httpx.Parse(r, &req); err != nil {
httpx.ErrorCtx(r.Context(), w, err)
return
}
l := auth.NewRefreshTokenLogic(r.Context(), svcCtx)
resp, err := l.RefreshToken(&req)
if err != nil {
httpx.ErrorCtx(r.Context(), w, err)
} else {
httpx.OkJsonCtx(r.Context(), w, resp)
}
}
}

32
backend/internal/handler/auth/registerhandler.go

@ -0,0 +1,32 @@
// Code scaffolded by goctl. Safe to edit.
// goctl 1.9.2
package auth
import (
"net/http"
"github.com/youruser/base/internal/logic/auth"
"github.com/youruser/base/internal/svc"
"github.com/youruser/base/internal/types"
"github.com/zeromicro/go-zero/rest/httpx"
)
// 用户注册
func RegisterHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.RegisterRequest
if err := httpx.Parse(r, &req); err != nil {
httpx.ErrorCtx(r.Context(), w, err)
return
}
l := auth.NewRegisterLogic(r.Context(), svcCtx)
resp, err := l.Register(&req)
if err != nil {
httpx.ErrorCtx(r.Context(), w, err)
} else {
httpx.OkJsonCtx(r.Context(), w, resp)
}
}
}

32
backend/internal/handler/profile/changepasswordhandler.go

@ -0,0 +1,32 @@
// Code scaffolded by goctl. Safe to edit.
// goctl 1.9.2
package profile
import (
"net/http"
"github.com/youruser/base/internal/logic/profile"
"github.com/youruser/base/internal/svc"
"github.com/youruser/base/internal/types"
"github.com/zeromicro/go-zero/rest/httpx"
)
// 修改密码
func ChangePasswordHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.ChangePasswordRequest
if err := httpx.Parse(r, &req); err != nil {
httpx.ErrorCtx(r.Context(), w, err)
return
}
l := profile.NewChangePasswordLogic(r.Context(), svcCtx)
resp, err := l.ChangePassword(&req)
if err != nil {
httpx.ErrorCtx(r.Context(), w, err)
} else {
httpx.OkJsonCtx(r.Context(), w, resp)
}
}
}

25
backend/internal/handler/profile/getprofilehandler.go

@ -0,0 +1,25 @@
// Code scaffolded by goctl. Safe to edit.
// goctl 1.9.2
package profile
import (
"net/http"
"github.com/youruser/base/internal/logic/profile"
"github.com/youruser/base/internal/svc"
"github.com/zeromicro/go-zero/rest/httpx"
)
// 获取个人信息
func GetProfileHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
l := profile.NewGetProfileLogic(r.Context(), svcCtx)
resp, err := l.GetProfile()
if err != nil {
httpx.ErrorCtx(r.Context(), w, err)
} else {
httpx.OkJsonCtx(r.Context(), w, resp)
}
}
}

32
backend/internal/handler/profile/updateprofilehandler.go

@ -0,0 +1,32 @@
// Code scaffolded by goctl. Safe to edit.
// goctl 1.9.2
package profile
import (
"net/http"
"github.com/youruser/base/internal/logic/profile"
"github.com/youruser/base/internal/svc"
"github.com/youruser/base/internal/types"
"github.com/zeromicro/go-zero/rest/httpx"
)
// 更新个人资料
func UpdateProfileHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.UpdateProfileRequest
if err := httpx.Parse(r, &req); err != nil {
httpx.ErrorCtx(r.Context(), w, err)
return
}
l := profile.NewUpdateProfileLogic(r.Context(), svcCtx)
resp, err := l.UpdateProfile(&req)
if err != nil {
httpx.ErrorCtx(r.Context(), w, err)
} else {
httpx.OkJsonCtx(r.Context(), w, resp)
}
}
}

110
backend/internal/handler/routes.go

@ -0,0 +1,110 @@
// Code generated by goctl. DO NOT EDIT.
// goctl 1.9.2
package handler
import (
"net/http"
auth "github.com/youruser/base/internal/handler/auth"
profile "github.com/youruser/base/internal/handler/profile"
user "github.com/youruser/base/internal/handler/user"
"github.com/youruser/base/internal/svc"
"github.com/zeromicro/go-zero/rest"
)
func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) {
server.AddRoutes(
rest.WithMiddlewares(
[]rest.Middleware{serverCtx.Cors, serverCtx.Log},
[]rest.Route{
{
// 用户登录
Method: http.MethodPost,
Path: "/login",
Handler: auth.LoginHandler(serverCtx),
},
{
// 刷新Token
Method: http.MethodPost,
Path: "/refresh",
Handler: auth.RefreshTokenHandler(serverCtx),
},
{
// 用户注册
Method: http.MethodPost,
Path: "/register",
Handler: auth.RegisterHandler(serverCtx),
},
}...,
),
rest.WithPrefix("/api/v1"),
)
server.AddRoutes(
rest.WithMiddlewares(
[]rest.Middleware{serverCtx.Cors, serverCtx.Log, serverCtx.Auth},
[]rest.Route{
{
// 获取个人信息
Method: http.MethodGet,
Path: "/profile/me",
Handler: profile.GetProfileHandler(serverCtx),
},
{
// 更新个人资料
Method: http.MethodPut,
Path: "/profile/me",
Handler: profile.UpdateProfileHandler(serverCtx),
},
{
// 修改密码
Method: http.MethodPost,
Path: "/profile/password",
Handler: profile.ChangePasswordHandler(serverCtx),
},
}...,
),
rest.WithPrefix("/api/v1"),
)
server.AddRoutes(
rest.WithMiddlewares(
[]rest.Middleware{serverCtx.Cors, serverCtx.Log, serverCtx.Auth},
[]rest.Route{
{
// 创建用户
Method: http.MethodPost,
Path: "/user",
Handler: user.CreateUserHandler(serverCtx),
},
{
// 获取用户详情
Method: http.MethodGet,
Path: "/user/:id",
Handler: user.GetUserHandler(serverCtx),
},
{
// 更新用户信息
Method: http.MethodPut,
Path: "/user/:id",
Handler: user.UpdateUserHandler(serverCtx),
},
{
// 删除用户
Method: http.MethodDelete,
Path: "/user/:id",
Handler: user.DeleteUserHandler(serverCtx),
},
{
// 获取用户列表
Method: http.MethodGet,
Path: "/users",
Handler: user.GetUserListHandler(serverCtx),
},
}...,
),
rest.WithPrefix("/api/v1"),
)
}

32
backend/internal/handler/user/createuserhandler.go

@ -0,0 +1,32 @@
// Code scaffolded by goctl. Safe to edit.
// goctl 1.9.2
package user
import (
"net/http"
"github.com/youruser/base/internal/logic/user"
"github.com/youruser/base/internal/svc"
"github.com/youruser/base/internal/types"
"github.com/zeromicro/go-zero/rest/httpx"
)
// 创建用户
func CreateUserHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.CreateUserRequest
if err := httpx.Parse(r, &req); err != nil {
httpx.ErrorCtx(r.Context(), w, err)
return
}
l := user.NewCreateUserLogic(r.Context(), svcCtx)
resp, err := l.CreateUser(&req)
if err != nil {
httpx.ErrorCtx(r.Context(), w, err)
} else {
httpx.OkJsonCtx(r.Context(), w, resp)
}
}
}

32
backend/internal/handler/user/deleteuserhandler.go

@ -0,0 +1,32 @@
// Code scaffolded by goctl. Safe to edit.
// goctl 1.9.2
package user
import (
"net/http"
"github.com/youruser/base/internal/logic/user"
"github.com/youruser/base/internal/svc"
"github.com/youruser/base/internal/types"
"github.com/zeromicro/go-zero/rest/httpx"
)
// 删除用户
func DeleteUserHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.DeleteUserRequest
if err := httpx.Parse(r, &req); err != nil {
httpx.ErrorCtx(r.Context(), w, err)
return
}
l := user.NewDeleteUserLogic(r.Context(), svcCtx)
resp, err := l.DeleteUser(&req)
if err != nil {
httpx.ErrorCtx(r.Context(), w, err)
} else {
httpx.OkJsonCtx(r.Context(), w, resp)
}
}
}

32
backend/internal/handler/user/getuserhandler.go

@ -0,0 +1,32 @@
// Code scaffolded by goctl. Safe to edit.
// goctl 1.9.2
package user
import (
"net/http"
"github.com/youruser/base/internal/logic/user"
"github.com/youruser/base/internal/svc"
"github.com/youruser/base/internal/types"
"github.com/zeromicro/go-zero/rest/httpx"
)
// 获取用户详情
func GetUserHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.GetUserRequest
if err := httpx.Parse(r, &req); err != nil {
httpx.ErrorCtx(r.Context(), w, err)
return
}
l := user.NewGetUserLogic(r.Context(), svcCtx)
resp, err := l.GetUser(&req)
if err != nil {
httpx.ErrorCtx(r.Context(), w, err)
} else {
httpx.OkJsonCtx(r.Context(), w, resp)
}
}
}

32
backend/internal/handler/user/getuserlisthandler.go

@ -0,0 +1,32 @@
// Code scaffolded by goctl. Safe to edit.
// goctl 1.9.2
package user
import (
"net/http"
"github.com/youruser/base/internal/logic/user"
"github.com/youruser/base/internal/svc"
"github.com/youruser/base/internal/types"
"github.com/zeromicro/go-zero/rest/httpx"
)
// 获取用户列表
func GetUserListHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.UserListRequest
if err := httpx.Parse(r, &req); err != nil {
httpx.ErrorCtx(r.Context(), w, err)
return
}
l := user.NewGetUserListLogic(r.Context(), svcCtx)
resp, err := l.GetUserList(&req)
if err != nil {
httpx.ErrorCtx(r.Context(), w, err)
} else {
httpx.OkJsonCtx(r.Context(), w, resp)
}
}
}

32
backend/internal/handler/user/updateuserhandler.go

@ -0,0 +1,32 @@
// Code scaffolded by goctl. Safe to edit.
// goctl 1.9.2
package user
import (
"net/http"
"github.com/youruser/base/internal/logic/user"
"github.com/youruser/base/internal/svc"
"github.com/youruser/base/internal/types"
"github.com/zeromicro/go-zero/rest/httpx"
)
// 更新用户信息
func UpdateUserHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.UpdateUserRequest
if err := httpx.Parse(r, &req); err != nil {
httpx.ErrorCtx(r.Context(), w, err)
return
}
l := user.NewUpdateUserLogic(r.Context(), svcCtx)
resp, err := l.UpdateUser(&req)
if err != nil {
httpx.ErrorCtx(r.Context(), w, err)
} else {
httpx.OkJsonCtx(r.Context(), w, resp)
}
}
}

69
backend/internal/logic/auth/loginlogic.go

@ -0,0 +1,69 @@
package auth
import (
"context"
"crypto/md5"
"fmt"
"github.com/youruser/base/internal/svc"
"github.com/youruser/base/internal/types"
"github.com/youruser/base/internal/util/jwt"
"github.com/youruser/base/model"
"github.com/zeromicro/go-zero/core/logx"
)
type LoginLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
// 用户登录
func NewLoginLogic(ctx context.Context, svcCtx *svc.ServiceContext) *LoginLogic {
return &LoginLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *LoginLogic) Login(req *types.LoginRequest) (resp *types.LoginResponse, err error) {
// 查询用户
user, err := model.FindOneByEmail(l.ctx, l.svcCtx.DB, req.Email)
if err != nil {
if err == model.ErrNotFound {
return &types.LoginResponse{
Code: 404,
Message: "用户不存在或密码错误",
Success: false,
}, nil
}
return nil, fmt.Errorf("查询用户失败: %v", err)
}
// 加密输入的密码并与数据库密码对比
inputPassword := fmt.Sprintf("%x", md5.Sum([]byte(req.Password)))
if user.Password != inputPassword {
return &types.LoginResponse{
Code: 400,
Message: "用户不存在或密码错误",
Success: false,
}, nil
}
// 生成 Token
token, err := jwt.GenerateToken(user.Id, user.Username, user.Email)
if err != nil {
return nil, fmt.Errorf("生成Token失败: %v", err)
}
l.Infof("登录成功,userId=%d", user.Id)
return &types.LoginResponse{
Code: 200,
Message: "登录成功",
Success: true,
Token: token,
}, nil
}

68
backend/internal/logic/auth/refreshtokenlogic.go

@ -0,0 +1,68 @@
package auth
import (
"context"
"fmt"
"github.com/youruser/base/internal/svc"
"github.com/youruser/base/internal/types"
"github.com/youruser/base/internal/util/jwt"
"github.com/youruser/base/model"
"github.com/zeromicro/go-zero/core/logx"
)
type RefreshTokenLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
// 刷新 Token
func NewRefreshTokenLogic(ctx context.Context, svcCtx *svc.ServiceContext) *RefreshTokenLogic {
return &RefreshTokenLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *RefreshTokenLogic) RefreshToken(req *types.RefreshTokenRequest) (resp *types.LoginResponse, err error) {
// 解析并验证 Token
claims, err := jwt.ParseToken(req.Token)
if err != nil {
return &types.LoginResponse{
Code: 401,
Message: "Token无效或已过期",
Success: false,
}, nil
}
// 查询用户是否存在
user, err := model.FindOne(l.ctx, l.svcCtx.DB, claims.UserID)
if err != nil {
if err == model.ErrNotFound {
return &types.LoginResponse{
Code: 404,
Message: "用户不存在",
Success: false,
}, nil
}
return nil, fmt.Errorf("查询用户失败: %v", err)
}
// 生成新 Token
newToken, err := jwt.GenerateToken(user.Id, user.Username, user.Email)
if err != nil {
return nil, fmt.Errorf("生成Token失败: %v", err)
}
l.Infof("刷新Token成功,userId=%d", user.Id)
return &types.LoginResponse{
Code: 200,
Message: "刷新Token成功",
Success: true,
Token: newToken,
}, nil
}

77
backend/internal/logic/auth/registerlogic.go

@ -0,0 +1,77 @@
package auth
import (
"context"
"crypto/md5"
"fmt"
"github.com/youruser/base/internal/svc"
"github.com/youruser/base/internal/types"
"github.com/youruser/base/model"
"github.com/zeromicro/go-zero/core/logx"
)
type RegisterLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
// 用户注册
func NewRegisterLogic(ctx context.Context, svcCtx *svc.ServiceContext) *RegisterLogic {
return &RegisterLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *RegisterLogic) Register(req *types.RegisterRequest) (resp *types.UserInfo, err error) {
// 检查邮箱是否已存在
_, err = model.FindOneByEmail(l.ctx, l.svcCtx.DB, req.Email)
if err == nil {
return nil, fmt.Errorf("邮箱已被注册")
}
if err != model.ErrNotFound {
return nil, fmt.Errorf("检查邮箱失败: %v", err)
}
// 创建用户模型
user := &model.User{
Username: req.Username,
Email: req.Email,
Password: fmt.Sprintf("%x", md5.Sum([]byte(req.Password))), // 密码加密
Phone: req.Phone,
Status: 1, // 默认正常状态
}
// 插入数据库
id, err := model.Insert(l.ctx, l.svcCtx.DB, user)
if err != nil {
return nil, fmt.Errorf("创建用户失败: %v", err)
}
// 查询创建的用户
user, err = model.FindOne(l.ctx, l.svcCtx.DB, id)
if err != nil {
return nil, fmt.Errorf("查询用户失败: %v", err)
}
// 返回用户信息(不返回密码)
resp = &types.UserInfo{
Id: user.Id,
Username: user.Username,
Email: user.Email,
Phone: user.Phone,
Status: int(user.Status),
CreatedAt: user.CreatedAt.Format("2006-01-02 15:04:05"),
UpdatedAt: user.UpdatedAt.Format("2006-01-02 15:04:05"),
}
// 返回 Token 在响应头中(通过中间件处理)
// 临时方案:将 token 放入响应 Data 中
l.Infof("注册成功,userId=%d", user.Id)
return resp, nil
}

73
backend/internal/logic/profile/changepasswordlogic.go

@ -0,0 +1,73 @@
package profile
import (
"context"
"crypto/md5"
"fmt"
"github.com/youruser/base/internal/svc"
"github.com/youruser/base/internal/types"
"github.com/youruser/base/model"
"github.com/zeromicro/go-zero/core/logx"
)
type ChangePasswordLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
// 修改密码
func NewChangePasswordLogic(ctx context.Context, svcCtx *svc.ServiceContext) *ChangePasswordLogic {
return &ChangePasswordLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *ChangePasswordLogic) ChangePassword(req *types.ChangePasswordRequest) (resp *types.Response, err error) {
// 从上下文中获取当前用户ID
userIdValue := l.ctx.Value("userId")
if userIdValue == nil {
return nil, fmt.Errorf("未获取到用户信息,请先登录")
}
userId, ok := userIdValue.(int64)
if !ok {
return nil, fmt.Errorf("用户ID格式错误")
}
// 查询用户
user, err := model.FindOne(l.ctx, l.svcCtx.DB, userId)
if err != nil {
if err == model.ErrNotFound {
return nil, fmt.Errorf("用户不存在")
}
return nil, fmt.Errorf("查询用户失败: %v", err)
}
// 加密旧密码
oldEncrypted := fmt.Sprintf("%x", md5.Sum([]byte(req.OldPassword)))
if user.Password != oldEncrypted {
return &types.Response{
Code: 400,
Message: "旧密码错误",
}, nil
}
// 加密新密码
newEncrypted := fmt.Sprintf("%x", md5.Sum([]byte(req.NewPassword)))
user.Password = newEncrypted
// 更新数据库
err = model.Update(l.ctx, l.svcCtx.DB, user)
if err != nil {
return nil, fmt.Errorf("修改密码失败: %v", err)
}
return &types.Response{
Code: 200,
Message: "修改密码成功",
}, nil
}

76
backend/internal/logic/profile/getprofilelogic.go

@ -0,0 +1,76 @@
package profile
import (
"context"
"fmt"
"github.com/youruser/base/internal/svc"
"github.com/youruser/base/internal/types"
"github.com/youruser/base/model"
"github.com/zeromicro/go-zero/core/logx"
)
type GetProfileLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
// 获取个人信息
func NewGetProfileLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetProfileLogic {
return &GetProfileLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *GetProfileLogic) GetProfile() (resp *types.GetProfileResponse, err error) {
// 从上下文中获取当前用户ID(假设通过 Auth 中间件设置)
userIdValue := l.ctx.Value("userId")
if userIdValue == nil {
return nil, fmt.Errorf("未获取到用户信息,请先登录")
}
userId, ok := userIdValue.(int64)
if !ok {
return nil, fmt.Errorf("用户ID格式错误")
}
// 查询用户信息
user, err := model.FindOne(l.ctx, l.svcCtx.DB, userId)
if err != nil {
if err == model.ErrNotFound {
return nil, fmt.Errorf("用户不存在")
}
return nil, fmt.Errorf("查询用户失败: %v", err)
}
// 查询个人资料
profile, err := model.FindProfileByUserId(l.ctx, l.svcCtx.DB, userId)
if err != nil && err != model.ErrNotFound {
return nil, fmt.Errorf("查询个人资料失败: %v", err)
}
// 如果没有个人资料,使用默认值
avatar := ""
bio := ""
if profile != nil {
avatar = profile.Avatar
bio = profile.Bio
}
resp = &types.GetProfileResponse{
Id: user.Id,
Username: user.Username,
Email: user.Email,
Phone: user.Phone,
Avatar: avatar,
Bio: bio,
Status: int(user.Status),
CreatedAt: user.CreatedAt.Format("2006-01-02 15:04:05"),
UpdatedAt: user.UpdatedAt.Format("2006-01-02 15:04:05"),
}
return resp, nil
}

121
backend/internal/logic/profile/updateprofilelogic.go

@ -0,0 +1,121 @@
package profile
import (
"context"
"fmt"
"github.com/youruser/base/internal/svc"
"github.com/youruser/base/internal/types"
"github.com/youruser/base/model"
"github.com/zeromicro/go-zero/core/logx"
)
type UpdateProfileLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
// 更新个人资料
func NewUpdateProfileLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UpdateProfileLogic {
return &UpdateProfileLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *UpdateProfileLogic) UpdateProfile(req *types.UpdateProfileRequest) (resp *types.GetProfileResponse, err error) {
// 从上下文中获取当前用户ID
userIdValue := l.ctx.Value("userId")
if userIdValue == nil {
return nil, fmt.Errorf("未获取到用户信息,请先登录")
}
userId, ok := userIdValue.(int64)
if !ok {
return nil, fmt.Errorf("用户ID格式错误")
}
// 查询用户
user, err := model.FindOne(l.ctx, l.svcCtx.DB, userId)
if err != nil {
if err == model.ErrNotFound {
return nil, fmt.Errorf("用户不存在")
}
return nil, fmt.Errorf("查询用户失败: %v", err)
}
// 更新用户基本信息
if req.Username != "" {
user.Username = req.Username
}
if req.Phone != "" {
user.Phone = req.Phone
}
// 更新用户表
err = model.Update(l.ctx, l.svcCtx.DB, user)
if err != nil {
return nil, fmt.Errorf("更新用户信息失败: %v", err)
}
// 查询或创建个人资料
profile, err := model.FindProfileByUserId(l.ctx, l.svcCtx.DB, userId)
if err == model.ErrNotFound {
// 创建新的个人资料
profile = &model.Profile{
UserId: userId,
Avatar: req.Avatar,
Bio: req.Bio,
}
_, err = model.InsertProfile(l.ctx, l.svcCtx.DB, profile)
if err != nil {
return nil, fmt.Errorf("创建个人资料失败: %v", err)
}
} else if err != nil {
return nil, fmt.Errorf("查询个人资料失败: %v", err)
} else {
// 更新现有个人资料
if req.Avatar != "" {
profile.Avatar = req.Avatar
}
if req.Bio != "" {
profile.Bio = req.Bio
}
err = model.UpdateProfile(l.ctx, l.svcCtx.DB, profile)
if err != nil {
return nil, fmt.Errorf("更新个人资料失败: %v", err)
}
}
// 重新查询用户信息
user, err = model.FindOne(l.ctx, l.svcCtx.DB, userId)
if err != nil {
return nil, fmt.Errorf("查询用户失败: %v", err)
}
// 查询个人资料
profile, _ = model.FindProfileByUserId(l.ctx, l.svcCtx.DB, userId)
avatar := ""
bio := ""
if profile != nil {
avatar = profile.Avatar
bio = profile.Bio
}
resp = &types.GetProfileResponse{
Id: user.Id,
Username: user.Username,
Email: user.Email,
Phone: user.Phone,
Avatar: avatar,
Bio: bio,
Status: int(user.Status),
CreatedAt: user.CreatedAt.Format("2006-01-02 15:04:05"),
UpdatedAt: user.UpdatedAt.Format("2006-01-02 15:04:05"),
}
return resp, nil
}

76
backend/internal/logic/user/createuserlogic.go

@ -0,0 +1,76 @@
package user
import (
"context"
"crypto/md5"
"fmt"
"github.com/youruser/base/internal/svc"
"github.com/youruser/base/internal/types"
"github.com/youruser/base/model"
"github.com/zeromicro/go-zero/core/logx"
)
type CreateUserLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
// 创建用户
func NewCreateUserLogic(ctx context.Context, svcCtx *svc.ServiceContext) *CreateUserLogic {
return &CreateUserLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *CreateUserLogic) CreateUser(req *types.CreateUserRequest) (resp *types.UserInfo, err error) {
// 检查邮箱是否已存在
_, err = model.FindOneByEmail(l.ctx, l.svcCtx.DB, req.Email)
if err == nil {
return nil, fmt.Errorf("邮箱已被注册")
}
if err != model.ErrNotFound {
return nil, fmt.Errorf("检查邮箱失败: %v", err)
}
// 密码加密(简单MD5,实际项目应使用bcrypt等更安全的加密)
encryptedPassword := fmt.Sprintf("%x", md5.Sum([]byte(req.Password)))
// 创建用户模型
user := &model.User{
Username: req.Username,
Email: req.Email,
Password: encryptedPassword,
Phone: req.Phone,
Status: 1, // 默认正常状态
}
// 插入数据库
id, err := model.Insert(l.ctx, l.svcCtx.DB, user)
if err != nil {
return nil, fmt.Errorf("创建用户失败: %v", err)
}
// 查询创建的用户
user, err = model.FindOne(l.ctx, l.svcCtx.DB, id)
if err != nil {
return nil, fmt.Errorf("查询用户失败: %v", err)
}
// 返回用户信息(不返回密码)
resp = &types.UserInfo{
Id: user.Id,
Username: user.Username,
Email: user.Email,
Phone: user.Phone,
Status: int(user.Status),
CreatedAt: user.CreatedAt.Format("2006-01-02 15:04:05"),
UpdatedAt: user.UpdatedAt.Format("2006-01-02 15:04:05"),
}
return resp, nil
}

56
backend/internal/logic/user/deleteuserlogic.go

@ -0,0 +1,56 @@
package user
import (
"context"
"fmt"
"github.com/youruser/base/internal/svc"
"github.com/youruser/base/internal/types"
"github.com/youruser/base/model"
"github.com/zeromicro/go-zero/core/logx"
)
type DeleteUserLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
// 删除用户
func NewDeleteUserLogic(ctx context.Context, svcCtx *svc.ServiceContext) *DeleteUserLogic {
return &DeleteUserLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *DeleteUserLogic) DeleteUser(req *types.DeleteUserRequest) (resp *types.Response, err error) {
// 检查用户是否存在
user, err := model.FindOne(l.ctx, l.svcCtx.DB, req.Id)
if err != nil {
if err == model.ErrNotFound {
return &types.Response{
Code: 404,
Message: "用户不存在",
Success: false,
}, nil
}
return nil, fmt.Errorf("查询用户失败: %v", err)
}
// 删除用户
err = model.Delete(l.ctx, l.svcCtx.DB, req.Id)
if err != nil {
return nil, fmt.Errorf("删除用户失败: %v", err)
}
l.Infof("删除用户成功: id=%d, username=%s", user.Id, user.Username)
return &types.Response{
Code: 200,
Message: "删除成功",
Success: true,
}, nil
}

57
backend/internal/logic/user/getuserlistlogic.go

@ -0,0 +1,57 @@
package user
import (
"context"
"fmt"
"github.com/youruser/base/internal/svc"
"github.com/youruser/base/internal/types"
"github.com/youruser/base/model"
"github.com/zeromicro/go-zero/core/logx"
)
type GetUserListLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
// 获取用户列表
func NewGetUserListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetUserListLogic {
return &GetUserListLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *GetUserListLogic) GetUserList(req *types.UserListRequest) (resp *types.UserListResponse, err error) {
// 查询数据库
users, total, err := model.FindList(l.ctx, l.svcCtx.DB,
int64(req.Page), int64(req.PageSize), req.Keyword, int64(req.Status))
if err != nil {
return nil, fmt.Errorf("查询用户列表失败: %v", err)
}
// 转换为返回类型
list := make([]types.UserInfo, 0, len(users))
for _, user := range users {
list = append(list, types.UserInfo{
Id: user.Id,
Username: user.Username,
Email: user.Email,
Phone: user.Phone,
Status: int(user.Status),
CreatedAt: user.CreatedAt.Format("2006-01-02 15:04:05"),
UpdatedAt: user.UpdatedAt.Format("2006-01-02 15:04:05"),
})
}
resp = &types.UserListResponse{
Total: total,
List: list,
}
return resp, nil
}

51
backend/internal/logic/user/getuserlogic.go

@ -0,0 +1,51 @@
package user
import (
"context"
"fmt"
"github.com/youruser/base/internal/svc"
"github.com/youruser/base/internal/types"
"github.com/youruser/base/model"
"github.com/zeromicro/go-zero/core/logx"
)
type GetUserLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
// 获取用户详情
func NewGetUserLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetUserLogic {
return &GetUserLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *GetUserLogic) GetUser(req *types.GetUserRequest) (resp *types.UserInfo, err error) {
// 根据ID查询用户
user, err := model.FindOne(l.ctx, l.svcCtx.DB, req.Id)
if err != nil {
if err == model.ErrNotFound {
return nil, fmt.Errorf("用户不存在")
}
return nil, fmt.Errorf("查询用户失败: %v", err)
}
// 返回用户信息
resp = &types.UserInfo{
Id: user.Id,
Username: user.Username,
Email: user.Email,
Phone: user.Phone,
Status: int(user.Status),
CreatedAt: user.CreatedAt.Format("2006-01-02 15:04:05"),
UpdatedAt: user.UpdatedAt.Format("2006-01-02 15:04:05"),
}
return resp, nil
}

81
backend/internal/logic/user/updateuserlogic.go

@ -0,0 +1,81 @@
package user
import (
"context"
"fmt"
"github.com/youruser/base/internal/svc"
"github.com/youruser/base/internal/types"
"github.com/youruser/base/model"
"github.com/zeromicro/go-zero/core/logx"
)
type UpdateUserLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
// 更新用户信息
func NewUpdateUserLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UpdateUserLogic {
return &UpdateUserLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *UpdateUserLogic) UpdateUser(req *types.UpdateUserRequest) (resp *types.UserInfo, err error) {
// 检查用户是否存在
user, err := model.FindOne(l.ctx, l.svcCtx.DB, req.Id)
if err != nil {
if err == model.ErrNotFound {
return nil, fmt.Errorf("用户不存在")
}
return nil, fmt.Errorf("查询用户失败: %v", err)
}
// 更新字段
if req.Username != "" {
user.Username = req.Username
}
if req.Email != "" {
// 检查邮箱是否被其他用户使用
existing, err := model.FindOneByEmail(l.ctx, l.svcCtx.DB, req.Email)
if err == nil && existing.Id != req.Id {
return nil, fmt.Errorf("邮箱已被使用")
}
user.Email = req.Email
}
if req.Phone != "" {
user.Phone = req.Phone
}
if req.Status > 0 {
user.Status = int64(req.Status)
}
// 更新数据库
err = model.Update(l.ctx, l.svcCtx.DB, user)
if err != nil {
return nil, fmt.Errorf("更新用户失败: %v", err)
}
// 重新查询获取更新后的数据
user, err = model.FindOne(l.ctx, l.svcCtx.DB, req.Id)
if err != nil {
return nil, fmt.Errorf("查询用户失败: %v", err)
}
resp = &types.UserInfo{
Id: user.Id,
Username: user.Username,
Email: user.Email,
Phone: user.Phone,
Status: int(user.Status),
CreatedAt: user.CreatedAt.Format("2006-01-02 15:04:05"),
UpdatedAt: user.UpdatedAt.Format("2006-01-02 15:04:05"),
}
return resp, nil
}

48
backend/internal/middleware/authmiddleware.go

@ -0,0 +1,48 @@
package middleware
import (
"context"
"net/http"
"strings"
"github.com/youruser/base/internal/util/jwt"
)
type AuthMiddleware struct{}
func NewAuthMiddleware() *AuthMiddleware {
return &AuthMiddleware{}
}
func (m *AuthMiddleware) Handle(next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
// 从 Header 中获取 Token
authHeader := r.Header.Get("Authorization")
if authHeader == "" {
http.Error(w, "Unauthorized", http.StatusUnauthorized)
return
}
// Token 格式: "Bearer <token>"
tokenString := strings.TrimPrefix(authHeader, "Bearer ")
if tokenString == "" {
http.Error(w, "Invalid token format", http.StatusUnauthorized)
return
}
// 解析并验证 Token
claims, err := jwt.ParseToken(tokenString)
if err != nil {
http.Error(w, "Invalid token: "+err.Error(), http.StatusUnauthorized)
return
}
// 将 userId 存入上下文,供后续 logic 使用
ctx := context.WithValue(r.Context(), "userId", claims.UserID)
ctx = context.WithValue(ctx, "username", claims.Username)
ctx = context.WithValue(ctx, "email", claims.Email)
// 传递给下一个处理器
next(w, r.WithContext(ctx))
}
}

22
backend/internal/middleware/corsmiddleware.go

@ -0,0 +1,22 @@
// Code scaffolded by goctl. Safe to edit.
// goctl 1.9.2
package middleware
import "net/http"
type CorsMiddleware struct {
}
func NewCorsMiddleware() *CorsMiddleware {
return &CorsMiddleware{}
}
func (m *CorsMiddleware) Handle(next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
// TODO generate middleware implement function, delete after code implementation
// Passthrough to next handler if need
next(w, r)
}
}

22
backend/internal/middleware/logmiddleware.go

@ -0,0 +1,22 @@
// Code scaffolded by goctl. Safe to edit.
// goctl 1.9.2
package middleware
import "net/http"
type LogMiddleware struct {
}
func NewLogMiddleware() *LogMiddleware {
return &LogMiddleware{}
}
func (m *LogMiddleware) Handle(next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
// TODO generate middleware implement function, delete after code implementation
// Passthrough to next handler if need
next(w, r)
}
}

59
backend/internal/svc/servicecontext.go

@ -0,0 +1,59 @@
// Code scaffolded by goctl. Safe to edit.
// goctl 1.9.2
package svc
import (
"github.com/youruser/base/internal/config"
"github.com/youruser/base/internal/middleware"
"github.com/youruser/base/model"
"gorm.io/driver/mysql"
"gorm.io/gorm"
"github.com/zeromicro/go-zero/rest"
)
type ServiceContext struct {
Config config.Config
Cors rest.Middleware
Log rest.Middleware
Auth rest.Middleware
// 数据库连接
DB *gorm.DB
}
func NewServiceContext(c config.Config) *ServiceContext {
// 创建数据库连接
dsn := c.MySQL.DSN
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
panic("Failed to connect database: " + err.Error())
}
// 自动迁移表
err = db.AutoMigrate(&model.User{}, &model.Profile{})
if err != nil {
panic("Failed to migrate database: " + err.Error())
}
return &ServiceContext{
Config: c,
Cors: middleware.NewCorsMiddleware().Handle,
Log: middleware.NewLogMiddleware().Handle,
Auth: middleware.NewAuthMiddleware().Handle,
DB: db,
}
}
// Close 关闭资源
func (s *ServiceContext) Close() error {
if s.DB != nil {
sqlDB, err := s.DB.DB()
if err != nil {
return err
}
return sqlDB.Close()
}
return nil
}

103
backend/internal/types/types.go

@ -0,0 +1,103 @@
// Code generated by goctl. DO NOT EDIT.
// goctl 1.9.2
package types
type ChangePasswordRequest struct {
OldPassword string `json:"oldPassword" validate:"required,min=6,max=32"` // 旧密码
NewPassword string `json:"newPassword" validate:"required,min=6,max=32"` // 新密码
}
type CreateUserRequest struct {
Username string `json:"username" validate:"required,min=3,max=32"` // 用户名
Email string `json:"email" validate:"required,email"` // 邮箱
Password string `json:"password" validate:"required,min=6,max=32"` // 密码
Phone string `json:"phone,optional"` // 手机号
}
type DeleteUserRequest struct {
Id int64 `path:"id" validate:"required,min=1"` // 用户ID
}
type GetProfileResponse struct {
Id int64 `json:"id"` // 用户ID
Username string `json:"username"` // 用户名
Email string `json:"email"` // 邮箱
Phone string `json:"phone"` // 手机号
Avatar string `json:"avatar"` // 头像URL
Bio string `json:"bio"` // 个人简介
Status int `json:"status"` // 状态 1-正常 2-禁用
CreatedAt string `json:"createdAt"` // 创建时间
UpdatedAt string `json:"updatedAt"` // 更新时间
}
type GetUserRequest struct {
Id int64 `path:"id" validate:"required,min=1"` // 用户ID
}
type LoginRequest struct {
Email string `json:"email" validate:"required,email"` // 邮箱
Password string `json:"password" validate:"required,min=6,max=32"` // 密码
}
type LoginResponse struct {
Code int `json:"code"` // 状态码
Message string `json:"message"` // 消息
Success bool `json:"success"` // 是否成功
Token string `json:"token"` // JWT Token
}
type RefreshTokenRequest struct {
Token string `json:"token" validate:"required"` // Token
}
type RegisterRequest struct {
Username string `json:"username" validate:"required,min=3,max=32"` // 用户名
Email string `json:"email" validate:"required,email"` // 邮箱
Password string `json:"password" validate:"required,min=6,max=32"` // 密码
Phone string `json:"phone,optional"` // 手机号
}
type Response struct {
Code int `json:"code"` // 状态码
Message string `json:"message"` // 消息
Success bool `json:"success"` // 是否成功
Data interface{} `json:"data"` // 数据
}
type UpdateProfileRequest struct {
Username string `json:"username,optional" validate:"min=3,max=32"` // 用户名
Phone string `json:"phone,optional"` // 手机号
Avatar string `json:"avatar,optional"` // 头像URL
Bio string `json:"bio,optional"` // 个人简介
}
type UpdateUserRequest struct {
Id int64 `path:"id" validate:"required,min=1"` // 用户ID
Username string `json:"username,optional"` // 用户名
Email string `json:"email,optional"` // 邮箱
Phone string `json:"phone,optional"` // 手机号
Status int `json:"status,optional"` // 状态
}
type UserInfo struct {
Id int64 `json:"id"` // 用户ID
Username string `json:"username"` // 用户名
Email string `json:"email"` // 邮箱
Phone string `json:"phone"` // 手机号
Status int `json:"status"` // 状态 1-正常 2-禁用
CreatedAt string `json:"createdAt"` // 创建时间
UpdatedAt string `json:"updatedAt"` // 更新时间
}
type UserListRequest struct {
Page int `form:"page,default=1"` // 页码
PageSize int `form:"pageSize,default=10"` // 每页数量
Keyword string `form:"keyword,optional"` // 关键词搜索
Status int `form:"status,optional"` // 状态筛选
}
type UserListResponse struct {
Total int64 `json:"total"` // 总数
List []UserInfo `json:"list"` // 用户列表
}

59
backend/internal/util/jwt/jwt.go

@ -0,0 +1,59 @@
package jwt
import (
"errors"
"time"
"github.com/golang-jwt/jwt/v5"
)
var (
// Token 有效期(7天)
TokenExpireTime = time.Hour * 24 * 7
// 签名
SigningKey = []byte("base-jwt-secret-key-2024")
)
type Claims struct {
UserID int64 `json:"userId"`
Username string `json:"username"`
Email string `json:"email"`
jwt.RegisteredClaims
}
// GenerateToken 生成 JWT Token
func GenerateToken(userId int64, username, email string) (string, error) {
claims := Claims{
UserID: userId,
Username: username,
Email: email,
RegisteredClaims: jwt.RegisteredClaims{
ExpiresAt: jwt.NewNumericDate(time.Now().Add(TokenExpireTime)),
Issuer: "base-api",
},
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
return token.SignedString(SigningKey)
}
// ParseToken 解析并验证 JWT Token
func ParseToken(tokenString string) (*Claims, error) {
token, err := jwt.ParseWithClaims(tokenString, &Claims{}, func(token *jwt.Token) (interface{}, error) {
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, errors.New("invalid signing method")
}
return SigningKey, nil
})
if err != nil {
return nil, err
}
if claims, ok := token.Claims.(*Claims); ok && token.Valid {
return claims, nil
}
return nil, errors.New("invalid token")
}

60
backend/mcp-zero.md

@ -0,0 +1,60 @@
# MCP Zero - Model Context Protocol for Go
> mcp-zero 是 go-zero 的 Model Context Protocol (MCP) 实现,用于 AI 上下文管理和集成。
## 官方资源
- [GitHub](https://github.com/zeromicro/mcp-zero)
- [MCP 规范](https://modelcontextprotocol.io/)
## 什么是 MCP
Model Context Protocol (MCP) 是一种开放协议,用于在 AI 模型和外部工具/数据源之间建立标准化的通信方式。
## 功能特性
- 标准化的上下文传输协议
- 支持多种数据源的连接
- 可扩展的工具调用机制
- 类型安全的 Go 实现
## 安装
```bash
go get github.com/zeromicro/mcp-zero
```
## 基本使用
```go
import "github.com/zeromicro/mcp-zero"
// 初始化 MCP 客户端
client := mcp.NewClient(mcp.Config{
Host: "localhost",
Port: 8080,
})
// 注册工具
client.RegisterTool("my_tool", func(ctx context.Context, args mcp.ToolArgs) (interface{}, error) {
// 工具逻辑
return nil, nil
})
```
## 配置示例
```yaml
mcp:
enabled: true
server:
host: "0.0.0.0"
port: 8080
tools:
- name: "database_query"
description: "Query database"
enabled: true
- name: "api_call"
description: "Make API calls"
enabled: true
```

20
backend/model/profile_entity.go

@ -0,0 +1,20 @@
package model
import (
"time"
)
// Profile 用户个人资料模型
type Profile struct {
Id int64 `gorm:"column:id;primaryKey;autoIncrement" json:"id"`
UserId int64 `gorm:"column:user_id;not null;uniqueIndex" json:"userId"` // 关联用户ID
Avatar string `gorm:"column:avatar;type:varchar(255);default:''" json:"avatar"` // 头像URL
Bio string `gorm:"column:bio;type:text" json:"bio"` // 个人简介
CreatedAt time.Time `gorm:"column:created_at;autoCreateTime" json:"createdAt"`
UpdatedAt time.Time `gorm:"column:updated_at;autoUpdateTime" json:"updatedAt"`
}
// TableName 指定表名
func (Profile) TableName() string {
return "profile"
}

52
backend/model/profile_model.go

@ -0,0 +1,52 @@
package model
import (
"context"
"errors"
"gorm.io/gorm"
)
// InsertProfile 插入个人资料
func InsertProfile(ctx context.Context, db *gorm.DB, profile *Profile) (int64, error) {
result := db.WithContext(ctx).Create(profile)
if result.Error != nil {
return 0, result.Error
}
return profile.Id, nil
}
// FindOneProfile 根据ID查询个人资料
func FindOneProfile(ctx context.Context, db *gorm.DB, id int64) (*Profile, error) {
var profile Profile
result := db.WithContext(ctx).First(&profile, id)
if result.Error != nil {
if errors.Is(result.Error, gorm.ErrRecordNotFound) {
return nil, ErrNotFound
}
return nil, result.Error
}
return &profile, nil
}
// FindProfileByUserId 根据用户ID查询个人资料
func FindProfileByUserId(ctx context.Context, db *gorm.DB, userId int64) (*Profile, error) {
var profile Profile
result := db.WithContext(ctx).Where("user_id = ?", userId).First(&profile)
if result.Error != nil {
if errors.Is(result.Error, gorm.ErrRecordNotFound) {
return nil, ErrNotFound
}
return nil, result.Error
}
return &profile, nil
}
// UpdateProfile 更新个人资料
func UpdateProfile(ctx context.Context, db *gorm.DB, profile *Profile) error {
return db.WithContext(ctx).Save(profile).Error
}
// DeleteProfile 删除个人资料
func DeleteProfile(ctx context.Context, db *gorm.DB, id int64) error {
return db.WithContext(ctx).Delete(&Profile{}, id).Error
}

22
backend/model/user_entity.go

@ -0,0 +1,22 @@
package model
import (
"time"
)
// User 用户模型
type User struct {
Id int64 `gorm:"column:id;primaryKey;autoIncrement" json:"id"`
Username string `gorm:"column:username;type:varchar(32);not null" json:"username"`
Email string `gorm:"column:email;type:varchar(128);not null" json:"email"`
Password string `gorm:"column:password;type:varbinary(64);not null" json:"-"`
Phone string `gorm:"column:phone;type:varchar(20);default:''" json:"phone"`
Status int64 `gorm:"column:status;type:tinyint;default:1" json:"status"`
CreatedAt time.Time `gorm:"column:created_at;autoCreateTime" json:"createdAt"`
UpdatedAt time.Time `gorm:"column:updated_at;autoUpdateTime" json:"updatedAt"`
}
// TableName 指定表名
func (User) TableName() string {
return "user"
}

94
backend/model/user_model.go

@ -0,0 +1,94 @@
package model
import (
"context"
"errors"
"gorm.io/gorm"
)
var (
ErrNotFound = errors.New("record not found")
)
// Insert 插入用户
func Insert(ctx context.Context, db *gorm.DB, user *User) (int64, error) {
result := db.WithContext(ctx).Create(user)
if result.Error != nil {
return 0, result.Error
}
return user.Id, nil
}
// FindOne 根据ID查询用户
func FindOne(ctx context.Context, db *gorm.DB, id int64) (*User, error) {
var user User
result := db.WithContext(ctx).First(&user, id)
if result.Error != nil {
if errors.Is(result.Error, gorm.ErrRecordNotFound) {
return nil, ErrNotFound
}
return nil, result.Error
}
return &user, nil
}
// FindOneByEmail 根据邮箱查询用户
func FindOneByEmail(ctx context.Context, db *gorm.DB, email string) (*User, error) {
var user User
result := db.WithContext(ctx).Where("email = ?", email).First(&user)
if result.Error != nil {
if errors.Is(result.Error, gorm.ErrRecordNotFound) {
return nil, ErrNotFound
}
return nil, result.Error
}
return &user, nil
}
// Update 更新用户
func Update(ctx context.Context, db *gorm.DB, user *User) error {
result := db.WithContext(ctx).Save(user)
return result.Error
}
// Delete 删除用户
func Delete(ctx context.Context, db *gorm.DB, id int64) error {
result := db.WithContext(ctx).Delete(&User{}, id)
return result.Error
}
// FindList 查询用户列表
func FindList(ctx context.Context, db *gorm.DB, page, pageSize int64, keyword string, status int64) ([]User, int64, error) {
var users []User
var total int64
query := db.WithContext(ctx).Model(&User{})
// 关键字搜索
if keyword != "" {
query = query.Where("username LIKE ? OR email LIKE ? OR phone LIKE ?",
"%"+keyword+"%", "%"+keyword+"%", "%"+keyword+"%")
}
// 状态筛选
if status > 0 {
query = query.Where("status = ?", status)
}
// 统计总数
if err := query.Count(&total).Error; err != nil {
return nil, 0, err
}
// 分页查询
offset := (page - 1) * pageSize
if offset < 0 {
offset = 0
}
err := query.Offset(int(offset)).Limit(int(pageSize)).Find(&users).Error
if err != nil {
return nil, 0, err
}
return users, total, nil
}

333
backend/tests/USER_MODULE_TEST_STANDARD.md

@ -0,0 +1,333 @@
# User 模块测试标准流程
## 基础测试流程
每个模块应遵循以下基础测试流程:
```
新增(创建) -> 查询(单条) -> 更新 -> 查询(验证更新) -> 列表查询 -> 删除 -> 查询(验证删除)
```
## 测试步骤详解
### 1. 新增(创建)
创建新记录并返回 ID
**验证点:**
- 记录成功插入数据库
- 返回正确的 ID
- 必填字段验证通过
- 重复数据检查(如邮箱唯一)
**示例请求:**
```bash
curl -X POST http://localhost:8888/api/v1/user \
-H "Content-Type: application/json" \
-d '{
"username": "testuser01",
"email": "test01@example.com",
"password": "password123",
"phone": "13800138000"
}'
```
---
### 2. 查询(单条)
使用步骤 1 返回的 ID 查询记录
**验证点:**
- 返回完整记录信息
- 字段值正确
- 状态码为 200
**示例请求:**
```bash
curl -X GET http://localhost:8888/api/v1/user/1
```
---
### 3. 更新
使用 ID 更新记录的部分字段
**验证点:**
- 字段值正确更新
- 返回更新后的完整记录
- 其他字段保持不变
- 数据验证(如邮箱唯一性)
**示例请求:**
```bash
curl -X PUT http://localhost:8888/api/v1/user/1 \
-H "Content-Type: application/json" \
-d '{
"username": "testuser01_updated",
"phone": "13900139000"
}'
```
---
### 4. 查询(验证更新)
再次使用 ID 查询记录,验证更新结果
**验证点:**
- 更新的字段值正确
- 未更新的字段值保持不变
**示例请求:**
```bash
curl -X GET http://localhost:8888/api/v1/user/1
```
---
### 5. 列表查询
查询记录列表,支持分页和筛选
**验证点:**
- 返回列表数据
- 分页参数生效
- 筛选条件生效
- 总数统计正确
**示例请求:**
```bash
# 查询所有
curl -X GET "http://localhost:8888/api/v1/users?page=1&pageSize=10"
# 带关键词搜索
curl -X GET "http://localhost:8888/api/v1/users?keyword=test&status=1"
```
---
### 6. 删除
使用 ID 删除记录
**验证点:**
- 删除成功
- 返回正确的响应格式
**示例请求:**
```bash
curl -X DELETE http://localhost:8888/api/v1/user/1
```
---
### 7. 查询(验证删除)
再次使用 ID 查询记录,验证已删除
**验证点:**
- 返回 404 或相应错误信息
- 记录确实已不存在
**示例请求:**
```bash
curl -X GET http://localhost:8888/api/v1/user/1
```
---
## 测试脚本模板
### Bash 脚本模板
```bash
#!/bin/bash
# 配置
BASE_URL="http://localhost:8888/api/v1"
MODULE="user"
# 日志函数
log_info() {
echo -e "\033[32m[INFO]\033[0m $1"
}
log_success() {
echo -e "\033[32m[SUCCESS]\033[0m $1"
}
log_error() {
echo -e "\033[31m[ERROR]\033[0m $1"
}
# 1. 新增
log_info "步骤 1: 新增 $MODULE"
CREATE_RESULT=$(curl -s -X POST ${BASE_URL}/${MODULE} \
-H "Content-Type: application/json" \
-d '{
"username": "test_'$(date +%s)'",
"email": "test_'$(date +%s)'@example.com",
"password": "password123",
"phone": "13800138000"
}')
echo $CREATE_RESULT | jq '.'
ID=$(echo $CREATE_RESULT | jq -r '.id')
# 2. 查询
log_info "步骤 2: 查询 $MODULE (ID: $ID)"
QUERY_RESULT=$(curl -s -X GET ${BASE_URL}/${MODULE}/${ID})
echo $QUERY_RESULT | jq '.'
# 3. 更新
log_info "步骤 3: 更新 $MODULE"
UPDATE_RESULT=$(curl -s -X PUT ${BASE_URL}/${MODULE}/${ID} \
-H "Content-Type: application/json" \
-d '{
"phone": "13900139000"
}')
echo $UPDATE_RESULT | jq '.'
# 4. 验证更新
log_info "步骤 4: 验证更新"
VERIFY_RESULT=$(curl -s -X GET ${BASE_URL}/${MODULE}/${ID})
echo $VERIFY_RESULT | jq '.'
# 5. 列表查询
log_info "步骤 5: 列表查询"
LIST_RESULT=$(curl -s -X GET "${BASE_URL}/${MODULE}s?page=1&pageSize=10")
echo $LIST_RESULT | jq '.'
# 6. 删除
log_info "步骤 6: 删除 $MODULE"
DELETE_RESULT=$(curl -s -X DELETE ${BASE_URL}/${MODULE}/${ID})
echo $DELETE_RESULT | jq '.'
# 7. 验证删除
log_info "步骤 7: 验证删除"
VERIFY_DELETE_RESULT=$(curl -s -X GET ${BASE_URL}/${MODULE}/${ID})
echo $VERIFY_DELETE_RESULT | jq '.'
log_success "测试完成"
```
---
## 测试报告格式
每个模块测试完成后,应输出测试报告:
```
=== [模块名] 测试报告 ===
步骤 结果 备注
------------------------
新增 [通过/失败]
查询(新增后) [通过/失败]
更新 [通过/失败]
查询(更新后) [通过/失败]
列表查询 [通过/失败]
删除 [通过/失败]
查询(删除后) [通过/失败]
总体结果: [全部通过/部分失败/全部失败]
```
---
## Profile 模块测试流程
```
获取 -> 更新 -> 查询(验证) -> 修改密码
```
### 步骤详解
### 1. 获取个人信息
获取当前登录用户的个人信息
**验证点:**
- 返回完整用户信息
- 头像和简介字段正确
**示例请求:**
```bash
curl -X GET http://localhost:8888/api/v1/profile/me
```
---
### 2. 更新个人资料
更新用户名、手机号、头像、简介等字段
**验证点:**
- 字段值正确更新
- 返回更新后的完整信息
- 未更新字段值保持不变
**示例请求:**
```bash
curl -X PUT http://localhost:8888/api/v1/profile/me \
-H "Content-Type: application/json" \
-d '{
"username": "updated_user",
"phone": "13900139000",
"avatar": "http://example.com/avatar.jpg",
"bio": "个人简介"
}'
```
---
### 3. 查询个人信息 (验证更新)
再次获取个人信息,验证更新结果
**验证点:**
- 更新的字段值正确
- 未更新的字段值保持不变
**示例请求:**
```bash
curl -X GET http://localhost:8888/api/v1/profile/me
```
---
### 4. 修改密码
修改用户密码
**验证点:**
- 返回正确的成功响应
- 错误密码返回正确错误信息
**示例请求:**
```bash
curl -X POST http://localhost:8888/api/v1/profile/password \
-H "Content-Type: application/json" \
-d '{
"oldPassword": "password123",
"newPassword": "newpassword123"
}'
```
**注意:** 修改密码后需要重新登录,完整验证流程需要实现登录接口并获取新 token。
---
## 测试注意事项
1. **环境准备**
- 确保服务已启动
- 数据库已连接
- 端口可访问
2. **数据隔离**
- 每次测试使用不同的测试数据
- 避免与其他测试产生冲突
- 建议使用时间戳或随机数生成唯一数据
3. **错误处理**
- 验证错误提示信息正确
- 验证 HTTP 状态码
- 验证响应格式
4. **边界条件**
- 测试必填字段缺失
- 测试数据格式错误(如邮箱格式)
- 测试字段长度限制
- 测试重复数据(唯一性验证)

193
backend/tests/profile/test_profile.sh

@ -0,0 +1,193 @@
#!/bin/bash
# Profile 模块测试脚本(含完整认证流程)- 不依赖 jq
# 测试流程: 注册 -> 登录 -> 获取个人信息 -> 更新 -> 查询(验证) -> 修改密码 -> 使用新token验证
# 配置
BASE_URL="http://localhost:8888/api/v1"
MODULE="profile"
TIMESTAMP=$(date +%s)
# 日志函数
log_info() {
echo -e "\033[32m[INFO]\033[0m $1"
}
log_success() {
echo -e "\033[32m[SUCCESS]\033[0m $1"
}
log_error() {
echo -e "\033[31m[ERROR]\033[0m $1"
}
log_step() {
echo -e "\n\033[36m--- 步骤 $1: $2 ---\033[0m"
}
# 存储登录后获取的 Token
AUTH_TOKEN=""
# 1. 注册新用户
log_step "1" "注册用户"
REGISTER_RESULT=$(curl -s -X POST ${BASE_URL}/register \
-H "Content-Type: application/json" \
-d "{
\"username\": \"testuser${TIMESTAMP}\",
\"email\": \"test${TIMESTAMP}@example.com\",
\"password\": \"password123\",
\"phone\": \"13800138000\"
}")
# 解析注册响应,获取用户ID
REGISTER_ID=$(echo "$REGISTER_RESULT" | grep -o '"id":[^,}]*' | sed 's/"id"://' | tr -d ' ')
if [ -z "$REGISTER_ID" ]; then
log_error "注册失败"
echo "$REGISTER_RESULT"
exit 1
fi
log_success "注册成功,ID: $REGISTER_ID"
echo "$REGISTER_RESULT"
# 2. 登录
log_step "2" "用户登录"
LOGIN_RESULT=$(curl -s -X POST ${BASE_URL}/login \
-H "Content-Type: application/json" \
-d "{
\"email\": \"test${TIMESTAMP}@example.com\",
\"password\": \"password123\"
}")
# 解析并存储 Token
AUTH_TOKEN=$(echo "$LOGIN_RESULT" | grep -o '"token":"[^"]*"' | sed 's/"token":"//' | sed 's/"$//')
LOGIN_CODE=$(echo "$LOGIN_RESULT" | grep -o '"code":[^,}]*' | sed 's/"code"://' | tr -d ' ')
if [ "$LOGIN_CODE" != "200" ] || [ -z "$AUTH_TOKEN" ]; then
log_error "登录失败"
echo "$LOGIN_RESULT"
exit 1
fi
log_success "登录成功,Token: ${AUTH_TOKEN:0:20}..."
echo "$LOGIN_RESULT"
# 3. 获取个人信息(带Token)
log_step "3" "获取个人信息(带Token)"
GET_RESULT=$(curl -s -X GET ${BASE_URL}/${MODULE}/me \
-H "Content-Type: application/json" \
-H "Authorization: Bearer ${AUTH_TOKEN}")
# 验证响应
ID=$(echo "$GET_RESULT" | grep -o '"id":[^,}]*' | sed 's/"id"://' | tr -d ' ')
if [ -z "$ID" ]; then
log_error "获取失败:可能Token无效"
echo "$GET_RESULT"
exit 1
fi
log_success "获取成功,ID: $ID"
echo "$GET_RESULT"
# 4. 更新个人资料
log_step "4" "更新个人资料"
UPDATE_RESULT=$(curl -s -X PUT ${BASE_URL}/${MODULE}/me \
-H "Content-Type: application/json" \
-H "Authorization: Bearer ${AUTH_TOKEN}" \
-d "{
\"username\": \"testuser${TIMESTAMP}_updated\",
\"phone\": \"13900139000\",
\"avatar\": \"http://example.com/avatar${TIMESTAMP}.jpg\",
\"bio\": \"这是个人简介${TIMESTAMP}\"
}")
# 验证更新
VERIFY_USERNAME=$(echo "$UPDATE_RESULT" | grep -o '"username":"[^"]*"' | sed 's/"username":"//' | sed 's/"$//')
EXPECTED_USERNAME="testuser${TIMESTAMP}_updated"
if [ "$VERIFY_USERNAME" != "$EXPECTED_USERNAME" ]; then
log_error "更新失败:用户名未更新"
echo "$UPDATE_RESULT"
exit 1
fi
log_success "更新成功"
echo "$UPDATE_RESULT"
# 5. 查询个人信息(验证更新)
log_step "5" "查询个人信息(验证更新)"
VERIFY_RESULT=$(curl -s -X GET ${BASE_URL}/${MODULE}/me \
-H "Content-Type: application/json" \
-H "Authorization: Bearer ${AUTH_TOKEN}")
VERIFY_USERNAME=$(echo "$VERIFY_RESULT" | grep -o '"username":"[^"]*"' | sed 's/"username":"//' | sed 's/"$//')
if [ "$VERIFY_USERNAME" != "$EXPECTED_USERNAME" ]; then
log_error "验证失败:用户名未更新"
echo "$VERIFY_RESULT"
exit 1
fi
log_success "验证成功"
# 6. 修改密码(使用旧密码)
log_step "6" "修改密码(使用旧密码)"
OLD_PASSWORD_RESULT=$(curl -s -X POST ${BASE_URL}/${MODULE}/password \
-H "Content-Type: application/json" \
-H "Authorization: Bearer ${AUTH_TOKEN}" \
-d "{
\"oldPassword\": \"password123\",
\"newPassword\": \"newpassword123\"
}")
# 验证修改
OLD_PASSWORD_CODE=$(echo "$OLD_PASSWORD_RESULT" | grep -o '"code":[^,}]*' | sed 's/"code"://' | tr -d ' ')
if [ "$OLD_PASSWORD_CODE" != "200" ]; then
log_error "修改密码失败"
echo "$OLD_PASSWORD_RESULT"
exit 1
fi
log_success "修改密码成功"
echo "$OLD_PASSWORD_RESULT"
# 7. 使用新密码重新登录
log_step "7" "使用新密码重新登录"
NEW_LOGIN_RESULT=$(curl -s -X POST ${BASE_URL}/login \
-H "Content-Type: application/json" \
-d "{
\"email\": \"test${TIMESTAMP}@example.com\",
\"password\": \"newpassword123\"
}")
# 获取新Token
NEW_AUTH_TOKEN=$(echo "$NEW_LOGIN_RESULT" | grep -o '"token":"[^"]*"' | sed 's/"token":"//' | sed 's/"$//')
NEW_LOGIN_CODE=$(echo "$NEW_LOGIN_RESULT" | grep -o '"code":[^,}]*' | sed 's/"code"://' | tr -d ' ')
if [ "$NEW_LOGIN_CODE" != "200" ] || [ -z "$NEW_AUTH_TOKEN" ]; then
log_error "重新登录失败"
echo "$NEW_LOGIN_RESULT"
exit 1
fi
log_success "重新登录成功,新Token: ${NEW_AUTH_TOKEN:0:20}..."
echo "$NEW_LOGIN_RESULT"
# 8. 获取个人信息(验证密码修改成功)
log_step "8" "获取个人信息(验证密码修改成功)"
FINAL_GET_RESULT=$(curl -s -X GET ${BASE_URL}/${MODULE}/me \
-H "Content-Type: application/json" \
-H "Authorization: Bearer ${NEW_AUTH_TOKEN}")
# 验证响应
ID=$(echo "$FINAL_GET_RESULT" | grep -o '"id":[^,}]*' | sed 's/"id"://' | tr -d ' ')
if [ -z "$ID" ]; then
log_error "获取失败:新Token可能无效"
echo "$FINAL_GET_RESULT"
exit 1
fi
log_success "验证成功,密码修改完成"
echo -e "\n\033[32m=== Profile 模块测试完成 ===\033[0m"
echo "用户ID: $REGISTER_ID"
echo "测试账号: test${TIMESTAMP}@example.com"
echo "测试密码: password123 / newpassword123"

207
backend/tests/profile/test_profile_no_jq.sh

@ -0,0 +1,207 @@
#!/bin/bash
# Profile 模块测试脚本(含完整认证流程)- 不依赖 jq
# 测试流程: 注册 -> 登录 -> 获取个人信息 -> 更新 -> 查询(验证) -> 修改密码 -> 使用新token验证
# 日志函数
log_info() {
echo -e "\033[32m[INFO]\033[0m $1"
}
log_success() {
echo -e "\033[32m[SUCCESS]\033[0m $1"
}
log_error() {
echo -e "\033[31m[ERROR]\033[0m $1"
}
log_step() {
echo -e "\n\033[36m--- 步骤 $1: $2 ---\033[0m"
}
# 简单 JSON 提取函数
extract_value() {
local json="$1"
local key="$2"
# 匹配 "key":value 或 "key": "value"
echo "$json" | grep -o "\"$key\":[^,}]*" | sed 's/"'"$key'"://' | sed 's/^"//' | sed 's/"$//' | tr -d ' '
}
extract_int() {
local json="$1"
local key="$2"
echo "$json" | grep -o "\"$key\":[^,}]*" | sed 's/"'"$key'"://' | tr -d ' '
}
# 配置
BASE_URL="http://localhost:8888/api/v1"
MODULE="profile"
TIMESTAMP=$(date +%s)
# 存储登录后获取的 Token
AUTH_TOKEN=""
# 1. 注册新用户
log_step "1" "注册用户"
REGISTER_RESULT=$(curl -s -X POST ${BASE_URL}/register \
-H "Content-Type: application/json" \
-d "{
\"username\": \"testuser${TIMESTAMP}\",
\"email\": \"test${TIMESTAMP}@example.com\",
\"password\": \"password123\",
\"phone\": \"13800138000\"
}")
# 解析注册响应,获取用户ID
REGISTER_ID=$(extract_int "$REGISTER_RESULT" "id")
if [ -z "$REGISTER_ID" ]; then
log_error "注册失败"
echo "$REGISTER_RESULT"
exit 1
fi
log_success "注册成功,ID: $REGISTER_ID"
echo "$REGISTER_RESULT"
# 2. 登录
log_step "2" "用户登录"
LOGIN_RESULT=$(curl -s -X POST ${BASE_URL}/login \
-H "Content-Type: application/json" \
-d "{
\"email\": \"test${TIMESTAMP}@example.com\",
\"password\": \"password123\"
}")
# 解析并存储 Token
AUTH_TOKEN=$(extract_value "$LOGIN_RESULT" "token")
LOGIN_CODE=$(extract_int "$LOGIN_RESULT" "code")
if [ "$LOGIN_CODE" != "200" ] || [ -z "$AUTH_TOKEN" ]; then
log_error "登录失败"
echo "$LOGIN_RESULT"
exit 1
fi
log_success "登录成功,Token: ${AUTH_TOKEN:0:20}..."
echo "$LOGIN_RESULT"
# 3. 获取个人信息(带Token)
log_step "3" "获取个人信息(带Token)"
GET_RESULT=$(curl -s -X GET ${BASE_URL}/${MODULE}/me \
-H "Content-Type: application/json" \
-H "Authorization: Bearer ${AUTH_TOKEN}")
# 验证响应
ID=$(extract_int "$GET_RESULT" "id")
if [ -z "$ID" ]; then
log_error "获取失败:可能Token无效"
echo "$GET_RESULT"
exit 1
fi
log_success "获取成功,ID: $ID"
echo "$GET_RESULT"
# 4. 更新个人资料
log_step "4" "更新个人资料"
UPDATE_RESULT=$(curl -s -X PUT ${BASE_URL}/${MODULE}/me \
-H "Content-Type: application/json" \
-H "Authorization: Bearer ${AUTH_TOKEN}" \
-d "{
\"username\": \"testuser${TIMESTAMP}_updated\",
\"phone\": \"13900139000\",
\"avatar\": \"http://example.com/avatar${TIMESTAMP}.jpg\",
\"bio\": \"这是个人简介${TIMESTAMP}\"
}")
# 验证更新
VERIFY_USERNAME=$(extract_value "$UPDATE_RESULT" "username")
EXPECTED_USERNAME="testuser${TIMESTAMP}_updated"
if [ "$VERIFY_USERNAME" != "$EXPECTED_USERNAME" ]; then
log_error "更新失败:用户名未更新"
echo "$UPDATE_RESULT"
exit 1
fi
log_success "更新成功"
echo "$UPDATE_RESULT"
# 5. 查询个人信息(验证更新)
log_step "5" "查询个人信息(验证更新)"
VERIFY_RESULT=$(curl -s -X GET ${BASE_URL}/${MODULE}/me \
-H "Content-Type: application/json" \
-H "Authorization: Bearer ${AUTH_TOKEN}")
VERIFY_USERNAME=$(extract_value "$VERIFY_RESULT" "username")
if [ "$VERIFY_USERNAME" != "$EXPECTED_USERNAME" ]; then
log_error "验证失败:用户名未更新"
echo "$VERIFY_RESULT"
exit 1
fi
log_success "验证成功"
# 6. 修改密码(使用旧密码)
log_step "6" "修改密码(使用旧密码)"
OLD_PASSWORD_RESULT=$(curl -s -X POST ${BASE_URL}/${MODULE}/password \
-H "Content-Type: application/json" \
-H "Authorization: Bearer ${AUTH_TOKEN}" \
-d "{
\"oldPassword\": \"password123\",
\"newPassword\": \"newpassword123\"
}")
# 验证修改
OLD_PASSWORD_CODE=$(extract_int "$OLD_PASSWORD_RESULT" "code")
if [ "$OLD_PASSWORD_CODE" != "200" ]; then
log_error "修改密码失败"
echo "$OLD_PASSWORD_RESULT"
exit 1
fi
log_success "修改密码成功"
echo "$OLD_PASSWORD_RESULT"
# 7. 使用新密码重新登录
log_step "7" "使用新密码重新登录"
NEW_LOGIN_RESULT=$(curl -s -X POST ${BASE_URL}/login \
-H "Content-Type: application/json" \
-d "{
\"email\": \"test${TIMESTAMP}@example.com\",
\"password\": \"newpassword123\"
}")
# 获取新Token
NEW_AUTH_TOKEN=$(extract_value "$NEW_LOGIN_RESULT" "token")
NEW_LOGIN_CODE=$(extract_int "$NEW_LOGIN_RESULT" "code")
if [ "$NEW_LOGIN_CODE" != "200" ] || [ -z "$NEW_AUTH_TOKEN" ]; then
log_error "重新登录失败"
echo "$NEW_LOGIN_RESULT"
exit 1
fi
log_success "重新登录成功,新Token: ${NEW_AUTH_TOKEN:0:20}..."
echo "$NEW_LOGIN_RESULT"
# 8. 获取个人信息(验证密码修改成功)
log_step "8" "获取个人信息(验证密码修改成功)"
FINAL_GET_RESULT=$(curl -s -X GET ${BASE_URL}/${MODULE}/me \
-H "Content-Type: application/json" \
-H "Authorization: Bearer ${NEW_AUTH_TOKEN}")
# 验证响应
ID=$(extract_int "$FINAL_GET_RESULT" "id")
if [ -z "$ID" ]; then
log_error "获取失败:新Token可能无效"
echo "$FINAL_GET_RESULT"
exit 1
fi
log_success "验证成功,密码修改完成"
echo -e "\n\033[32m=== Profile 模块测试完成 ===\033[0m"
echo "用户ID: $REGISTER_ID"
echo "测试账号: test${TIMESTAMP}@example.com"
echo "测试密码: password123 / newpassword123"

121
backend/tests/run_all_tests.sh

@ -0,0 +1,121 @@
#!/bin/bash
# 统一测试运行脚本
set -e
BASE_DIR="$(cd "$(dirname "$0")" && pwd)"
RESULTS_DIR="$BASE_DIR/results"
mkdir -p "$RESULTS_DIR"
# 日志函数
log_info() {
echo -e "\033[32m[INFO]\033[0m $1"
}
log_success() {
echo -e "\033[32m[SUCCESS]\033[0m $1"
}
log_error() {
echo -e "\033[31m[ERROR]\033[0m $1"
}
print_header() {
echo -e "\n\033[36m========================================\033[0m"
echo -e "\033[36m $1\033[0m"
echo -e "\033[36m========================================\033[0m\n"
}
# 测试结果统计
declare -A USER_RESULTS=()
declare -A PROFILE_RESULTS=()
# 运行 User 模块测试
print_header "User 模块测试"
if bash "$BASE_DIR/user/test_user.sh" > "$RESULTS_DIR/user_test.log" 2>&1; then
USER_RESULTS["新增"]="通过"
USER_RESULTS["查询(新增后)"]="通过"
USER_RESULTS["更新"]="通过"
USER_RESULTS["查询(更新后)"]="通过"
USER_RESULTS["列表查询"]="通过"
USER_RESULTS["删除"]="通过"
USER_RESULTS["查询(删除后)"]="通过"
log_success "User 模块测试全部通过"
else
USER_RESULTS["新增"]="失败"
log_error "User 模块测试失败,查看日志: $RESULTS_DIR/user_test.log"
fi
# 运行 Profile 模块测试
print_header "Profile 模块测试"
if bash "$BASE_DIR/profile/test_profile.sh" > "$RESULTS_DIR/profile_test.log" 2>&1; then
PROFILE_RESULTS["获取"]="通过"
PROFILE_RESULTS["更新"]="通过"
PROFILE_RESULTS["查询(更新后)"]="通过"
PROFILE_RESULTS["修改密码"]="通过"
log_success "Profile 模块测试全部通过"
else
PROFILE_RESULTS["获取"]="失败"
log_error "Profile 模块测试失败,查看日志: $RESULTS_DIR/profile_test.log"
fi
# 输出测试报告
echo -e "\n\033[32m========================================\033[0m"
echo -e "\033[32m 测试报告\033[0m"
echo -e "\033[32m========================================\033[0m\n"
echo -e "\033[1m【User 模块】\033[0m"
echo "步骤 结果"
echo "--------------------------------"
echo "新增 ${USER_RESULTS["新增"]}"
echo "查询(新增后) ${USER_RESULTS["查询(新增后)"]}"
echo "更新 ${USER_RESULTS["更新"]}"
echo "查询(更新后) ${USER_RESULTS["查询(更新后)"]}"
echo "列表查询 ${USER_RESULTS["列表查询"]}"
echo "删除 ${USER_RESULTS["删除"]}"
echo "查询(删除后) ${USER_RESULTS["查询(删除后)"]}"
echo -e "\n\033[1m【Profile 模块】\033[0m"
echo "步骤 结果"
echo "--------------------------------"
echo "获取 ${PROFILE_RESULTS["获取"]}"
echo "更新 ${PROFILE_RESULTS["更新"]}"
echo "查询(更新后) ${PROFILE_RESULTS["查询(更新后)"]}"
echo "修改密码 ${PROFILE_RESULTS["修改密码"]}"
# 判断总体结果
USER_PASS=0
USER_FAIL=0
for result in "${USER_RESULTS[@]}"; do
if [ "$result" = "通过" ]; then
((USER_PASS++))
else
((USER_FAIL++))
fi
done
PROFILE_PASS=0
PROFILE_FAIL=0
for result in "${PROFILE_RESULTS[@]}"; do
if [ "$result" = "通过" ]; then
((PROFILE_PASS++))
else
((PROFILE_FAIL++))
fi
done
echo -e "\n\033[32m========================================\033[0m"
echo -e "\033[32m 总体结果\033[0m"
echo -e "\033[32m========================================\033[0m"
echo "User 模块: 通过 $USER_PASS / $((USER_PASS + USER_FAIL))"
echo "Profile 模块: 通过 $PROFILE_PASS / $((PROFILE_PASS + PROFILE_FAIL))"
if [ $USER_FAIL -eq 0 ] && [ $PROFILE_FAIL -eq 0 ]; then
echo -e "\033[32m所有测试通过!\033[0m\n"
exit 0
else
echo -e "\033[31m存在失败的测试项,请查看日志\033[0m\n"
exit 1
fi

136
backend/tests/user/test_user.sh

@ -0,0 +1,136 @@
#!/bin/bash
BASE_URL="http://localhost:8888/api/v1"
MODULE="user"
TIMESTAMP=$(date +%s)
log_step() {
echo -e "\n\033[36m--- Step $1: $2 ---\033[0m"
}
log_success() {
echo -e "\033[32m[SUCCESS]\033[0m $1"
}
log_error() {
echo -e "\033[31m[ERROR]\033[0m $1"
}
extract_value() {
echo "$1" | grep -o "\"$2\":\"[^\"]*\"" | sed 's/"'"$2'""://' | sed 's/^"//' | sed 's/"$//'
}
extract_int() {
echo "$1" | grep -o "\"$2\":[^,}]*" | sed 's/"'"$2'"://' | tr -d ' '
}
AUTH_TOKEN=""
log_step "0" "Register admin"
REGISTER_RESULT=$(curl -s -X POST ${BASE_URL}/register -H "Content-Type: application/json" -d "{\"username\":\"admin${TIMESTAMP}\",\"email\":\"admin${TIMESTAMP}@example.com\",\"password\":\"password123\",\"phone\":\"13800138000\"}")
log_success "Register success"
echo "$REGISTER_RESULT"
log_step "0.1" "Admin login"
LOGIN_RESULT=$(curl -s -X POST ${BASE_URL}/login -H "Content-Type: application/json" -d "{\"email\":\"admin${TIMESTAMP}@example.com\",\"password\":\"password123\"}")
AUTH_TOKEN=$(extract_value "$LOGIN_RESULT" "token")
LOGIN_CODE=$(extract_int "$LOGIN_RESULT" "code")
if [ "$LOGIN_CODE" != "200" ] || [ -z "$AUTH_TOKEN" ]; then
log_error "Login failed"
echo "$LOGIN_RESULT"
exit 1
fi
log_success "Login success"
echo "$LOGIN_RESULT"
log_step "1" "Create user"
CREATE_RESULT=$(curl -s -X POST ${BASE_URL}/${MODULE} -H "Content-Type: application/json" -H "Authorization: Bearer ${AUTH_TOKEN}" -d "{\"username\":\"testuser${TIMESTAMP}\",\"email\":\"test${TIMESTAMP}@example.com\",\"password\":\"password123\",\"phone\":\"13800138000\"}")
ID=$(extract_int "$CREATE_RESULT" "id")
if [ -z "$ID" ]; then
log_error "Create failed"
echo "$CREATE_RESULT"
exit 1
fi
log_success "Create success, ID: $ID"
echo "$CREATE_RESULT"
log_step "2" "Query user"
QUERY_RESULT=$(curl -s -X GET ${BASE_URL}/${MODULE}/${ID} -H "Authorization: Bearer ${AUTH_TOKEN}")
QUERY_ID=$(extract_int "$QUERY_RESULT" "id")
if [ "$QUERY_ID" != "$ID" ]; then
log_error "Query failed"
echo "$QUERY_RESULT"
exit 1
fi
log_success "Query success"
echo "$QUERY_RESULT"
log_step "3" "Update user"
UPDATE_RESULT=$(curl -s -X PUT ${BASE_URL}/${MODULE}/${ID} -H "Content-Type: application/json" -H "Authorization: Bearer ${AUTH_TOKEN}" -d "{\"username\":\"testuser${TIMESTAMP}_updated\",\"phone\":\"13900139000\"}")
VERIFY_UPDATE_RESULT=$(curl -s -X GET ${BASE_URL}/${MODULE}/${ID} -H "Authorization: Bearer ${AUTH_TOKEN}")
VERIFY_USERNAME=$(extract_value "$VERIFY_UPDATE_RESULT" "username")
VERIFY_PHONE=$(extract_value "$VERIFY_UPDATE_RESULT" "phone")
EXPECTED_USERNAME="testuser${TIMESTAMP}_updated"
if [ "$VERIFY_USERNAME" != "$EXPECTED_USERNAME" ] || [ "$VERIFY_PHONE" != "13900139000" ]; then
log_error "Update failed"
echo "$VERIFY_UPDATE_RESULT"
exit 1
fi
log_success "Update success"
log_step "4" "Verify update"
VERIFY_RESULT=$(curl -s -X GET ${BASE_URL}/${MODULE}/${ID} -H "Authorization: Bearer ${AUTH_TOKEN}")
VERIFY_USERNAME=$(extract_value "$VERIFY_RESULT" "username")
if [ "$VERIFY_USERNAME" != "$EXPECTED_USERNAME" ]; then
log_error "Verify failed"
echo "$VERIFY_RESULT"
exit 1
fi
log_success "Verify success"
log_step "5" "List query"
LIST_RESULT=$(curl -s -X GET "${BASE_URL}/${MODULE}s?page=1&pageSize=10" -H "Authorization: Bearer ${AUTH_TOKEN}")
if echo "$LIST_RESULT" | grep -q '"id"'; then
log_success "List success"
else
log_error "List failed"
echo "$LIST_RESULT"
exit 1
fi
log_step "6" "Delete user"
DELETE_RESULT=$(curl -s -X DELETE ${BASE_URL}/${MODULE}/${ID} -H "Authorization: Bearer ${AUTH_TOKEN}")
DELETE_CODE=$(extract_int "$DELETE_RESULT" "code")
if [ "$DELETE_CODE" = "200" ]; then
log_success "Delete success"
else
log_error "Delete failed"
echo "$DELETE_RESULT"
exit 1
fi
log_step "7" "Verify delete"
VERIFY_DELETE_RESULT=$(curl -s -X GET ${BASE_URL}/${MODULE}/${ID} -H "Authorization: Bearer ${AUTH_TOKEN}")
VERIFY_CODE=$(extract_int "$VERIFY_DELETE_RESULT" "code")
if [ "$VERIFY_CODE" = "404" ]; then
log_success "Verify success"
else
log_error "Verify failed"
echo "$VERIFY_DELETE_RESULT"
exit 1
fi
echo -e "\n\033[32m=== User module test completed ===\033[0m"

122
backend/zero-skills.md

@ -0,0 +1,122 @@
# Zero Skills - AI 技能框架
> zero-skills 是基于 go-zero 的 AI 技能管理框架,用于创建和管理 AI 助手的自定义技能。
## 官方资源
- [GitHub](https://github.com/zeromicro/zero-skills)
## 概述
zero-skills 提供了一套技能定义和执行的框架,使得可以方便地为 AI 系统添加自定义功能和业务逻辑。
## 核心概念
### 技能 (Skill)
技能是 AI 系统可以执行的一个原子性任务或功能,例如:
- 发送邮件
- 调用 API
- 数据库查询
- 文件处理
### 技能定义
```go
type Skill struct {
Name string // 技能名称
Description string // 技能描述
Parameters map[string]interface{} // 参数定义
Handler HandlerFunc // 处理函数
}
type HandlerFunc func(ctx context.Context, args map[string]interface{}) (interface{}, error)
```
## 使用示例
### 定义技能
```go
package skills
import (
"context"
"fmt"
)
var SendEmailSkill = &Skill{
Name: "send_email",
Description: "Send an email to a recipient",
Parameters: map[string]interface{}{
"to": map[string]string{"type": "string", "description": "Recipient email"},
"subject": map[string]string{"type": "string", "description": "Email subject"},
"body": map[string]string{"type": "string", "description": "Email body"},
},
Handler: func(ctx context.Context, args map[string]interface{}) (interface{}, error) {
to := args["to"].(string)
subject := args["subject"].(string)
body := args["body"].(string)
// 发送邮件逻辑
fmt.Printf("Sending email to %s: %s\n", to, subject)
return map[string]interface{}{
"success": true,
"message": "Email sent successfully",
}, nil
},
}
```
### 注册技能
```go
package main
import (
"github.com/yourapp/skills"
)
func main() {
// 初始化技能管理器
skillManager := skills.NewManager()
// 注册技能
skillManager.Register(skills.SendEmailSkill)
skillManager.Register(skills.QueryDatabaseSkill)
// 启动服务
// ...
}
```
## 技能配置
```yaml
skills:
enabled: true
base_path: "./skills"
skills:
- name: "send_email"
enabled: true
config:
smtp_host: "smtp.example.com"
smtp_port: 587
- name: "query_database"
enabled: true
config:
connection_string: "user:pass@localhost:3306/db"
```
## 中间件支持
```go
// 添加日志中间件
skillManager.Use(middleware.Logger())
// 添加认证中间件
skillManager.Use(middleware.Auth())
// 添加限流中间件
skillManager.Use(middleware.RateLimit(100))
```

257
frontend/react-native/ai-context.md

@ -0,0 +1,257 @@
# React Native AI Context - shadcn/ui x NativeWind
> React Native 应用的 AI 上下文指南,结合 shadcn/ui 设计理念和 NativeWind 工具类。
## 概述
这是 React Native 应用的 AI 开发上下文,使用 NativeWind (Tailwind CSS for React Native) 作为样式解决方案,遵循 shadcn/ui 的设计理念。
## 核心技术栈
### 基础框架
- [Expo](https://expo.dev/) - React Native 开发框架
- [NativeWind](https://www.nativewind.dev/) - Tailwind CSS for React Native
- [React Native Reusables](https://rnr-docs.vercel.app/) - UI 组件库
### shadcn/ui 设计原则
1. **Open Code** - 代码完全可定制
2. **Composition** - 组件可组合
3. **Distribution** - 易于分发和安装
4. **Beautiful Defaults** - 美观的默认样式
5. **AI-Ready** - 支持AI开发
## 安装
```bash
# 初始化 Expo 项目
npx create-expo-app myapp --template expo-template-blank-typescript
cd myapp
# 安装 NativeWind
npm install --save-dev tailwindcss
npm install nativewind
npm install -D @types/react-native
# 配置 NativeWind
npx tailwindcss init
```
## 配置
### tailwind.config.js
```javascript
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
"./App.{js,jsx,ts,tsx}",
"./src/**/*.{js,jsx,ts,tsx}",
],
presets: [require("nativewind/preset")],
theme: {
extend: {
colors: {
border: "hsl(var(--border))",
input: "hsl(var(--input))",
ring: "hsl(var(--ring))",
background: "hsl(var(--background))",
foreground: "hsl(var(--foreground))",
primary: {
DEFAULT: "hsl(var(--primary))",
foreground: "hsl(var(--primary-foreground))",
},
secondary: {
DEFAULT: "hsl(var(--secondary))",
foreground: "hsl(var(--secondary-foreground))",
},
destructive: {
DEFAULT: "hsl(var(--destructive))",
foreground: "hsl(var(--destructive-foreground))",
},
muted: {
DEFAULT: "hsl(var(--muted))",
foreground: "hsl(var(--muted-foreground))",
},
accent: {
DEFAULT: "hsl(var(--accent))",
foreground: "hsl(var(--accent-foreground))",
},
},
borderRadius: {
lg: "var(--radius)",
md: "calc(var(--radius) - 2px)",
sm: "calc(var(--radius) - 4px)",
},
},
},
plugins: [],
}
```
### nativewind-env.d.ts
```typescript
/// <reference types="nativewind/types" />
```
### babel.config.js
```javascript
module.exports = function (api) {
api.cache(true);
return {
presets: [['babel-preset-expo', { jsxImportSource: 'nativewind' }]],
plugins: [
'react-native-reanimated/plugin',
'nativewind/babel',
],
};
}
```
## 核心组件
### Button 组件
```typescript
import { Text, Pressable, PressableProps, ViewProps, TextProps } from 'react-native';
import { cn } from '@/lib/utils';
interface ButtonProps extends PressableProps {
variant?: 'default' | 'destructive' | 'outline' | 'secondary' | 'ghost';
size?: 'default' | 'sm' | 'lg';
className?: string;
children: React.ReactNode;
}
const Button = React.forwardRef<Pressable, ButtonProps>(
({ className, variant = 'default', size = 'default', children, ...props }, ref) => {
const variants = {
default: 'bg-primary text-primary-foreground hover:bg-primary/90',
destructive: 'bg-destructive text-destructive-foreground hover:bg-destructive/90',
outline: 'border border-input bg-background hover:bg-accent hover:text-accent-foreground',
secondary: 'bg-secondary text-secondary-foreground hover:bg-secondary/80',
ghost: 'hover:bg-accent hover:text-accent-foreground',
};
const sizes = {
default: 'h-10 px-4 py-2',
sm: 'h-9 rounded-md px-3',
lg: 'h-11 rounded-md px-8',
};
return (
<Pressable
ref={ref}
className={cn(
'rounded-md text-sm font-medium flex items-center justify-center',
variants[variant],
sizes[size],
className
)}
{...props}
>
<Text className="font-medium">{children}</Text>
</Pressable>
);
}
);
```
### Card 组件
```typescript
import { View, ViewProps, Text, TextProps } from 'react-native';
import { cn } from '@/lib/utils';
interface CardProps extends ViewProps {
className?: string;
}
const Card = React.forwardRef<View, CardProps>(({ className, ...props }, ref) => (
<View ref={ref} className={cn("rounded-lg border bg-card text-card-foreground shadow-sm", className)} {...props} />
));
const CardHeader = React.forwardRef<View, ViewProps>(({ className, ...props }, ref) => (
<View ref={ref} className={cn("flex flex-col space-y-1.5 p-6", className)} {...props} />
));
const CardTitle = React.forwardRef<Text, TextProps>(({ className, ...props }, ref) => (
<Text ref={ref} className={cn("text-2xl font-semibold leading-none tracking-tight", className)} {...props} />
));
const CardContent = React.forwardRef<View, ViewProps>(({ className, ...props }, ref) => (
<View ref={ref} className={cn("p-6 pt-0", className)} {...props} />
));
```
## 设计令牌
### 颜色系统
```typescript
// 使用 CSS 变量定义颜色
// 在 tailwind.config.js 中配置
```
### 字体系统
```typescript
// NativeWind 中的字体设置
// 使用 Tailwind 的字体工具类
text-sm // 小号文本
text-base // 基础文本
text-lg // 大号文本
font-medium // 中等字重
font-bold // 粗体
```
## 开发规范
### 组件结构
```
src/
├── components/
│ ├── ui/ # 基础 UI 组件
│ │ ├── button.tsx
│ │ ├── card.tsx
│ │ └── ...
│ └── features/ # 功能组件
├── lib/
│ └── utils.ts # 工具函数
├── hooks/ # 自定义 hooks
└── screens/ # 屏幕组件
```
### 命名约定
- 组件使用 PascalCase: `Button.tsx`
- 文件使用 kebab-case: `user-profile.tsx`
- 样式类使用 camelCase 或 Tailwind 工具类
## 与 React Web shadcn/ui 对应
| React Web | React Native |
|----------|--------------|
| `<Button>` | `<Pressable>` with styling |
| `<Card>` | `<View>` with border and shadow |
| `<Input>` | `<TextInput>` with styling |
| `<Dialog>` | `<Modal>` or `<Modal>` from react-native-modal |
| `<Toast>` | Toast from react-native-toast-message |
| `<Avatar>` | `<Image>` with circular styling |
## 相关资源
- [NativeWind 文档](https://www.nativewind.dev/)
- [React Native Reusables](https://rnr-docs.vercel.app/) - UI 组件库
- [Expo 文档](https://docs.expo.dev/)
- [shadcn/ui](https://ui.shadcn.com/) - Web 版本参考
- [shadcn/ui llms.txt](https://ui.shadcn.com/llms.txt) - 参考
## MCP 集成
使用 AI 工具:
```bash
npx shadcn@latest mcp
```
## 最佳实践
1. 使用 NativeWind 进行样式管理
2. 遵循 shadcn/ui 的组件设计原则
3. 组件应该是可组合和可定制的
4. 使用 TypeScript 保证类型安全
5. 使用 Expo Router 进行导航

149
frontend/react-shadcn/llms.txt

@ -0,0 +1,149 @@
# shadcn/ui
> shadcn/ui is a collection of beautifully-designed, accessible
components and a code distribution platform. It is built with TypeScript, Tailwind
CSS, and Radix UI primitives. It supports multiple frameworks including Next.js,
Vite, Remix, Astro, and more. Open Source. Open Code. AI-Ready. It also comes
with a command-line tool to install and manage components and a registry system
to publish and distribute code.
## Overview
- [Introduction](https://ui.shadcn.com/docs): Core principles—Open Code, Composition, Distribution, Beautiful Defaults, and AI-Ready design.
- [CLI](https://ui.shadcn.com/docs/cli): Command-line tool for installing and managing components.
- [components.json](https://ui.shadcn.com/docs/components-json): Configuration file for customizing the CLI and component installation.
- [Theming](https://ui.shadcn.com/docs/theming): Guide to customizing colors, typography, and design tokens.
- [Changelog](https://ui.shadcn.com/docs/changelog): Release notes and version history.
- [About](https://ui.shadcn.com/docs/about): Credits and project information.
## Installation
- [Next.js](https://ui.shadcn.com/docs/installation/next): Install shadcn/ui in a Next.js project.
- [Vite](https://ui.shadcn.com/docs/installation/vite): Install shadcn/ui in a Vite project.
- [Remix](https://ui.shadcn.com/docs/installation/remix): Install shadcn/ui in a Remix project.
- [Astro](https://ui.shadcn.com/docs/installation/astro): Install shadcn/ui in an Astro project.
- [Laravel](https://ui.shadcn.com/docs/installation/laravel): Install shadcn/ui in a Laravel project.
- [Gatsby](https://ui.shadcn.com/docs/installation/gatsby): Install shadcn/ui in a Gatsby project.
- [React Router](https://ui.shadcn.com/docs/installation/react-router): Install shadcn/ui in a React Router project.
- [TanStack Router](https://ui.shadcn.com/docs/installation/tanstack-router): Install shadcn/ui in a TanStack Router project.
- [TanStack Start](https://ui.shadcn.com/docs/installation/tanstack): Install shadcn/ui in a TanStack Start project.
- [Manual Installation](https://ui.shadcn.com/docs/installation/manual): Manually install shadcn/ui without the CLI.
## Components
### Form & Input
- [Form](https://ui.shadcn.com/docs/components/form): Building forms with React Hook Form and Zod validation.
- [Field](https://ui.shadcn.com/docs/components/field): Field component for form inputs with labels and error messages.
- [Button](https://ui.shadcn.com/docs/components/button): Button component with multiple variants.
- [Button Group](https://ui.shadcn.com/docs/components/button-group): Group multiple buttons together.
- [Input](https://ui.shadcn.com/docs/components/input): Text input component.
- [Input Group](https://ui.shadcn.com/docs/components/input-group): Input component with prefix and suffix addons.
- [Input OTP](https://ui.shadcn.com/docs/components/input-otp): One-time password input component.
- [Textarea](https://ui.shadcn.com/docs/components/textarea): Multi-line text input component.
- [Checkbox](https://ui.shadcn.com/docs/components/checkbox): Checkbox input component.
- [Radio Group](https://ui.shadcn.com/docs/components/radio-group): Radio button group component.
- [Select](https://ui.shadcn.com/docs/components/select): Select dropdown component.
- [Switch](https://ui.shadcn.com/docs/components/switch): Toggle switch component.
- [Slider](https://ui.shadcn.com/docs/components/slider): Slider input component.
- [Calendar](https://ui.shadcn.com/docs/components/calendar): Calendar component for date selection.
- [Date Picker](https://ui.shadcn.com/docs/components/date-picker): Date picker component combining input and calendar.
- [Combobox](https://ui.shadcn.com/docs/components/combobox): Searchable select component with autocomplete.
- [Label](https://ui.shadcn.com/docs/components/label): Form label component.
### Layout & Navigation
- [Accordion](https://ui.shadcn.com/docs/components/accordion): Collapsible accordion component.
- [Breadcrumb](https://ui.shadcn.com/docs/components/breadcrumb): Breadcrumb navigation component.
- [Navigation Menu](https://ui.shadcn.com/docs/components/navigation-menu): Accessible navigation menu with dropdowns.
- [Sidebar](https://ui.shadcn.com/docs/components/sidebar): Collapsible sidebar component for app layouts.
- [Tabs](https://ui.shadcn.com/docs/components/tabs): Tabbed interface component.
- [Separator](https://ui.shadcn.com/docs/components/separator): Visual divider between content sections.
- [Scroll Area](https://ui.shadcn.com/docs/components/scroll-area): Custom scrollable area with styled scrollbars.
- [Resizable](https://ui.shadcn.com/docs/components/resizable): Resizable panel layout component.
### Overlays & Dialogs
- [Dialog](https://ui.shadcn.com/docs/components/dialog): Modal dialog component.
- [Alert Dialog](https://ui.shadcn.com/docs/components/alert-dialog): Alert dialog for confirmation prompts.
- [Sheet](https://ui.shadcn.com/docs/components/sheet): Slide-out panel component (drawer).
- [Drawer](https://ui.shadcn.com/docs/components/drawer): Mobile-friendly drawer component using Vaul.
- [Popover](https://ui.shadcn.com/docs/components/popover): Floating popover component.
- [Tooltip](https://ui.shadcn.com/docs/components/tooltip): Tooltip component for additional context.
- [Hover Card](https://ui.shadcn.com/docs/components/hover-card): Card that appears on hover.
- [Context Menu](https://ui.shadcn.com/docs/components/context-menu): Right-click context menu.
- [Dropdown Menu](https://ui.shadcn.com/docs/components/dropdown-menu): Dropdown menu component.
- [Menubar](https://ui.shadcn.com/docs/components/menubar): Horizontal menubar component.
- [Command](https://ui.shadcn.com/docs/components/command): Command palette component (cmdk).
### Feedback & Status
- [Alert](https://ui.shadcn.com/docs/components/alert): Alert component for messages and notifications.
- [Toast](https://ui.shadcn.com/docs/components/toast): Toast notification component using Sonner.
- [Progress](https://ui.shadcn.com/docs/components/progress): Progress bar component.
- [Spinner](https://ui.shadcn.com/docs/components/spinner): Loading spinner component.
- [Skeleton](https://ui.shadcn.com/docs/components/skeleton): Skeleton loading placeholder.
- [Badge](https://ui.shadcn.com/docs/components/badge): Badge component for labels and status indicators.
- [Empty](https://ui.shadcn.com/docs/components/empty): Empty state component for no data scenarios.
### Display & Media
- [Avatar](https://ui.shadcn.com/docs/components/avatar): Avatar component for user profiles.
- [Card](https://ui.shadcn.com/docs/components/card): Card container component.
- [Table](https://ui.shadcn.com/docs/components/table): Table component for displaying data.
- [Data Table](https://ui.shadcn.com/docs/components/data-table): Advanced data table with sorting, filtering, and pagination.
- [Chart](https://ui.shadcn.com/docs/components/chart): Chart components using Recharts.
- [Carousel](https://ui.shadcn.com/docs/components/carousel): Carousel component using Embla Carousel.
- [Aspect Ratio](https://ui.shadcn.com/docs/components/aspect-ratio): Container that maintains aspect ratio.
- [Typography](https://ui.shadcn.com/docs/components/typography): Typography styles and components.
- [Item](https://ui.shadcn.com/docs/components/item): Generic item component for lists and menus.
- [Kbd](https://ui.shadcn.com/docs/components/kbd): Keyboard shortcut display component.
### Misc
- [Collapsible](https://ui.shadcn.com/docs/components/collapsible): Collapsible container component.
- [Toggle](https://ui.shadcn.com/docs/components/toggle): Toggle button component.
- [Toggle Group](https://ui.shadcn.com/docs/components/toggle-group): Group of toggle buttons.
- [Pagination](https://ui.shadcn.com/docs/components/pagination): Pagination component for lists and tables.
## Dark Mode
- [Dark Mode](https://ui.shadcn.com/docs/dark-mode): Overview of dark mode implementation.
- [Dark Mode - Next.js](https://ui.shadcn.com/docs/dark-mode/next): Dark mode setup for Next.js.
- [Dark Mode - Vite](https://ui.shadcn.com/docs/dark-mode/vite): Dark mode setup for Vite.
- [Dark Mode - Astro](https://ui.shadcn.com/docs/dark-mode/astro): Dark mode setup for Astro.
- [Dark Mode - Remix](https://ui.shadcn.com/docs/dark-mode/remix): Dark mode setup for Remix.
## Forms
- [Forms Overview](https://ui.shadcn.com/docs/forms): Guide to building forms with shadcn/ui.
- [React Hook Form](https://ui.shadcn.com/docs/forms/react-hook-form): Using shadcn/ui with React Hook Form.
- [TanStack Form](https://ui.shadcn.com/docs/forms/tanstack-form): Using shadcn/ui with TanStack Form.
- [Forms - Next.js](https://ui.shadcn.com/docs/forms/next): Building forms in Next.js with Server Actions.
## Advanced
- [Monorepo](https://ui.shadcn.com/docs/monorepo): Using shadcn/ui in a monorepo setup.
- [React 19](https://ui.shadcn.com/docs/react-19): React 19 support and migration guide.
- [Tailwind CSS v4](https://ui.shadcn.com/docs/tailwind-v4): Tailwind CSS v4 support and setup.
- [JavaScript](https://ui.shadcn.com/docs/javascript): Using shadcn/ui with JavaScript (no TypeScript).
- [Figma](https://ui.shadcn.com/docs/figma): Figma design resources.
- [v0](https://ui.shadcn.com/docs/v0): Generating UI with v0 by Vercel.
## MCP Server
- [MCP Server](https://ui.shadcn.com/docs/mcp): Model Context Protocol server for AI integrations.
## Registry
- [Registry Overview](https://ui.shadcn.com/docs/registry): Creating and publishing your own component registry.
- [Getting Started](https://ui.shadcn.com/docs/registry/getting-started): Set up your own registry.
- [Examples](https://ui.shadcn.com/docs/registry/examples): Example registries.
- [FAQ](https://ui.shadcn.com/docs/registry/faq): Common questions about registries.
- [Authentication](https://ui.shadcn.com/docs/registry/authentication): Adding authentication to your registry.
- [Registry MCP](https://ui.shadcn.com/docs/registry/mcp): MCP integration for registries.
### Registry Schemas
- [Registry Schema](https://ui.shadcn.com/schema/registry.json): JSON Schema for registry index files.
- [Registry Item Schema](https://ui.shadcn.com/schema/registry-item.json): JSON Schema for individual registry items.

234
frontend/vue-primevue/llms.txt

@ -0,0 +1,234 @@
# PrimeVue - Vue UI 组件库
> PrimeVue 是一个功能强大且易于使用的 Vue 3 UI 组件库,提供丰富的组件和主题。
## 官方文档
- [官方网站](https://primevue.org/)
- [GitHub](https://github.com/primefaces/primevue)
- [入门指南](https://primevue.org/gettingstarted/)
- [组件文档](https://primevue.org/components/)
## 安装
```bash
npm install primevue @primevue/themes
# 或
yarn add primevue @primevue/themes
# 或
pnpm add primevue @primevue/themes
```
## 快速开始
```javascript
// main.js
import { createApp } from 'vue'
import PrimeVue from 'primevue/config'
import Aura from '@primevue/themes/aura'
import App from './App.vue'
const app = createApp(App)
app.use(PrimeVue, {
theme: {
preset: Aura
}
})
app.mount('#app')
```
## 主要组件
### Form Components
- [InputText](https://primevue.org/inputtext/)
- [InputNumber](https://primevue.org/inputnumber/)
- [Textarea](https://primevue.org/textarea/)
- [Checkbox](https://primevue.org/checkbox/)
- [RadioButton](https://primevue.org/radiobutton/)
- [Dropdown](https://primevue.org/dropdown/)
- [MultiSelect](https://primevue.org/multiselect/)
- [Calendar](https://primevue.org/calendar/)
- [DatePicker](https://primevue.org/calendar/)
- [ToggleButton](https://primevue.org/togglebutton/)
- [SelectButton](https://primevue.org/selectbutton/)
- [InputSwitch](https://primevue.org/inputswitch/)
- [Slider](https://primevue.org/slider/)
- [Knob](https://primevue.org/knob/)
### Data Components
- [DataTable](https://primevue.org/datatable/)
- [DataView](https://primevue.org/dataview/)
- [Tree](https://primevue.org/tree/)
- [TreeTable](https://primevue.org/treetable/)
- [OrderList](https://primevue.org/orderlist/)
- [PickList](https://primevue.org/picklist/)
- [Timeline](https://primevue.org/timeline/)
- [VirtualScroller](https://primevue.org/virtualscroller/)
### Overlay Components
- [Dialog](https://primevue.org/dialog/)
- [ConfirmDialog](https://primevue.org/confirmdialog/)
- [DynamicDialog](https://primevue.org/dynamicdialog/)
- [OverlayPanel](https://primevue.org/overlaypanel/)
- [Sidebar](https://primevue.org/sidebar/)
- [Tooltip](https://primevue.org/tooltip/)
- [Menu](https://primevue.org/menu/)
- [ContextMenu](https://primevue.org/contextmenu/)
- [Megamenu](https://primevue.org/megamenu/)
- [Menubar](https://primevue.org/menubar/)
- [PanelMenu](https://primevue.org/panelmenu/)
- [TieredMenu](https://primevue.org/tieredmenu/)
### Navigation Components
- [BreadCrumb](https://primevue.org/breadcrumb/)
- [TabMenu](https://primevue.org/tabmenu/)
- [TabView](https://primevue.org/tabview/)
- [TieredMenu](https://primevue.org/tieredmenu/)
- [Dock](https://primevue.org/dock/)
### Messages Components
- [Message](https://primevue.org/message/)
- [Toast](https://primevue.org/toast/)
- [Inplace](https://primevue.org/inplace/)
- [ScrollTop](https://primevue.org/scrolltop/)
- [ProgressBar](https://primevue.org/progressbar/)
- [Messages](https://primevue.org/messages/)
### Media Components
- [Image](https://primevue.org/image/)
- [Galleria](https://primevue.org/galleria/)
- [Carousel](https://primevue.org/carousel/)
- [ImageCompare](https://primevue.org/imagecompare/)
### Panels Components
- [Accordion](https://primevue.org/accordion/)
- [Card](https://primevue.org/card/)
- [Fieldset](https://primevue.org/fieldset/)
- [Panel](https://primevue.org/panel/)
- [Divider](https://primevue.org/divider/)
- [ScrollPanel](https://primevue.org/scrollpanel/)
### Misc Components
- [Badge](https://primevue.org/badge/)
- [BlockUI](https://primevue.org/blockui/)
- [Chip](https://primevue.org/chip/)
- [Inplace](https://primevue.org/inplace/)
- [Skeleton](https://primevue.org/skeleton/)
- [Tag](https://primevue.org/tag/)
- [Avatar](https://primevue.org/avatar/)
- [AvatarGroup](https://primevue.org/avatargroup/)
- [BlockUI](https://primevue.org/blockui/)
- [ProgressBar](https://primevue.org/progressbar/)
## 主题系统
PrimeVue 提供了强大的主题系统,支持多种预设主题和自定义主题:
### 预设主题
```javascript
import Aura from '@primevue/themes/aura'
import Lara from '@primevue/themes/lara'
import Nora from '@primevue/themes/nora'
import Amethyst from '@primevue/themes/amethyst'
app.use(PrimeVue, {
theme: {
preset: Aura,
options: {
prefix: 'p',
darkModeSelector: '.dark-mode',
cssLayer: false
}
}
})
```
### 自定义主题
```javascript
app.use(PrimeVue, {
theme: {
preset: Aura,
semantic: {
primary: {
50: '{blue.50}',
100: '{blue.100}',
// ...
},
colorScheme: {
light: { ... },
dark: { ... }
}
}
}
})
```
## PrimeFlex
PrimeFlex 是响应式布局系统,用于快速创建布局:
```bash
npm install primeflex
```
## PrimeIcons
图标库:
```bash
npm install primeicons
```
```javascript
import 'primeicons/primeicons.css'
// 使用图标
<i class="pi pi-home"></i>
<i class="pi pi-user"></i>
<i class="pi pi-cog"></i>
```
## Form Validation
与主流表单验证库集成:
- VeeValidate
- Vuelidate
- FormKit
## 无障碍
PrimeVue 完全支持 WCAG 2.0 标准,提供:
- 键盘导航
- ARIA 属性
- 屏幕阅读器支持
## 国际化 (i18n)
```javascript
app.use(PrimeVue, {
locale: {
accept: 'Yes',
reject: 'No',
// ... 更多翻译
}
})
```
## 相关工具
- [MCP Server](https://github.com/primefaces/mcp) - AI 集成
- [PrimeVue Designer](https://primevue.org/designer/) - 可视化设计工具
- [PrimeBlocks](https://primeblocks.org/) - 预构建的 UI 模板
## 示例项目
```bash
# 克隆示例项目
git clone https://github.com/primefaces/primevue-quickstart-vite.git
cd primevue-quickstart-vite
npm install
npm run dev
```

43
readme.md

@ -0,0 +1,43 @@
# 项目介绍
本项目是一个AI开发基础脚手架,旨在帮助开发者快速搭建和启动新的项目。它包含了一些常用的AI开发配置和工具,简化了项目初始化过程。
## 项目构成
- 后端服务:使用 go + go-zero + gorm 构建,提供 RESTful API 或者 gRPC 服务。
- 前端服务:使用 React + Vite 构建,提供用户界面和交互功能。
### 后端使用的主要技术
- Go:一种高效的编程语言,适合构建高性能的后端服务。
- go-zero:一个高性能的微服务框架,简化了服务的开发和部署,AI开发支持如下:
> ai-context: https://github.com/zeromicro/ai-context?tab=readme-ov-file#%E7%AE%80%E4%BD%93%E4%B8%AD%E6%96%87
> mcp-zero : https://github.com/zeromicro/mcp-zero
> zero-skills : https://github.com/zeromicro/zero-skills
- gorm:一个流行的 ORM 库,简化了数据库操作。
### 前端使用的主要技术
- React shadcn/ui :一个基于 React 的 UI 组件库,提供丰富的界面组件和样式。
> ai-context: https://ui.shadcn.com/llms.txt
> mcp-server: npx shadcn@latest mcp
> skills : frontend-design
- React Native:用于构建跨平台移动应用。
> 推荐组件库: [React Native Reusables](https://rnr-docs.vercel.app/) (RN 版 shadcn)
> 基础脚手架: [Expo](https://expo.dev/) + [NativeWind](https://www.nativewind.dev/)
> AI Context: 参考 shadcn_llms.txt 结合 NativeWind 规约
- vue primevue: 一个基于 Vue 的 UI 组件库,提供丰富的界面组件和样式。
> ai-context: https://primevue.org/llms.txt
> mcp-server: npx -y @primevue/mcp
> skills : frontend-design
- Vite:一个现代化的前端构建工具,提供快速的开发体验
## 项目结构
```
.base
├── backend # 后端服务代码
│ ├── api # API 定义和实现
| |── rpc # gRPC 服务定义和实现
├── frontend # 前端服务代码
│ ├── vue-primevue # Vue + PrimeVue 前端代码
│ │ |── pc # PC 端代码
│ │ └── mobile # 移动端代码
│ ├── react-shadcn # React + shadcn/ui 前端代码
| | ├── pc # PC 端代码
| | └── mobile # 移动端代码
│ ├── react-native # React Native 移动端代码
│ └── public # 公共资源
└── README.md # 项目说明文件
Loading…
Cancel
Save