# Decision **Repository Path**: karentwan/decision ## Basic Information - **Project Name**: Decision - **Description**: 决策天平 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-07-20 - **Last Updated**: 2026-09-06 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 决策帮手 · Decision Helper ⚖️ > 遇到选择,不要急着拍脑袋。把优缺点、关键维度都摊开来,让分数和可视化帮你做决定。 一个全栈决策辅助工具:加权优缺点 / 决策矩阵 + 账号系统 + 云端同步 + 可选的 AI 辅助。 部署形态为**单一二进制**(Go embed 前端 + SQLite),拷一个文件、起一个进程就能跑。 ## ✨ 功能 ### 决策建模 1. **加权优缺点** —— 「要不要做这件事」类的二选一 - 每条优/缺点打 1-10 权重;实时计算净偏 + 信心度 - 可视化:⚖️ 权衡天平、📊 双柱状对比、🎯 信心度仪表 2. **决策矩阵** —— 「在多个选项中选一个」 - 自定义维度(含权重);总分 = Σ(维度权重 × 选项得分) - 可视化:🌡️ 热力表格、📡 多选项雷达图、🏅 排名条形图 ### 平台能力 - 🔐 **账号系统**:注册/登录(JWT),决策数据云端同步、多设备可见 - 📱 **响应式**:Mobile-First,手机/平板/桌面通用;Modal 手机底部弹出 - 🤖 **AI 辅助(可选)**:管理员配置 DeepSeek key 后,用户可在设置页开启: - 新建决策时根据标题智能预填优缺点 / 维度 - 决策填写完成后生成一段自然语言总结 ## 🏗️ 架构 ``` ┌──────────────────────────────────────────────┐ │ 单一 Go 二进制 │ │ ├── //go:embed dist/ ← 前端构建产物 │ │ ├── /api/* → REST API(鉴权+业务) │ │ ├── /* → SPA(静态+fallback) │ │ └── decisions.db SQLite(WAL) │ └──────────────────────────────────────────────┘ ``` - 同源部署,无 CORS;前端 `/api/*` 直接命中 Go - SQLite 单文件零配置;JWT 密钥首次生成并持久化到 `.secret` - AI 功能:所有用户共用一个 DeepSeek key(管理员买单),用户层独立开关 ## 📁 项目结构 ``` Decision/ ├── frontend/ 独立前端项目(Vite + React + TS + Tailwind) │ ├── src/ │ ├── index.html package.json vite.config.ts tsconfig*.json tailwind.config.js │ └── dist/ (vite build 产物,gitignored) ├── backend/ 独立 Go module │ ├── main.go go.mod go.sum │ ├── internal/ (auth / config / decisions / llm / server / settings / store) │ ├── config.example.toml │ └── dist/ (build 脚本从 frontend/ 拷贝来,被 //go:embed) └── scripts/ 工具脚本(build/run + 冒烟测试) ``` ## 📦 环境准备 需要 Go 和 Node.js。建议版本: - **Go** ≥ 1.23 - **Node.js** ≥ 18 确认已装好: ```bash go version # 应输出 go1.23+ node -v # 应输出 v18+ ``` 如果 Go 模块下载慢,配置国内代理: ```bash go env -w GOPROXY=https://goproxy.cn,direct ``` ## 🚀 启动 ### 场景 A:本地开发(热重载) 开发时前端走 Vite dev server(5173,热重载),后端跑编译好的二进制(8080), 前端 `/api/*` 通过 Vite proxy 转发到后端。 **首次准备**(每个新克隆只需一次): ```bash cd frontend npm install ``` **日常开发**(两个终端): ```bash # 终端 1:前端 cd frontend npm run dev # → 浏览器打开 http://localhost:5173 # 终端 2:后端(先 build 一次,之后用 run 启动;改 Go 代码后重跑 build 再 run) ./scripts/build.sh # build + 冒烟 ./scripts/run.sh # 仅启动 ``` 开发模式下访问 **http://localhost:5173**(不是 8080)——Vite 提供热重载, `/api` 自动转发到 8080 的后端。 ### 场景 B:生产部署(单一二进制) 适合部署到服务器。最终只有一个二进制 + 一个 SQLite 文件 + 配置文件。 ```bash # 1. 构建前端 cd frontend && npm install && npm run build && cd .. # 2. 构建后端(拷贝 dist → backend/dist + go build) ./scripts/build.sh # 3. 配置(按需) cd backend cp config.example.toml config.toml # 编辑 config.toml:填入 DeepSeek API key、按需改端口(不填 key 则 AI 功能整体禁用) # 4. 运行(产出 ./backend/decision-helper 二进制) ./decision-helper # 默认读同目录 config.toml ./decision-helper -c ./config.prod.toml # 或指定其它配置文件 # → 监听 config.toml 里 [server].addr(默认 :8080),浏览器访问 http://your-server:8080 # 前端用相对路径构建,根路径部署直接可用;要部署到子路径见下方「场景 C」。 ``` **运行时文件**(都在与二进制同目录): | 文件 | 说明 | |---|---| | `decision-helper` | 二进制本身 | | `config.toml` | 全局运维配置:端口、DB/密钥路径、DeepSeek key(可选;缺失则 AI 禁用) | | `decisions.db` | SQLite 数据库(首次运行自动创建;路径可在 config.toml 改) | | `.secret` | JWT 密钥(首次运行自动生成;删除 = 强制所有人重登) | > 所有运行参数都集中在 `config.toml`,不再使用环境变量。切换环境只需换一个配置文件(`-c` 指定)。 ### 场景 C:部署到子路径(如 `/decision/`) 前端**自动适配**部署路径——同一份构建产物既能在根路径(`http://host:8080/`)跑,也能在任意一级子路径(`http://host/decision/`)跑,**无需重新构建**。原理: - 静态资源用相对路径(vite `base: './'`),浏览器按当前 URL 相对解析; - API 路径和路由 basename 由前端运行时从当前 URL 推断(`src/lib/basePath.ts`):根路径返回空串,子路径返回 `/<段>`。 子路径部署只需在 nginx 加一段反代,**剥离前缀**转给后端(后端只认 `/api/...`,零改动): ```nginx server { listen 80; server_name your-server; # /decision/ 反代到后端。proxy_pass 末尾的 / 关键: # 会把 /decision/api/x 改写成 /api/x 再转给后端。 location /decision/ { proxy_pass http://127.0.0.1:8080/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } # 访问 /decision(无尾斜杠)时 301 到 /decision/,避免 SPA 资源相对路径解析错乱 location = /decision { return 301 /decision/; } } ``` > **换子路径名**(如 `/app/`):只改 nginx 的 `location`,**前端不用重新 build**——产物与路径解耦。注意子路径只支持一级(`/app/` 可以,`/a/b/` 不行)。 > **本地开发**:`npm run dev` 默认按根路径跑,访问 `http://localhost:5173/`。dev 模式不模拟子路径(子路径行为靠生产环境 nginx 验证)。 ### 启用 AI 辅助 1. 在 [platform.deepseek.com](https://platform.deepseek.com) 注册并获取 API key 2. `cp backend/config.example.toml backend/config.toml`,填入 `api_key` 3. 重启后端(日志会打印 `llm enabled`) 4. 用户登录后进入「设置」页,打开「启用 AI 辅助」开关 5. 之后新建决策可点「✨ AI 预填」;详情页可点「生成总结」 ## 🧪 测试 ```bash # 前端单元测试(评分算法 + reducer) cd frontend && npm test # 后端冒烟测试(自带 build + 启动 + 测试 + 清理) ./scripts/smoke-auth.sh # auth 完整链路 ./scripts/smoke-decisions.sh # decisions CRUD + 跨用户隔离 # 联调冒烟(需先用 run.sh 启动后端) ./scripts/run.sh ./scripts/smoke-full.sh # 前后端联调(注册→创建→列表→取单→PUT) ./scripts/smoke-llm.sh # LLM 守卫(无 config 时降级) ``` ## 🛣️ 上线前清单 - [ ] **HTTPS**:账号系统需 HTTPS(否则浏览器拒存密码/JWT)。推荐 Caddy 反代 + 自动证书 - [ ] **DeepSeek key**:填入 `config.toml` - [ ] **备份**:定期备份 `decisions.db`;升级二进制时勿覆盖 `decisions.db` 和 `.secret` ## 🏗️ 技术栈 | 层 | 选择 | |---|---| | 前端 | Vite 5 + React 18 + TypeScript + Tailwind CSS 3 | | 后端 | Go 1.23+ 标准库 `net/http`(无 web 框架) | | 数据库 | SQLite(modernc.org/sqlite,纯 Go 无 CGO) | | 鉴权 | JWT(HS256,golang-jwt)+ bcrypt | | AI | DeepSeek(OpenAI 兼容协议) | | 测试 | Vitest(前端)+ curl 冒烟脚本(后端) | ## 📄 License MIT