2026-07-16 01:47:11 +00:00
|
|
|
|
# FloorVisualizer
|
|
|
|
|
|
|
2026-07-16 02:39:55 +00:00
|
|
|
|
AI 换地板可视化后端服务 — 上传房间照片,AI 自动识别地面区域并生成换地板效果图。
|
2026-07-16 01:47:11 +00:00
|
|
|
|
|
|
|
|
|
|
## 技术栈
|
|
|
|
|
|
|
|
|
|
|
|
- **语言**: Go 1.23+
|
2026-07-16 02:39:55 +00:00
|
|
|
|
- **数据库**: PostgreSQL 15(GORM + 原生 SQL)
|
2026-07-16 01:47:11 +00:00
|
|
|
|
- **缓存/队列**: Redis 7
|
|
|
|
|
|
- **AI**: OpenRouter API (Gemini 3 Pro / Flash)
|
|
|
|
|
|
- **部署**: Docker Compose
|
|
|
|
|
|
|
|
|
|
|
|
## 快速开始
|
|
|
|
|
|
|
2026-07-16 02:39:55 +00:00
|
|
|
|
### 首次部署
|
2026-07-16 01:47:11 +00:00
|
|
|
|
|
|
|
|
|
|
```bash
|
2026-07-16 02:39:55 +00:00
|
|
|
|
# 1. 初始化数据库(仅一次)
|
|
|
|
|
|
psql -U postgres < schema.sql
|
2026-07-16 01:47:11 +00:00
|
|
|
|
|
|
|
|
|
|
# 2. 导入产品数据
|
2026-07-16 02:39:55 +00:00
|
|
|
|
go run cmd/import/main.go # 3539 条产品
|
|
|
|
|
|
go run cmd/import_articles/main.go # 30 篇科普文章
|
2026-07-16 01:47:11 +00:00
|
|
|
|
|
2026-07-16 02:39:55 +00:00
|
|
|
|
# 3. 启动开发服务(本地 DB + Redis)
|
|
|
|
|
|
docker compose -f docker-compose.dev.yml up -d
|
2026-07-16 01:47:11 +00:00
|
|
|
|
go run main.go
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2026-07-16 02:39:55 +00:00
|
|
|
|
服务运行在 `http://localhost:8099`。
|
2026-07-16 01:47:11 +00:00
|
|
|
|
|
|
|
|
|
|
### 生产部署
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
docker compose up -d --build
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
## 项目结构
|
|
|
|
|
|
|
|
|
|
|
|
```
|
2026-07-16 02:39:55 +00:00
|
|
|
|
├── main.go
|
|
|
|
|
|
├── schema.sql # 建表脚本(导入一次)
|
2026-07-16 01:47:11 +00:00
|
|
|
|
├── cmd/
|
|
|
|
|
|
│ ├── import/main.go # 产品数据导入
|
|
|
|
|
|
│ ├── import_articles/main.go # 科普文章导入
|
|
|
|
|
|
│ └── scrape_logos/main.py # 品牌 Logo 刮取
|
|
|
|
|
|
├── internal/
|
2026-07-16 02:39:55 +00:00
|
|
|
|
│ ├── 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 # 品牌浏览统计
|
|
|
|
|
|
│ │ └── ...
|
2026-07-16 01:47:11 +00:00
|
|
|
|
│ ├── service/ # 业务逻辑(推荐算法)
|
2026-07-16 02:39:55 +00:00
|
|
|
|
│ ├── middleware/ # JWT 认证 + 访问日志
|
|
|
|
|
|
│ ├── queue/ # Redis 异步任务队列
|
|
|
|
|
|
│ │ ├── redis.go # 入队/出队/状态管理
|
|
|
|
|
|
│ │ └── worker.go # Worker 池(并发消费)
|
2026-07-16 01:47:11 +00:00
|
|
|
|
│ ├── openrouter/ # OpenRouter API 客户端
|
2026-07-16 02:39:55 +00:00
|
|
|
|
│ └── logger/ # 分级日志(日切 + 30 天清理)
|
|
|
|
|
|
├── data/products/ # 产品 JSON(3539 条,不入 git)
|
2026-07-16 01:47:11 +00:00
|
|
|
|
├── Dockerfile
|
2026-07-16 02:39:55 +00:00
|
|
|
|
├── docker-compose.yml
|
|
|
|
|
|
├── docker-compose.dev.yml
|
2026-07-16 01:47:11 +00:00
|
|
|
|
└── 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` | 日志目录 |
|
2026-07-16 02:39:55 +00:00
|
|
|
|
| `LOG_LEVEL` | `INFO` | DEBUG / INFO / WARN / ERROR |
|
2026-07-16 01:47:11 +00:00
|
|
|
|
|
|
|
|
|
|
## API 总览
|
|
|
|
|
|
|
|
|
|
|
|
所有接口统一返回 `{"code": 200, "data": {...}}` 信封格式。
|
|
|
|
|
|
|
|
|
|
|
|
### 认证
|
|
|
|
|
|
- `POST /auth/register` — 注册
|
|
|
|
|
|
- `POST /auth/login` — 登录
|
|
|
|
|
|
|
|
|
|
|
|
### 产品(公开)
|
|
|
|
|
|
- `GET /products` — 列表(多维度筛选+搜索+分页)
|
2026-07-16 02:39:55 +00:00
|
|
|
|
- `GET /products/{sku}` — 详情(含 specs 规格、收藏状态、热点缓存)
|
2026-07-16 01:47:11 +00:00
|
|
|
|
- `GET /product-options` — 筛选选项(含品牌 logo、款式数、合集数)
|
|
|
|
|
|
|
|
|
|
|
|
### 推荐(公开)
|
2026-07-16 02:39:55 +00:00
|
|
|
|
- `GET /recommend/api?sku=xxx` — 跨品牌相似推荐(specs 感知)
|
2026-07-16 01:47:11 +00:00
|
|
|
|
|
2026-07-16 02:39:55 +00:00
|
|
|
|
### 地板更换(需 Redis)
|
2026-07-16 01:47:11 +00:00
|
|
|
|
- `GET /floor/options` — 地板样式+铺设方式+房间类型
|
|
|
|
|
|
- `POST /floor/generate` — 提交 AI 生成任务 → 返回 `job_id`
|
2026-07-16 02:39:55 +00:00
|
|
|
|
- `GET /floor/status?job_id=xxx` — 查询异步任务进度
|
2026-07-16 01:47:11 +00:00
|
|
|
|
|
|
|
|
|
|
### 用户(需 JWT)
|
|
|
|
|
|
- `GET /user/me` / `PUT /user/me` — 个人信息
|
|
|
|
|
|
- `POST /user/avatar` — 上传头像
|
|
|
|
|
|
- `GET /user/favorites` / `POST` / `DELETE` — 收藏管理
|
|
|
|
|
|
- `GET /user/projects` / `POST` / `PUT` / `DELETE` — 项目(生成记录)
|
2026-07-16 02:39:55 +00:00
|
|
|
|
- `GET /user/frequent-brands` — 常用品牌(自动统计,浏览≥3 或 收藏≥2)
|
2026-07-16 01:47:11 +00:00
|
|
|
|
|
|
|
|
|
|
### 计算器
|
|
|
|
|
|
- `POST /calculator/calc` — 多房间面积+损耗+费用计算
|
|
|
|
|
|
|
|
|
|
|
|
### 文章(公开)
|
|
|
|
|
|
- `GET /articles` — 科普文章列表
|
|
|
|
|
|
- `GET /articles/{id}` — 文章详情
|
|
|
|
|
|
- `GET /articles/recommend` — 随机推荐
|
|
|
|
|
|
|
|
|
|
|
|
### 其他
|
2026-07-16 02:39:55 +00:00
|
|
|
|
- `POST /upload` — 通用图片上传(20MB)
|
2026-07-16 01:47:11 +00:00
|
|
|
|
- `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 |
|
|
|
|
|
|
|
|
|
|
|
|
## 产品数据
|
|
|
|
|
|
|
2026-07-16 02:39:55 +00:00
|
|
|
|
10 个品牌,3539 条产品,覆盖 Hardwood / Engineered Wood / Laminate / SPC/LVP / Wood-Look Tile 五大品类。
|
|
|
|
|
|
|
|
|
|
|
|
数据存储在 `data/products/*.json`(不入 git),首次使用需运行 `go run cmd/import/main.go` 导入 PostgreSQL。
|
|
|
|
|
|
|
|
|
|
|
|
## 数据库
|
|
|
|
|
|
|
|
|
|
|
|
表结构统一管理在 `schema.sql`,服务启动时不再自动建表。修改表结构后更新此文件即可。
|
2026-07-16 01:47:11 +00:00
|
|
|
|
|
2026-07-16 02:39:55 +00:00
|
|
|
|
查询层使用 GORM + 原生 SQL 混合:
|
|
|
|
|
|
- 简单 CRUD(`GetProductBySKU`)→ GORM
|
|
|
|
|
|
- 复杂查询(动态筛选 `DISTINCT ON`、批量统计)→ 原生 SQL
|
2026-07-16 01:47:11 +00:00
|
|
|
|
|
|
|
|
|
|
## 日志
|
|
|
|
|
|
|
2026-07-16 02:39:55 +00:00
|
|
|
|
- **格式**: `2026-07-10 16:30:01.234 [INFO] main.go:96 Server started`
|
|
|
|
|
|
- **存储**: `logs/app-YYYY-MM-DD.log`,每天自动切文件
|
2026-07-16 01:47:11 +00:00
|
|
|
|
- **清理**: 自动删除 30 天前的日志
|
2026-07-16 02:39:55 +00:00
|
|
|
|
- **输出**: 同时写 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`
|
2026-07-27 06:39:45 +00:00
|
|
|
|
"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."
|