# v2ex **Repository Path**: ifercarly/v2ex ## Basic Information - **Project Name**: v2ex - **Description**: No description available - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-10-30 - **Last Updated**: 2026-10-30 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # V2EX 基于 **Gin + Vue 3** 的前后端分离论坛项目,实现了注册登录、版块、发帖、评论、投票与 Redis 热度榜单。 - 后端:Go 1.26 + Gin + MySQL + Redis,JWT 鉴权、bcrypt 密码、Swagger 文档 - 前端:Vue 3 + Vite + TailwindCSS + Pinia,Vite 代理后端接口 ## 功能 - **用户**:注册、登录、刷新 token;密码 bcrypt 存储,JWT 鉴权 - **版块**:列表与详情(`classifies` 表) - **帖子**:发帖、详情、榜单(Redis 分数榜 / 时间榜)、MySQL 列表 - **投票**:赞成 / 反对 / 取消,发帖一周内有效,按 Reddit 排序算法计分 - **评论**:发表评论、按 ID 批量查询 - **文档**:Swagger UI 在线调试 ## 技术栈 ### 后端 `backend/` | 用途 | 选型 | | --- | --- | | 语言 / 模块 | Go 1.26,module `v2ex` | | Web 框架 | `gin-gonic/gin` v1.12 | | 数据库 | MySQL 8 + `jmoiron/sqlx` + `go-sql-driver/mysql` | | 缓存 | Redis 7+ + `redis/go-redis/v9` | | 配置 | `spf13/viper`(支持配置文件热更新) | | 日志 | `go.uber.org/zap` + `natefinch/lumberjack` | | 鉴权 / 密码 | `golang-jwt/jwt/v5` / `golang.org/x/crypto/bcrypt` | | 分布式 ID | `sony/sonyflake` | | 接口文档 | `swaggo/swag` v1.16.4 + `gin-swagger`(Swagger 2.0) | ### 前端 `frontend/` | 用途 | 选型 | | --- | --- | | 框架 | Vue 3.5 | | 构建 | Vite 8 | | 样式 | TailwindCSS 4 | | 状态 / 路由 | Pinia 4 / vue-router 4 | | 请求 | axios(统一拦截器与 token 注入) | ## 目录结构 ``` . ├── backend/ │ ├── main.go 程序入口(优雅退出、Swagger 通用注解) │ ├── api.md Swagger 文档正文(@description.markdown 引用) │ ├── conf/ 配置文件(本地 config.yaml / 容器 config.docker.yaml) │ ├── controller/ HTTP 处理、参数校验、统一响应、JWT 中间件 │ ├── logic/ 业务逻辑 │ ├── dao/mysql/ MySQL 数据访问 │ ├── dao/redis/ Redis 榜单与投票 │ ├── models/ 结构体、请求 DTO、建表 SQL │ ├── pkg/ jwt / password / snowflake │ ├── routers/ 路由注册 │ ├── settings/ viper 配置加载 │ ├── logger/ zap 日志与 gin 中间件 │ ├── docs/ swag 生成的接口文档(随代码提交) │ ├── Dockerfile 多阶段构建 │ ├── docker-compose.yml MySQL + Redis + 应用 │ └── Makefile 常用命令 └── frontend/ └── src/ ├── assets/ 图片、字体、iconfont ├── components/ HeadBar、ToTop ├── router/ 路由与登录守卫 ├── service/ axios 实例与拦截器 ├── store/ Pinia(登录信息) ├── views/ Home、Content、Publish、Login、SignUp └── style.css Tailwind 主题变量 ``` ## 快速开始 ### 1. 环境要求 - Go >= 1.26 - Node.js >= 20.19 - MySQL 8.x、Redis 7+(也可用 Docker 起,见下) ### 2. 启动依赖 ```bash cd backend docker compose up -d mysql redis ``` 映射到宿主机:MySQL `3306`、Redis `16379`。 ### 3. 初始化数据库 ```bash mysql -h127.0.0.1 -uroot -p123456 v2ex < models/create_tables.sql ``` 脚本会创建 `user`、`classifies`、`post`、`comment` 四张表,并写入 4 条版块种子数据。 ### 4. 启动后端 ```bash cd backend # 按需修改 conf/config.yaml 里的连接信息 go run . # 或热重载(配置见 .air.toml) air ``` - 服务地址: - 接口文档: ### 5. 启动前端 ```bash cd frontend npm install npm run dev ``` 访问 ,`/api/v1` 已由 Vite 代理到 `127.0.0.1:8081`。 ## 配置说明 `backend/conf/config.yaml`(容器环境使用 `config.docker.yaml`,服务名改为 `mysql` / `redis`): | 字段 | 说明 | | --- | --- | | `mode` | `dev` 输出终端彩色日志,`release` 只写文件 | | `port` | HTTP 监听端口,默认 `8081` | | `swagger_enabled` | 是否挂载 `/swagger/*any`,默认开发环境 `true`、容器 `false` | | `log.*` | 日志级别、文件路径、切割大小 / 保留天数 / 备份数 | | `mysql.*` | 连接信息与连接池(`max_open_conns`、`max_idle_conns`) | | `redis.*` | 连接信息、`db`、连接池大小 | 配置支持热更新:改了 `config.yaml` 会自动重新加载。 ## 接口 统一响应结构(HTTP 状态码恒为 200,业务结果看 `code`): ```json { "code": 1000, "message": "success", "data": {} } ``` | code | 含义 | | --- | --- | | 1000 | 成功 | | 1001 | 请求参数错误 | | 1002 | 用户名重复 | | 1003 | 用户不存在 | | 1004 | 用户名或密码错误 | | 1005 | 服务繁忙 | | 1006 / 1007 | token 无效 / 认证格式有误 | | 1008 | 未登录 | 接口前缀 `/api/v1`: | 方法 | 路径 | 说明 | 需登录 | | --- | --- | --- | :-: | | POST | `/signup` | 注册 | ✗ | | POST | `/login` | 登录,返回 `accessToken`(1 年)/ `refreshToken`(7 天) | ✗ | | GET | `/refresh_token` | 用 refreshToken 换新 token | ✗ | | GET | `/classifies` | 版块列表 | ✓ | | GET | `/classifies/:id` | 版块详情 | ✓ | | GET | `/classifies/:id/post` | 版块下的帖子(分页) | ✓ | | POST | `/post` | 发帖,返回新帖 `post_id` | ✓ | | GET | `/post` | 帖子榜单(Redis,返回 `{count, list}`,`order=time/score`) | ✓ | | GET | `/post/:id` | 帖子详情(含票数、评论数、我的投票方向) | ✓ | | GET | `/search` | 关键词搜索帖子(分页) | ✓ | | POST | `/vote` | 投票(`direction`:1 / -1 / 0) | ✓ | | POST | `/comment` | 发表评论(传 `parent_id` 即为回复) | ✓ | | GET | `/comment` | 按评论 ID 批量查询 | ✓ | | GET | `/post/:id/comment` | 帖子评论列表(分页,含回复者信息) | ✓ | | GET | `/ping` | 健康检查 | ✓ | 登录后点 Swagger 右上角 **Authorize**,填入 `Bearer ` 即可在页面上直接调试。 ## 数据库 | 表 | 说明 | | --- | --- | | `user` | 用户,密码为 bcrypt 密文(60 字符) | | `classifies` | 版块,字段 `classifies_id` / `classifies_name` / `introduction` | | `post` | 帖子,`classifies_id` 关联版块,`author_id` 关联用户 | | `comment` | 评论,`post_id` 关联帖子,`parent_id` 支持楼中楼 | Redis 中的键: | key | 类型 | 用途 | | --- | --- | --- | | `v2ex:post:time` | ZSet | 按发帖时间排序 | | `v2ex:post:score` | ZSet | 按热度分数排序(每票 432 分) | | `v2ex:post:` | Hash | 帖子摘要信息(含 `user:name`、`votes`、`comments`) | | `v2ex:post:voted:` | ZSet | 该帖的投票记录(member=用户ID,score=1/-1/0) | ## 常用命令 后端(`backend/`): ```bash make run # go run . make build # 编译 v2ex.exe make test # go test ./... make vet # go vet ./... make fmt # gofmt -w . make docs # 重新生成 docs/(gofmt + go generate) make docs-check # 校验 docs/ 与注解是否一致 make rebuild # 用 MySQL 数据重建 Redis 榜单 ``` 前端(`frontend/`):`npm run dev` / `npm run build` / `npm run preview`。 ## 容器化部署 ```bash cd backend docker compose up -d --build ``` - MySQL / Redis 带健康检查,应用通过 `depends_on: service_healthy` 等待依赖就绪 - 应用镜像为多阶段构建(`golang:1.26-alpine` → `alpine`),以非 root 用户运行 - 容器内使用 `conf/config.docker.yaml`,`mode=release`、`swagger_enabled=false`;需要在线调试时把该文件里的开关改成 `true` ## 开发约定 - Go 代码统一 `gofmt`;流程中不使用 `swag fmt`(它会与 `gofmt` 对 doc comment 的处理互相覆盖) - 修改 Swagger 注解后必须执行 `go generate ./...`,并把 `docs/` 一起提交;`go test ./...` 会校验文档版本号与 `version.go` 一致 - 请求体使用 `models` 下的 DTO(如 `CreatePostForm`),不要把服务端字段(`post_id`、`author_id`)暴露给前端 - 密码只走 bcrypt;版块相关命名统一用 `classifies` ## 已知限制 - 除登录、注册、刷新 token 外,所有接口都需要 JWT,因此**未登录无法浏览版块和帖子** - 投票记录只存在 Redis;若 Redis 数据全丢,票数会从 0 重新累计(可执行 `./v2ex rebuild` 从 MySQL 重建榜单) - Swagger 输出为 2.0 格式(swag v2 目前仅有 beta / RC) - 除 `pkg/password` 与文档版本校验外暂无单元测试 ## 更多文档 - 后端细节:[`backend/README.md`](backend/README.md)(目录职责、接口文档生成流程、密码存储) - 前端细节:[`frontend/README.md`](frontend/README.md)(环境要求、主题变量) - 在线接口文档:(需 `swagger_enabled: true`)