diff --git a/.gitignore b/.gitignore
index 87f9ddf..2114bd0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,6 +2,7 @@
/data/
# 前端依赖与构建缓存
+/node_modules/
/frontend/node_modules/
/frontend/dist/
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000..db1aa52
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,46 @@
+# 参与贡献
+
+感谢你对姜十三论坛的关注!这是一个开源项目,欢迎提交 Issue 和 Pull Request。
+
+## 开发环境
+
+**要求:** Go 1.26+、Node.js 18+
+
+```powershell
+# Windows:一键启动后端 + 前端热更新
+.\build.ps1 -Target dev
+# 浏览器访问 http://localhost:5173
+```
+
+```bash
+# Linux / macOS
+make dev
+```
+
+## 提交规范
+
+- 一个 PR 只做一件事,保持 diff 小而清晰
+- 前端改动请确认浅色 / 暗色主题下都正常
+- 涉及 UI 变更时,建议在 PR 中附上截图
+
+## 完整构建
+
+发布单二进制前需先构建前端并 embed:
+
+```powershell
+.\build.ps1 # Windows
+make build # Linux / macOS
+```
+
+## 报告问题
+
+在 [Issues](https://git.iioio.com/freefire/jiang13-forum/issues) 中描述:
+
+1. 复现步骤
+2. 期望行为 vs 实际行为
+3. 环境信息(系统、浏览器、Go/Node 版本)
+4. 截图或日志(如有)
+
+## 行为准则
+
+请保持友善、尊重他人。骚扰、歧视或恶意行为不被容忍。
diff --git a/README.md b/README.md
index ba68cc9..152feee 100644
--- a/README.md
+++ b/README.md
@@ -1,68 +1,127 @@
+
+
# 姜十三论坛 Jiang13 Forum
-轻量自用论坛系统,面向小圈子内部交流。与 Gitea 相同,编译为**单个独立 Go 二进制**,前端静态资源 embed 内嵌,内置 SQLite,开箱即用。
+**轻量 · 好看 · 单文件部署的现代化论坛**
-## 项目目录结构
+面向小圈子内部交流,编译为单个 Go 二进制,前端 SPA 内嵌,内置 SQLite,开箱即用。
-```
-js13-forum/
-├── cmd/jiang13/main.go # 程序入口
-├── config/config.go # 命令行参数与配置
-├── model/
-│ ├── models.go # GORM 模型定义
-│ └── db.go # 数据库初始化与自动迁移
-├── service/ # 业务逻辑层
-│ ├── auth.go # 注册/登录/JWT
-│ ├── user.go # 用户资料/头像
-│ ├── board.go # 板块 CRUD
-│ ├── post.go # 帖子/点赞/收藏
-│ ├── comment.go # 楼层评论
-│ ├── backup.go # SQLite 备份
-│ ├── ratelimit.go # 访问限流
-│ └── common.go # 敏感词过滤等
-├── handler/
-│ ├── handlers.go # 前台页面与 API
-│ └── admin.go # 后台管理
-├── middleware/auth.go # JWT 鉴权与限流中间件
-├── router/router.go # 路由注册
-├── embed_static/
-│ ├── embed.go # go:embed 声明
-│ ├── static/css/style.css # 内嵌 CSS
-│ ├── static/js/app.js # 内嵌 JS
-│ └── templates/ # 内嵌 HTML 模板
-├── go.mod
-├── Makefile
-└── README.md
-```
+
+
+[](LICENSE)
+[](go.mod)
+[](frontend/package.json)
+[](#)
+
+[快速开始](#-快速开始) ·
+[界面预览](#-界面预览) ·
+[功能亮点](#-功能亮点) ·
+[参与贡献](CONTRIBUTING.md)
+
+
+
+

+
+
浅色主题 · V2EX/NGA 风格三栏布局 · 虚拟滚动帖列表
+
+
+
+---
+
+## 界面预览
+
+> 论坛用户第一眼看到的是界面。姜十三论坛采用清新绿色主题、高密度信息布局,兼顾桌面与移动端体验。
+
+
+
+
+
+ 浅色主题
+ 左栏板块导航 · 中间帖列表 · 右栏热门/动态/在线
+ |
+
+
+ 暗色主题
+ 一键切换 · 护眼阅读 · 全局色彩自适应
+ |
+
+
+
+
+ 帖子详情
+ 楼层式回复 · 引用回复 · 登录可见内容
+ |
+
+
+ 移动端适配
+ 侧栏自动收起 · 触控友好 · 板块快捷筛选
+ |
+
+
+
+
+
+
登录 / 注册 — 居中卡片式表单,简洁无干扰
+
+
+---
+
+## 功能亮点
+
+### 界面与交互
+
+| 特性 | 说明 |
+|------|------|
+| **三栏布局** | 左栏板块菜单(可折叠)+ 中间虚拟滚动帖列表 + 右栏热门/通知/在线 |
+| **虚拟滚动** | `@tanstack/react-virtual` 驱动帖列表与楼层回复,长列表依然流畅 |
+| **已读 / 未读** | 未读高亮、角标提醒、批量标记已读 |
+| **主题切换** | 浅色 / 暗色一键切换,跟随 `prefers-color-scheme` 与本地记忆 |
+| **响应式** | 平板 / 手机自动收起侧栏,搜索、发帖、登录触手可及 |
+| **高密度排版** | V2EX / NGA 风格信息密度,一屏浏览更多内容 |
+
+### 社区功能
+
+- 用户注册 / 登录(bcrypt + JWT Cookie)
+- 普通用户 / 管理员两级权限,**首个注册用户自动成为管理员**
+- 板块管理、发帖、Markdown / 富文本、标签、置顶
+- 楼层式评论,支持回复指定楼层、@ 高亮、引用回复
+- 点赞、收藏、热门帖、最新动态
+- 管理员后台:删帖、删评论、禁言、SQLite 一键备份
+- 内置敏感词过滤、发帖 / 评论限流
+
+### 部署体验
+
+- **单二进制部署** — 与 Gitea 同款 `go:embed` 打包,无需 Nginx 反代静态资源
+- **零依赖数据库** — SQLite 内建,数据目录 `--data` 一处管理
+- **跨平台** — Windows / Linux / macOS 一键编译
+
+---
## 快速开始
### 1. 编译
-**Windows(推荐,无需 GNU Make):**
+**Windows(推荐):**
```powershell
-# PowerShell
.\build.ps1
-
-# 或双击 / cmd
-build.bat
+# 或双击 build.bat
```
-**Linux / macOS(需 GNU Make):**
+**Linux / macOS:**
```bash
make build
```
-**手动分步(全平台通用):**
+**手动分步(全平台):**
```bash
cd frontend && npm install && npm run build
-cd .. && go build -trimpath -ldflags "-s -w" -o dist/jiang13.exe ./cmd/jiang13
+cd .. && go build -trimpath -ldflags "-s -w" -o dist/jiang13 ./cmd/jiang13
```
-> Windows 自带的 `make` 通常是 Embarcadero MAKE,**不能**识别本项目 Makefile。请用 `.\build.ps1` 或安装 GNU Make(`choco install make`)后再用 `make build`。
+> Windows 自带的 `make` 通常是 Embarcadero MAKE,不能识别本项目 Makefile。请用 `.\build.ps1` 或安装 GNU Make 后再用 `make build`。
跨平台编译:
@@ -84,104 +143,90 @@ cd .. && go build -trimpath -ldflags "-s -w" -o dist/jiang13.exe ./cmd/jiang13
### 3. 首次使用
-1. 浏览器打开 http://localhost:3000/register 注册账号
-2. **第一个注册的用户自动成为管理员**,无需额外命令
-3. 登录后可访问 http://localhost:3000/admin/dashboard 进入后台
+1. 浏览器打开 `http://localhost:3000/register` 注册账号
+2. **第一个注册的用户自动成为管理员**
+3. 登录后访问 `http://localhost:3000/admin/dashboard` 进入后台
-## 启动参数
+### 启动参数
| 参数 | 默认值 | 说明 |
|------|--------|------|
-| `--port` | 3000 | HTTP 监听端口 |
-| `--data` | ./data | 数据目录(SQLite、上传、日志) |
-| `--jwt-secret` | 自动生成 | JWT 签名密钥(留空则自动生成并持久化到 data/.jwt_secret) |
+| `--port` | `3000` | HTTP 监听端口 |
+| `--data` | `./data` | 数据目录(SQLite、上传、日志) |
+| `--jwt-secret` | 自动生成 | JWT 签名密钥(留空则持久化到 `data/.jwt_secret`) |
-## 数据目录说明
+---
+
+## 技术栈
+
+| 层级 | 技术 |
+|------|------|
+| **后端** | Go 1.26 · Gin · GORM · SQLite |
+| **前端** | React 18 · Radix UI · Tailwind CSS · TanStack Virtual |
+| **构建** | Vite → `go:embed` 内嵌 SPA,单二进制发布 |
+| **认证** | bcrypt · JWT Cookie |
+
+---
+
+## 前端开发
+
+日常改前端**不需要**重新 `npm run build` 或 `go build`,Vite 开发服务器支持秒级热更新(HMR,热模块替换):
+
+```powershell
+.\build.ps1 -Target dev # Windows
+make dev # Linux / macOS
+```
+
+浏览器访问 `http://localhost:5173`,API 自动代理到 `http://localhost:3000`。
+
+**何时需要完整构建:**
+
+- 修改 Go 代码、HTML 模板、embed 静态资源 → `go build` / `make build`
+- 发布单二进制前 → `npm run build` + `make build`
+
+> 直接访问 `:3000` 看到的是上次 build 嵌入的前端;开发时请用 `:5173`。
+
+---
+
+## 项目结构
+
+```
+jiang13-forum/
+├── cmd/jiang13/ # 程序入口
+├── config/ # 命令行参数与配置
+├── model/ # GORM 模型与数据库迁移
+├── service/ # 业务逻辑(认证、帖子、评论…)
+├── handler/ # HTTP 处理器(前台 + 后台)
+├── middleware/ # JWT 鉴权、在线状态
+├── router/ # 路由注册
+├── embed_static/ # go:embed 内嵌的 SPA 与模板
+├── frontend/ # React 源码(Vite 构建)
+├── docs/screenshots/ # README 界面截图
+└── scripts/ # 开发辅助脚本
+```
+
+---
+
+## 数据目录
```
data/
├── jiang13.db # SQLite 主数据库
├── jiang13.log # 运行日志
-├── filter_words.txt # 敏感词配置(可编辑)
+├── filter_words.txt # 敏感词配置
├── .jwt_secret # JWT 密钥(自动生成)
-├── uploads/avatars/ # 用户头像(运行时写入,非 embed)
-└── jiang13_backup_*.db # 后台导出的备份文件
+├── uploads/avatars/ # 用户头像
+└── jiang13_backup_*.db # 后台导出的备份
```
-## 修改前端并重新打包(Gitea 同款流程)
+---
-1. 编辑 `embed_static/templates/` 下的 HTML 模板
-2. 编辑 `embed_static/static/css/` 或 `static/js/` 下的静态资源
-3. 重新编译:`make build`
-4. 替换旧二进制,重启服务
+## 参与贡献
-前端资源通过 `//go:embed` 编译进二进制,**运行时不需要单独的前端文件夹**。只有用户上传的头像保存在 `--data` 目录。
+欢迎提交 Issue 和 Pull Request!详见 [CONTRIBUTING.md](CONTRIBUTING.md)。
-```go
-// embed_static/embed.go
-//go:embed static/*
-var staticFS embed.FS
-
-//go:embed templates/*
-var templatesFS embed.FS
-```
-
-## 功能概览
-
-- 用户注册/登录(bcrypt + JWT Cookie)
-- 普通用户 / 管理员两级权限
-- 板块管理、发帖、富文本 HTML、标签、置顶
-- 楼层式评论,支持回复指定楼层
-- 点赞、收藏
-- 管理员:删帖、删评论、禁言、SQLite 备份
-- 内置敏感词过滤、发帖/评论限流
-
-## 技术栈
-
-- **后端**:Go 1.26 + Gin + GORM + SQLite
-- **前端**:React 18 + Arco Design + TanStack Virtual(虚拟滚动)
-- **打包**:Vite 构建 → `go:embed` 内嵌 SPA,与 Gitea 同款单二进制
-
-## 前端开发(热更新 HMR)
-
-日常改前端**不需要**重新 `npm run build` 或 `go build`,用 Vite 开发服务器即可秒级热更新:
-
-```bash
-# 一键启动:Go 后端 + Vite 热更新(推荐)
-.\build.ps1 -Target dev # Windows
-make dev # Linux / macOS(需 GNU Make)
-
-# 浏览器访问 http://localhost:5173
-# API 自动代理到 http://localhost:3000
-```
-
-也可开两个终端分别启动:
-
-```bash
-# 终端 1:仅后端
-.\build.ps1 -Target run # 或 make run
-
-# 终端 2:仅前端热更新
-cd frontend && npm install && npm run dev
-```
-
-**何时才需要完整构建:**
-
-- 改 Go 代码、模板、embed 静态资源 → `go build` / `make build`
-- 发布单二进制前 → `npm run build` + `make build`(前端产物写入 `embed_static/static/spa/` 后 embed 进二进制)
-
-> 若直接访问 `:3000`,看到的是**上次 build 嵌入**的前端,不会有热更新。开发时请用 `:5173`。
-
-### 前端特性(高密度 V2EX/NGA 风格)
-
-| 特性 | 实现 |
-|------|------|
-| 三栏布局 | 左栏板块菜单(可折叠)+ 中间虚拟滚动帖列表 + 右栏热门/通知/在线 |
-| 虚拟滚动 | `@tanstack/react-virtual` 帖列表 & 楼层回复 |
-| 已读/未读 | 未读高亮 + 角标 + 批量标记已读 |
-| 主题 | 浅色/暗黑一键切换,平板/手机自动收起侧栏 |
-| 交互 | 滚动加载更多、快捷回帖、楼层锚点、引用回复、@高亮 |
+---
## 许可证
-MIT — 自用随意,欢迎修改。
+[MIT](LICENSE) — 自由使用、修改与分发。
diff --git a/cmd/jiang13/main.go b/cmd/jiang13/main.go
index 97d747f..a238376 100644
--- a/cmd/jiang13/main.go
+++ b/cmd/jiang13/main.go
@@ -11,9 +11,9 @@ import (
"syscall"
"time"
- "github.com/jiang13/forum/config"
- "github.com/jiang13/forum/model"
- "github.com/jiang13/forum/router"
+ "git.iioio.com/freefire/jiang13-forum/config"
+ "git.iioio.com/freefire/jiang13-forum/model"
+ "git.iioio.com/freefire/jiang13-forum/router"
)
func main() {
diff --git a/docs/screenshots/compose.png b/docs/screenshots/compose.png
new file mode 100644
index 0000000..5ee246d
Binary files /dev/null and b/docs/screenshots/compose.png differ
diff --git a/docs/screenshots/home-dark.png b/docs/screenshots/home-dark.png
new file mode 100644
index 0000000..729e952
Binary files /dev/null and b/docs/screenshots/home-dark.png differ
diff --git a/docs/screenshots/home-light.png b/docs/screenshots/home-light.png
new file mode 100644
index 0000000..2d27755
Binary files /dev/null and b/docs/screenshots/home-light.png differ
diff --git a/docs/screenshots/mobile-home.png b/docs/screenshots/mobile-home.png
new file mode 100644
index 0000000..8b63ce1
Binary files /dev/null and b/docs/screenshots/mobile-home.png differ
diff --git a/docs/screenshots/post-detail.png b/docs/screenshots/post-detail.png
new file mode 100644
index 0000000..a8239ed
Binary files /dev/null and b/docs/screenshots/post-detail.png differ
diff --git a/go.mod b/go.mod
index 8e9235b..b8c8965 100644
--- a/go.mod
+++ b/go.mod
@@ -1,4 +1,4 @@
-module github.com/jiang13/forum
+module git.iioio.com/freefire/jiang13-forum
go 1.26
diff --git a/handler/admin.go b/handler/admin.go
index c544b32..609f1af 100644
--- a/handler/admin.go
+++ b/handler/admin.go
@@ -7,8 +7,8 @@ import (
"strings"
"github.com/gin-gonic/gin"
- "github.com/jiang13/forum/model"
- "github.com/jiang13/forum/service"
+ "git.iioio.com/freefire/jiang13-forum/model"
+ "git.iioio.com/freefire/jiang13-forum/service"
)
// --- 后台管理页面 ---
diff --git a/handler/api.go b/handler/api.go
index 2ca13a7..b29a747 100644
--- a/handler/api.go
+++ b/handler/api.go
@@ -5,8 +5,8 @@ import (
"strconv"
"github.com/gin-gonic/gin"
- "github.com/jiang13/forum/model"
- "github.com/jiang13/forum/service"
+ "git.iioio.com/freefire/jiang13-forum/model"
+ "git.iioio.com/freefire/jiang13-forum/service"
)
// APIMe 当前登录用户
diff --git a/handler/handlers.go b/handler/handlers.go
index 9c1c0fd..016eda5 100644
--- a/handler/handlers.go
+++ b/handler/handlers.go
@@ -6,10 +6,10 @@ import (
"strings"
"github.com/gin-gonic/gin"
- "github.com/jiang13/forum/config"
- "github.com/jiang13/forum/middleware"
- "github.com/jiang13/forum/model"
- "github.com/jiang13/forum/service"
+ "git.iioio.com/freefire/jiang13-forum/config"
+ "git.iioio.com/freefire/jiang13-forum/middleware"
+ "git.iioio.com/freefire/jiang13-forum/model"
+ "git.iioio.com/freefire/jiang13-forum/service"
)
// Handlers 聚合所有 HTTP 处理器
diff --git a/middleware/auth.go b/middleware/auth.go
index e8f82f4..91757f9 100644
--- a/middleware/auth.go
+++ b/middleware/auth.go
@@ -6,8 +6,8 @@ import (
"strings"
"github.com/gin-gonic/gin"
- "github.com/jiang13/forum/model"
- "github.com/jiang13/forum/service"
+ "git.iioio.com/freefire/jiang13-forum/model"
+ "git.iioio.com/freefire/jiang13-forum/service"
)
const (
diff --git a/middleware/presence.go b/middleware/presence.go
index ef85529..f596b77 100644
--- a/middleware/presence.go
+++ b/middleware/presence.go
@@ -5,7 +5,7 @@ import (
"encoding/hex"
"github.com/gin-gonic/gin"
- "github.com/jiang13/forum/service"
+ "git.iioio.com/freefire/jiang13-forum/service"
)
const VisitorCookieName = "j13_vid"
diff --git a/router/router.go b/router/router.go
index cbd9167..a70cf91 100644
--- a/router/router.go
+++ b/router/router.go
@@ -6,11 +6,11 @@ import (
"time"
"github.com/gin-gonic/gin"
- "github.com/jiang13/forum/config"
- "github.com/jiang13/forum/embed_static"
- "github.com/jiang13/forum/handler"
- "github.com/jiang13/forum/middleware"
- "github.com/jiang13/forum/service"
+ "git.iioio.com/freefire/jiang13-forum/config"
+ "git.iioio.com/freefire/jiang13-forum/embed_static"
+ "git.iioio.com/freefire/jiang13-forum/handler"
+ "git.iioio.com/freefire/jiang13-forum/middleware"
+ "git.iioio.com/freefire/jiang13-forum/service"
)
func Setup(cfg *config.Config) (*gin.Engine, error) {
diff --git a/scripts/capture-screenshots.mjs b/scripts/capture-screenshots.mjs
new file mode 100644
index 0000000..7d6fa04
--- /dev/null
+++ b/scripts/capture-screenshots.mjs
@@ -0,0 +1,86 @@
+import { chromium } from 'playwright';
+import { mkdir } from 'node:fs/promises';
+import path from 'node:path';
+import { fileURLToPath } from 'node:url';
+
+const __dirname = path.dirname(fileURLToPath(import.meta.url));
+const outDir = path.join(__dirname, '..', 'docs', 'screenshots');
+const base = process.env.J13_URL || 'http://localhost:8080';
+
+async function shot(page, name, opts = {}) {
+ const file = path.join(outDir, name);
+ await page.screenshot({ path: file, type: 'png', ...opts });
+ console.log('saved', file);
+}
+
+async function setTheme(page, theme) {
+ await page.addInitScript((t) => {
+ localStorage.setItem('j13-theme', t);
+ document.documentElement.classList.toggle('dark', t === 'dark');
+ document.documentElement.style.colorScheme = t;
+ }, theme);
+}
+
+const browser = await chromium.launch();
+const context = await browser.newContext({ deviceScaleFactor: 2 });
+
+try {
+ await mkdir(outDir, { recursive: true });
+
+ // 首页 · 浅色
+ {
+ const page = await context.newPage();
+ await page.setViewportSize({ width: 1440, height: 900 });
+ await setTheme(page, 'light');
+ await page.goto(`${base}/`, { waitUntil: 'networkidle' });
+ await page.waitForTimeout(800);
+ await shot(page, 'home-light.png');
+ await page.close();
+ }
+
+ // 首页 · 暗色
+ {
+ const page = await context.newPage();
+ await page.setViewportSize({ width: 1440, height: 900 });
+ await setTheme(page, 'dark');
+ await page.goto(`${base}/`, { waitUntil: 'networkidle' });
+ await page.waitForTimeout(800);
+ await shot(page, 'home-dark.png');
+ await page.close();
+ }
+
+ // 帖子详情
+ {
+ const page = await context.newPage();
+ await page.setViewportSize({ width: 1440, height: 900 });
+ await setTheme(page, 'light');
+ await page.goto(`${base}/post/2`, { waitUntil: 'networkidle' });
+ await page.waitForTimeout(1000);
+ await shot(page, 'post-detail.png');
+ await page.close();
+ }
+
+ // 发帖编辑器
+ {
+ const page = await context.newPage();
+ await page.setViewportSize({ width: 1440, height: 900 });
+ await setTheme(page, 'light');
+ await page.goto(`${base}/compose`, { waitUntil: 'networkidle' });
+ await page.waitForTimeout(800);
+ await shot(page, 'compose.png');
+ await page.close();
+ }
+
+ // 移动端
+ {
+ const page = await context.newPage();
+ await page.setViewportSize({ width: 390, height: 844 });
+ await setTheme(page, 'light');
+ await page.goto(`${base}/`, { waitUntil: 'networkidle' });
+ await page.waitForTimeout(800);
+ await shot(page, 'mobile-home.png', { fullPage: true });
+ await page.close();
+ }
+} finally {
+ await browser.close();
+}
diff --git a/service/auth.go b/service/auth.go
index fa82368..3c23b1a 100644
--- a/service/auth.go
+++ b/service/auth.go
@@ -5,7 +5,7 @@ import (
"time"
"github.com/golang-jwt/jwt/v5"
- "github.com/jiang13/forum/model"
+ "git.iioio.com/freefire/jiang13-forum/model"
)
const TokenExpire = 7 * 24 * time.Hour
diff --git a/service/board.go b/service/board.go
index 9cdd4ca..0358ab2 100644
--- a/service/board.go
+++ b/service/board.go
@@ -3,7 +3,7 @@ package service
import (
"errors"
- "github.com/jiang13/forum/model"
+ "git.iioio.com/freefire/jiang13-forum/model"
)
type BoardService struct{}
diff --git a/service/comment.go b/service/comment.go
index 9c0476e..f7012ba 100644
--- a/service/comment.go
+++ b/service/comment.go
@@ -6,7 +6,7 @@ import (
"net/url"
"strings"
- "github.com/jiang13/forum/model"
+ "git.iioio.com/freefire/jiang13-forum/model"
)
type CommentService struct {
diff --git a/service/online.go b/service/online.go
index 10460a5..3c06ba4 100644
--- a/service/online.go
+++ b/service/online.go
@@ -4,7 +4,7 @@ import (
"sync"
"time"
- "github.com/jiang13/forum/model"
+ "git.iioio.com/freefire/jiang13-forum/model"
)
const onlineTTL = 5 * time.Minute
diff --git a/service/post.go b/service/post.go
index f8a68c7..9ac6811 100644
--- a/service/post.go
+++ b/service/post.go
@@ -4,7 +4,7 @@ import (
"errors"
"strings"
- "github.com/jiang13/forum/model"
+ "git.iioio.com/freefire/jiang13-forum/model"
"gorm.io/gorm"
)
diff --git a/service/user.go b/service/user.go
index 5e516c1..3f8c3d1 100644
--- a/service/user.go
+++ b/service/user.go
@@ -10,7 +10,7 @@ import (
"strings"
"time"
- "github.com/jiang13/forum/model"
+ "git.iioio.com/freefire/jiang13-forum/model"
)
type UserService struct {