- Go 52.2%
- Vue 40%
- JavaScript 4.6%
- CSS 2.9%
- Dockerfile 0.3%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
- 多阶段 Dockerfile:node:24-alpine 构建前端 → golang:1.27-alpine 编译纯静态 Go 二进制(SQLite 无 CGO)→ alpine:3.20 运行镜像 - 运行镜像内置前端产物并由后端托管 SPA,包含 CA 证书与 Asia/Shanghai 时区 - docker-compose.yml:一键启动、端口映射、健康检查(wget /api/system/info)、SQLite 与上传文件持久化到命名卷 /data - .dockerignore:排除 node_modules/dist/数据库/上传文件等,缩小构建上下文 - 已验证:docker build 成功、容器内 API 与 SPA 正常响应、docker compose up 数据落盘 - README 补充 Docker 部署章节(含 JWT_SECRET 生产配置说明) |
||
| config | ||
| database | ||
| handlers | ||
| middleware | ||
| models | ||
| router | ||
| services | ||
| storage/uploads | ||
| validators | ||
| web | ||
| .dockerignore | ||
| .gitignore | ||
| collect_test.go | ||
| dicts_test.go | ||
| docker-compose.yml | ||
| Dockerfile | ||
| go.mod | ||
| go.sum | ||
| main.go | ||
| main_test.go | ||
| passkey_test.go | ||
| profile_test.go | ||
| rbac_test.go | ||
| README.md | ||
团队档案管理系统(TravelManager)
一个面向团队运营场景的多角色 Web 管理后台:旅行社、合同、团队预订单、消费核算、档案归档全流程管理。内置用户与角色权限体系(管理员 / 旅行社 / 负责人),支持 SMTP 邮件、短信(短信宝 / 阿里云 SNS / Webhook)、多云端存储(本地 / 腾讯云 COS / 又拍云 / 阿里云 OSS / S3 兼容)以及第三方登录(彩虹聚合登录 / OauthGo REST API),白天 / 黑夜 / 自动三档主题。
系统名称、水印、存储、发信等均可通过「系统设置」动态配置,不绑定任何特定地区或景区。
功能特性
- 用户与角色权限:管理员、旅行社、负责人三种角色,前端菜单与后端接口双重收口,数据严格隔离
- 旅行社管理:合作旅行社、老板 / 计调联系方式维护,手机号与邮箱格式校验,CSV 导入导出
- 合同管理:合同拍照 / PDF 上传存档,状态流转与回收站恢复,CSV 导入导出
- 团队统计:预订单录入、抵达 / 离开日期、带队负责人、消费明细自动核算总花费;团型与消费分类均可自定义,支持高德地图选点记录团队地点
- 消费收集链接:团队可生成公开收集链接发给他人补录消费(含票据 / 照片附件),可随时开启 / 关闭 / 重置;附件大小与格式在系统设置中统一控制
- 档案归档:团队附件分类归档(预订单 / 合同 / 票据 / 行程单 / 凭证),Word 简报一键导出
- 数据仪表盘:按年 / 月统计团型、客源地、团队数与总消费,按角色自动过滤
- 高德地图选点:在「系统设置」配置高德 Web JS API Key 后,团队录入与详情页支持地图搜索 / 点击选点并自动反查地名
- 通知接入:SMTP 邮件(STARTTLS / 隐式 SSL)、短信(短信宝 / 阿里云短信签名直连 / 通用 Webhook)
- 云存储:本地磁盘、腾讯云 COS、又拍云、阿里云 OSS、S3 兼容存储(AWS / MinIO / 华为云 OBS / 七牛云等)五种存储可切换,上传下载自动分发
- 个人主页:每个用户可在「个人主页」维护姓名 / 昵称 / 手机号 / 邮箱 / 头像,绑定或解绑 QQ / 微信 / 微博等第三方登录、绑定 / 删除通行密钥(Passkey),并修改或首次设置登录密码
- 注册与找回密码:系统首个用户注册即成为超级管理员;忘记密码时通过绑定邮箱 / 手机号发送验证码自助重置
- 通行密钥登录:支持 WebAuthn / FIDO2 无密码登录(设备指纹 / 人脸 / 安全密钥),登录页与个人主页均可绑定
- 第三方登录:支持「彩虹聚合登录」与「OauthGo REST API」两种接入方式,统一对接 QQ / 微信 / 微博等平台,自动注册账号并绑定角色;登录页与个人主页展示的平台由系统设置决定
- 审计日志:登录、查看、下载、增删改、导入导出全流程留痕
- 主题系统:白天 / 黑夜 / 跟随系统三档切换,品牌色与设计系统可全局定制
技术栈
后端
- Go 1.27 + Gin
- GORM + SQLite(纯 Go 驱动,无需 CGO)
- JWT(HS256)+ bcrypt
- 标准库
net/smtp发信,HTTP 签名直连云存储与短信网关
前端
- Vue 3 + Vite
- Element Plus
- Pinia 状态管理
- Vue Router
目录结构
TravelManager/
├── main.go # 服务入口
├── config/ # 配置加载(环境变量覆盖)
├── database/ # SQLite 初始化、自动建表、超级管理员保障
├── models/ # 数据模型(用户 / 旅行社 / 合同 / 团队 / 消费 / 附件 / 日志 / 设置)
├── middleware/ # JWT 认证、角色鉴权、CORS、审计
├── handlers/ # 全部 API handler(含 RBAC 数据作用域)
├── services/ # Word 导出、CSV、SMTP、短信、云存储、第三方登录
├── validators/ # 手机号 / 邮箱自定义校验
├── router/ # 路由注册
├── web/ # Vue 3 前端工程
│ └── src/
│ ├── views/ # 登录、仪表盘、旅行社、合同、团队、用户、日志、设置
│ ├── stores/ # 认证 / 系统名 / 主题
│ └── api/ # 接口封装
└── storage/uploads/ # 本地存储目录
快速开始
后端
go run .
默认监听 http://localhost:8080。系统不内置任何账号:首次启动后,第一个注册的用户自动成为超级管理员(注册入口仅在系统尚无用户时开放,之后由管理员统一创建账号)。
前端(开发模式)
cd web
npm install
npm run dev
前端开发服务器默认 http://localhost:5173(Vite 已配置代理到后端)。构建后后端会自动托管 web/dist:
cd web && npm run build
cd .. && go run .
环境变量
| 变量 | 默认值 | 说明 |
|---|---|---|
PORT |
8080 |
服务端口 |
DB_PATH |
travelmanager.db |
SQLite 数据库文件 |
JWT_SECRET |
travel-manager-dev-secret |
JWT 签名密钥(生产必改) |
UPLOAD_DIR |
storage/uploads |
本地存储根目录 |
Docker 部署
项目内置 Dockerfile(多阶段构建:前端 → Go 编译 → Alpine 运行镜像)与 docker-compose.yml,一条命令启动:
docker compose up -d --build
- 访问
http://localhost:8080;SQLite 数据库与上传文件持久化在命名卷travelmanager-data(挂载于容器/data) - 生产环境务必通过
.env或环境变量覆盖JWT_SECRET:
# .env
JWT_SECRET=换成足够长的随机字符串
docker compose up -d # 应用新密钥后重建
docker compose logs -f # 查看日志
docker compose down # 停止(数据保留在卷中)
docker compose down -v # 停止并删除数据卷
镜像包含 CA 证书与 Asia/Shanghai 时区,可直接访问高德地图、短信、邮件等外部服务。
角色与权限矩阵
| 能力 | 管理员 | 旅行社 | 负责人 |
|---|---|---|---|
| 用户管理 / 系统设置 / 操作日志 | ✅ | ❌ | ❌ |
| 新增 / 删除旅行社 | ✅ | ❌ | ❌ |
| 查看旅行社 | 全部 | 仅本社 | 仅本人带队关联 |
| 合同管理 | 全部 | 仅本社 | 仅本人带队关联(只读) |
| 团队录入 / 修改 / 归档 | 全部 | 仅本社 | 仅本人带队团队 |
| 团队删除 | ✅ | ✅(仅本社) | ❌ |
| 消费录入 / 修改 | ✅ | ✅(仅本社) | ✅(仅本人带队) |
| 附件上传 / 下载 | ✅ | ✅(仅本社) | ✅(仅本人带队) |
| CSV 导入 | ✅ | ✅(仅本社,导入旅行社除外) | ❌ |
| CSV 导出 | 全部 | 仅本社 | 仅本人带队 |
| 统计仪表盘 | 全部 | 仅本社 | 仅本人带队 |
超级管理员:系统首个注册用户自动获得(无内置账号),拥有全部权限;不可通过接口创建、提权、降级、停用或删除,防止误操作导致系统失守。
负责人角色的数据范围由其账号「手机号 / 邮箱」与团队档案中的「带队负责人手机号 / 邮箱」匹配决定;第三方登录自动注册的账号默认负责人角色,管理员需为其补充手机号 / 邮箱以完成团队绑定。
用户管理
- 管理员在「用户管理」页新增 / 编辑 / 停用 / 删除用户,可为旅行社角色绑定具体旅行社
- 新增用户时校验用户名唯一、密码 ≥ 6 位、手机号(11 位或座机)与邮箱格式
- 管理员可重置任意用户密码;用户可在「个人主页」维护个人资料、上传头像、绑定 / 解绑第三方登录、绑定 / 删除通行密钥,并修改密码(第三方账号首次设置密码无需原密码)
- 忘记密码:登录页「忘记密码?」→ 输入用户名 / 手机号 / 邮箱,向绑定邮箱(SMTP)或手机(短信)发送 6 位验证码 → 校验后设置新密码
- 禁止删除 / 停用 / 降级自己的账号,防止误操作导致管理员失联
- 超级管理员为保留角色,任何接口均不可创建、分配、降级、停用或删除该角色;系统存在用户后注册入口关闭,账号由管理员统一创建
通知接入
SMTP 邮件
在「系统设置 → SMTP 邮件发信」配置后保存,可发送测试邮件验证。
| 配置项 | 说明 |
|---|---|
| SMTP 服务器 / 端口 | 如 smtp.example.com、25 / 465 / 587 |
| 用户名 / 密码 | 发信账号与密码(或授权码) |
| 发件人地址 | 默认取用户名 |
| 加密方式 | STARTTLS(587)/ SSL(465)/ 无加密(25) |
短信(SNS)
「系统设置 → 短信发送」支持四种模式:
| 模式 | 说明 |
|---|---|
| 关闭 | 不发送 |
| 短信宝 | HTTP GET 直发 http://api.smsbao.com/sms,配置账号 / 密码,内容为「前缀 + 验证码」 |
| 阿里云短信 | 使用 RPC 签名(HMAC-SHA1)直连 dysmsapi.aliyuncs.com 的 SendSms 接口 |
| Webhook | 向自定义网关 POST JSON(含 phone、sign_name、template_code、template_params) |
短信宝在设置中「账号」对应 AccessKey 字段、「密码」对应 AccessSecret 字段(接口用其 MD5 值),「短信内容前缀」对应 SignName 字段;接口返回 0 表示发送成功,否则为错误码。
阿里云短信需配置 AccessKey / AccessSecret、签名名称与模板 Code;测试短信固定验证码 123456。
云存储
「系统设置 → 文件存储」支持五种类型,上传后文件路径 / 访问 URL 自动写入档案,下载时自动分发:
| 类型 | 配置 | 说明 |
|---|---|---|
| 本地存储 | 无需配置 | 保存至 UPLOAD_DIR |
| 腾讯云 COS | SecretId / SecretKey / Bucket / Region | q-signature 签名直传 |
| 又拍云 | 服务名 / 操作员 / 操作员密码 / 访问域名 | REST API Upyun 签名直传 |
| 阿里云 OSS | Bucket / Endpoint / AccessKey ID / Secret | OSS V1 签名直传 |
| S3 兼容存储 | Bucket / Endpoint / Region / AccessKey / SecretKey / 访问域名 / Path-Style | AWS Signature V4 签名直传,兼容 AWS S3、MinIO、华为云 OBS、七牛云、Cloudflare R2、百度 BOS 等 |
当前云存储与短信网关均以原生 HTTP 签名实现,不依赖云厂商 SDK(离线环境亦可编译运行)。如需使用官方 SDK(分片上传、断点续传等高级能力),可在有网环境引入官方 Go SDK 替换
services/storage.go与services/sms.go中的对应实现。
第三方登录
支持两种接入方式,均使用同一套 connect.php 兼容协议,仅在「网关地址」上不同:
- 彩虹聚合登录:官方聚合登录网关,注册应用后获得
AppID/AppKey - OauthGo REST API:自建兼容网关(参考 https://github.com/xiaoman1221/OauthGo),部署后统一对接 QQ、微信、微博等平台
部署与配置
- 在「系统设置 → 第三方登录」选择接入方式(彩虹聚合登录 / OauthGo REST API)
- 在对应平台创建应用,获得
AppID与AppKey - 在平台应用配置中添加本系统的回调地址白名单:
https://your-domain/api/auth/oauth/callback - 在本系统「系统设置 → 第三方登录」填写:
| 配置项 | 说明 |
|---|---|
| 启用第三方登录 | 开启后登录页显示第三方登录入口 |
| 登录平台 | 勾选 QQ / 微信 / 微博等平台,登录页与个人主页据此展示入口 |
| 接入方式 | 彩虹聚合登录 / OauthGo REST API(协议一致,仅网关地址不同) |
| AppID / AppKey | 应用凭据 |
| 网关地址 | 彩虹聚合登录如 https://api.1v.fit;OauthGo REST API 如 https://o.1v.fit |
| 回调地址 | 系统根据当前域名自动拼接(https://当前域名/api/auth/oauth/callback),不可手动配置;请将页面展示的地址加入平台回调白名单 |
协议流程
- 用户点击第三方登录 → 本系统请求
GET {网关}/connect.php?act=login&appid=..&appkey=..&type=qq|wechat|..&redirect_uri=..获取跳转地址 - 平台授权后回调
redirect_uri?state=..&code=..&sign=.. - 本系统用一次性
code请求GET {网关}/connect.php?act=callback&appid=..&appkey=..&type=..&code=..换取用户信息(social_uid、nickname、faceimg等) - 按
provider + social_uid自动注册或登录(新账号默认「负责人」角色;系统尚无用户时,首个自动注册账号为超级管理员),签发站内 JWT 并回跳前端
账号绑定
- 已登录用户在「个人主页 → 第三方登录绑定」可为当前账号绑定多个平台(生成一次性票据后跳转授权,回调自动归属到当前账号)
- 绑定后可用对应平台快捷登录;解绑受保护:无登录密码且仅剩一种登录方式时需先设置密码再解绑
- 第三方登录自动注册的账号无密码,可在个人主页「设置密码」后使用账号密码登录
API 一览
| 模块 | 方法 & 路径 | 权限 |
|---|---|---|
| 系统 | GET /api/system/info |
公开 |
| 系统字典 | GET /api/system/dicts(团型、消费分类、高德 Key) |
登录 |
| 登录 | POST /api/auth/login |
公开 |
| 注册 | POST /api/auth/register |
公开(仅系统无用户时,首个注册用户为超级管理员) |
| 找回密码 | POST /api/auth/forgot-password、POST /api/auth/reset-password |
公开 |
| 通行密钥登录 | GET /api/auth/passkey/begin-login、POST /api/auth/passkey/complete-login |
公开 |
| 第三方登录 | GET /api/auth/oauth/authorize?type=qq |
公开 |
| 第三方登录 | GET /api/auth/oauth/callback?type=..&code=.. |
公开 |
| 当前用户 | GET /api/auth/me、POST /api/auth/logout |
登录 |
| 个人主页 | PUT /api/users/me、POST /api/users/me/avatar、PUT /api/users/me/password |
登录 |
| 第三方绑定 | POST /api/users/me/oauth/ticket、DELETE /api/users/me/oauth/:provider |
登录 |
| 通行密钥 | GET /api/passkeys、POST /api/passkeys/begin-register、POST /api/passkeys/complete-register、DELETE /api/passkeys/:id |
登录 |
| 用户管理 | GET/POST /api/users、PUT/DELETE /api/users/:id |
管理员 |
| 角色字典 | GET /api/users/roles |
管理员 |
| 重置密码 | POST /api/users/:id/reset-password |
管理员 |
| 旅行社 | GET/POST /api/agents、GET/PUT/DELETE /api/agents/:id |
管理员(查看按角色过滤) |
| 合同 | GET/POST /api/contracts、GET/PUT/DELETE /api/contracts/:id、POST /api/contracts/:id/restore |
按角色 |
| 团队 | GET/POST /api/teams、GET/PUT/DELETE /api/teams/:id、POST /api/teams/:id/archive |
按角色 |
| 消费 | POST /api/teams/:id/expenses、PUT/DELETE /api/teams/:id/expenses/:eid |
按角色 |
| 消费附件 | POST /api/teams/:id/expenses/:eid/attachments、GET .../download、DELETE ... |
按角色 |
| 消费收集 | PUT /api/teams/:id/collect |
按角色 |
| 公开收集 | GET /api/public/collect/:token、POST /api/public/collect/:token/expenses |
公开(凭令牌) |
| 附件 | POST /api/teams/:id/attachments、GET /api/teams/:id/attachments/:aid/download |
按角色 |
| 导出 | GET /api/teams/:id/export(Word)、GET /api/{agents,contracts,teams}/export(CSV) |
按角色 |
| 导入 | POST /api/{agents,contracts,teams}/import |
管理员 / 旅行社 |
| 统计 | GET /api/stats/summary?year=..&month=.. |
按角色 |
| 设置 | GET/PUT /api/settings、POST /api/settings/test-mail、POST /api/settings/test-sms |
管理员 |
| 日志 | GET /api/logs |
管理员 |
响应统一为 {"code": 0, "message": "ok", "data": ...};业务错误返回对应 HTTP 状态码与 message。
CSV 导入导出
导入文件需为 UTF-8 编码(建议带 BOM),表头见导出文件的列名;系统按表头中文名匹配,自动跳过手机号 / 邮箱不合法的行并返回错误明细。团队导入支持日期(YYYY-MM-DD)、状态中文(预订中 / 接待中 / 已归档)与旅行社名称自动匹配。
测试
export PATH="$HOME/sdk/go1.27.0/bin:$PATH"
export GOPROXY=off GOSUMDB=off GOFLAGS=-mod=mod
go test ./...
测试覆盖:登录与会话、团队 / 消费 / 统计 / 导出全流程、RBAC 权限隔离(角色越权 403、数据范围过滤、停用账号拦截)、设置持久化、短信(短信宝 Mock 网关)、第三方登录全链路(内存 Mock 网关)。
已知限制与后续计划
- 云存储与短信使用原生 HTTP 签名实现,未接入云厂商官方 SDK(分片 / 断点续传等高级能力可在有网环境引入后替换)
- 第三方登录回调中的
sign参数暂未做本地二次校验,依赖一次性code与 HTTPS 保证安全 - 用户头像与 OAuth 头像未同步至页面展示,可后续补充
- 通行密钥(WebAuthn)依赖浏览器安全上下文:需通过 HTTPS 或 localhost 访问;RPID 取当前访问域名,更换域名后需重新绑定
- 短信模板参数目前以 JSON 字符串透传,按需扩展站内通知场景(如密码重置、团队归档提醒)