FloorVisualizer/README.md
2026-07-27 16:11:15 +08:00

206 lines
7.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# FloorVisualizer
AI 换地板可视化后端服务 — 上传房间照片AI 自动识别地面区域并生成换地板效果图。
## 技术栈
- **语言**: Go 1.23+
- **数据库**: PostgreSQL 15GORM + 原生 SQL
- **缓存/队列**: Redis 7
- **AI**: OpenRouter API (Gemini 3 Pro / Flash)
- **部署**: Docker Compose
## 快速开始
### 首次部署
```bash
# 1. 初始化数据库(仅一次)
psql -U postgres < schema.sql
# 2. 导入产品数据
go run cmd/import/main.go # 3539 条产品
go run cmd/import_articles/main.go # 30 篇科普文章
# 3. 启动开发服务(本地 DB + Redis
docker compose -f docker-compose.dev.yml up -d
go run main.go
```
服务运行在 `http://localhost:8099`
### 生产部署
```bash
docker compose up -d --build
```
## 项目结构
```
├── main.go
├── schema.sql # 建表脚本(导入一次)
├── cmd/
│ ├── import/main.go # 产品数据导入
│ ├── import_articles/main.go # 科普文章导入
│ └── scrape_logos/main.py # 品牌 Logo 刮取
├── internal/
│ ├── api/
│ │ └── router.go # 路由注册 + 依赖注入 + 响应 helper
│ ├── handler/ # HTTP 处理器(函数导出)
│ │ ├── router.go # writeJSON/writeErr
│ │ ├── auth_handler.go # Register / Login
│ │ ├── user_handler.go # Me / UploadAvatar / Favorites / Projects / FrequentBrands
│ │ ├── product_handler.go # Products / ProductOptions / FilterOptions
│ │ ├── recommend_handler.go # Recommend
│ │ ├── calc_handler.go # Calc
│ │ ├── floor_handler.go # FloorGenerate / FloorStatus / FloorOptions
│ │ ├── article_handler.go # Articles / ArticleRecommend
│ │ ├── health_handler.go # Healthz
│ │ └── upload_handler.go # Upload
│ ├── model/ # 数据模型 + 返回结构体
│ │ ├── response.go # ApiResponse / ApiError
│ │ ├── calc.go # CalcInput / CalcResult / CalcDeduction
│ │ ├── product.go # Product / ProductSpec
│ │ ├── brand.go # BrandInfo
│ │ └── ...
│ ├── repository/ # 数据访问GORM + 原生 SQL
│ │ ├── gorm.go # GORM 初始化
│ │ ├── db.go # PostgreSQL 连接
│ │ ├── product_repo.go # 产品查询(简单 GORM复杂 SQL
│ │ ├── brand_view_repo.go # 品牌浏览统计
│ │ └── ...
│ ├── service/ # 业务逻辑(推荐算法)
│ ├── middleware/ # JWT 认证 + 访问日志
│ ├── queue/ # Redis 异步任务队列
│ │ ├── redis.go # 入队/出队/状态管理
│ │ └── worker.go # Worker 池(并发消费)
│ ├── openrouter/ # OpenRouter API 客户端
│ └── logger/ # 分级日志(日切 + 30 天清理)
├── data/products/ # 产品 JSON3539 条,不入 git
├── Dockerfile
├── docker-compose.yml
├── docker-compose.dev.yml
└── apifox-import.json # API 文档
```
## 环境变量
| 变量 | 默认值 | 说明 |
|------|--------|------|
| `PORT` | `8099` | HTTP 端口 |
| `DATABASE_URL` | `postgres://postgres:dev123456@localhost:5432/floorvisualizer?sslmode=disable` | 数据库连接 |
| `REDIS_ADDR` | `localhost:6379` | Redis 地址 |
| `JWT_SECRET` | 内置默认值 | JWT 签名密钥(生产务必修改) |
| `OPENROUTER_API_KEY` | 内置默认值 | OpenRouter API Key |
| `PROXY_URL` | `127.0.0.1:7897` | HTTP 代理(留空禁用) |
| `DISABLE_PROXY` | — | 设为 `true` 强制禁用代理 |
| `WORKER_COUNT` | `5` | AI 生成并发 worker 数1-20 |
| `LOG_DIR` | `logs` | 日志目录 |
| `LOG_LEVEL` | `INFO` | DEBUG / INFO / WARN / ERROR |
## API 总览
所有接口统一返回 `{"code": 200, "data": {...}}` 信封格式。
### 认证
- `POST /auth/register` — 注册
- `POST /auth/login` — 登录
### 产品(公开)
- `GET /products` — 列表(多维度筛选+搜索+分页)
- `GET /products/{sku}` — 详情(含 specs 规格、收藏状态、热点缓存)
- `GET /product-options` — 筛选选项(含品牌 logo、款式数、合集数
### 推荐(公开)
- `GET /recommend/api?sku=xxx` — 跨品牌相似推荐specs 感知)
### 地板更换(需 Redis
- `GET /floor/options` — 地板样式+铺设方式+房间类型
- `POST /floor/generate` — 提交 AI 生成任务 → 返回 `job_id`
- `GET /floor/status?job_id=xxx` — 查询异步任务进度
### 用户(需 JWT
- `GET /user/me` / `PUT /user/me` — 个人信息
- `POST /user/avatar` — 上传头像
- `GET /user/favorites` / `POST` / `DELETE` — 收藏管理
- `GET /user/projects` / `POST` / `PUT` / `DELETE` — 项目(生成记录)
- `GET /user/frequent-brands` — 常用品牌自动统计浏览≥3 或 收藏≥2
### 计算器
- `POST /calculator/calc` — 多房间面积+损耗+费用计算
### 文章(公开)
- `GET /articles` — 科普文章列表
- `GET /articles/{id}` — 文章详情
- `GET /articles/recommend` — 随机推荐
### 其他
- `POST /upload` — 通用图片上传20MB
- `GET /healthz` — 健康检查
## 数字编码
### 铺设方式 (pattern_code)
| Code | 木地板 | 瓷砖 |
|------|--------|------|
| 1 | Straight Lay | — |
| 2 | Horizontal Lay | — |
| 3 | Herringbone | — |
| 4 | Chevron | — |
| 5 | — | Straight Grid |
| 6 | — | Running Bond |
| 7 | — | 1/3 Offset |
| 8 | — | Hexagonal |
| 9 | — | Diagonal (45°) |
### 房间类型 (room_code)
| Code | 房间 |
|------|------|
| 0 | Auto Detect |
| 1 | Living Room |
| 2 | Bedroom |
| 3 | Kitchen |
| 4 | Dining Room |
| 5 | Bathroom |
| 6 | Study Room |
| 7 | Hallway |
## 产品数据
10 个品牌3539 条产品,覆盖 Hardwood / Engineered Wood / Laminate / SPC/LVP / Wood-Look Tile 五大品类。
数据存储在 `data/products/*.json`(不入 git首次使用需运行 `go run cmd/import/main.go` 导入 PostgreSQL。
## 数据库
表结构统一管理在 `schema.sql`,服务启动时不再自动建表。修改表结构后更新此文件即可。
查询层使用 GORM + 原生 SQL 混合:
- 简单 CRUD`GetProductBySKU`)→ GORM
- 复杂查询(动态筛选 `DISTINCT ON`、批量统计)→ 原生 SQL
## 日志
- **格式**: `2026-07-10 16:30:01.234 [INFO] main.go:96 Server started`
- **存储**: `logs/app-YYYY-MM-DD.log`,每天自动切文件
- **清理**: 自动删除 30 天前的日志
- **输出**: 同时写 stdout + 文件
## 缓存
- **热点产品缓存**: Redis sorted set 统计浏览量Top 200 自动缓存TTL 1h ± 10min 随机抖动防雪崩
- **品牌浏览统计**: 自动记录(查询产品详情时触发)
## 异步队列
AI 图片生成使用 Redis List 做消息队列Worker 池消费:
- 提交任务 → RPUSH 入队 → 立即返回 `job_id`
- Worker 从 BRPOP 出队 → 调用 OpenRouter → 写入结果
- 并发数通过 `WORKER_COUNT` 控制,默认 5
- 进度查询: `GET /floor/status?job_id=xxx`
"description": "Light to medium golden oak-brown wood tone with warm
- yellow undertones and subtle tonal variation, featuring fine to moderately
- pronounced straight and gentle cathedral grain, a smooth lightly textured surface,
- low satin sheen, and a clean, natural, uniform visual character."