Initial commit: Material Texture API service
- Go + Gin + GORM + PostgreSQL backend - RESTful API for material management - Docker deployment support - Database partitioning for billion-scale data - API documentation 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
569
docs/API.md
Normal file
569
docs/API.md
Normal file
@@ -0,0 +1,569 @@
|
||||
# 材质管理系统 API 文档
|
||||
|
||||
## 基础信息
|
||||
|
||||
- **Base URL**: `http://10.99.98.248:8081/api/v1`
|
||||
- **认证方式**: API Token
|
||||
- **Content-Type**: `application/json`
|
||||
|
||||
## 认证
|
||||
|
||||
所有API请求需要在Header中携带Token:
|
||||
|
||||
```
|
||||
X-API-Token: seatons3d
|
||||
```
|
||||
|
||||
错误响应示例:
|
||||
```json
|
||||
{
|
||||
"code": 401,
|
||||
"message": "missing API token"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 通用响应格式
|
||||
|
||||
### 成功响应
|
||||
```json
|
||||
{
|
||||
"code": 0,
|
||||
"message": "success",
|
||||
"data": { ... }
|
||||
}
|
||||
```
|
||||
|
||||
### 分页响应
|
||||
```json
|
||||
{
|
||||
"code": 0,
|
||||
"message": "success",
|
||||
"data": {
|
||||
"items": [...],
|
||||
"total": 303,
|
||||
"page": 1,
|
||||
"page_size": 20
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 错误响应
|
||||
```json
|
||||
{
|
||||
"code": 400,
|
||||
"message": "error description"
|
||||
}
|
||||
```
|
||||
|
||||
### 错误码
|
||||
| 状态码 | 说明 |
|
||||
|--------|------|
|
||||
| 200 | 成功 |
|
||||
| 201 | 创建成功 |
|
||||
| 400 | 请求参数错误 |
|
||||
| 401 | 未授权(Token无效) |
|
||||
| 404 | 资源不存在 |
|
||||
| 500 | 服务器内部错误 |
|
||||
|
||||
---
|
||||
|
||||
## 材质管理接口
|
||||
|
||||
### 1. 获取材质列表
|
||||
|
||||
**GET** `/materials`
|
||||
|
||||
#### 请求参数(Query)
|
||||
| 参数 | 类型 | 必填 | 说明 |
|
||||
|------|------|------|------|
|
||||
| page | int | 否 | 页码,默认1 |
|
||||
| page_size | int | 否 | 每页数量,默认20,最大100 |
|
||||
| name | string | 否 | 按名称模糊搜索 |
|
||||
|
||||
#### 请求示例
|
||||
```bash
|
||||
curl -H "X-API-Token: seatons3d" \
|
||||
"http://10.99.98.248:8081/api/v1/materials?page=1&page_size=10&name=red"
|
||||
```
|
||||
|
||||
#### 响应示例
|
||||
```json
|
||||
{
|
||||
"code": 0,
|
||||
"message": "success",
|
||||
"data": {
|
||||
"items": [
|
||||
{
|
||||
"id": 4,
|
||||
"name": "dgnColor4",
|
||||
"diffuse_r": 255,
|
||||
"diffuse_g": 0,
|
||||
"diffuse_b": 0,
|
||||
"alpha": 255,
|
||||
"shininess": 20,
|
||||
"specular_r": 230,
|
||||
"specular_g": 230,
|
||||
"specular_b": 230,
|
||||
"ambient_r": 50,
|
||||
"ambient_g": 50,
|
||||
"ambient_b": 50,
|
||||
"metallic": 0,
|
||||
"roughness": 0,
|
||||
"reflectance": 0,
|
||||
"created_at": "2025-12-09T09:53:33.209555Z",
|
||||
"updated_at": "2025-12-09T09:53:33.209555Z"
|
||||
}
|
||||
],
|
||||
"total": 303,
|
||||
"page": 1,
|
||||
"page_size": 10
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 2. 添加材质
|
||||
|
||||
**POST** `/materials`
|
||||
|
||||
#### 请求Body
|
||||
| 字段 | 类型 | 必填 | 说明 |
|
||||
|------|------|------|------|
|
||||
| name | string | 是 | 材质名称 |
|
||||
| diffuse_r | float | 否 | 漫反射-红 (0-255) |
|
||||
| diffuse_g | float | 否 | 漫反射-绿 (0-255) |
|
||||
| diffuse_b | float | 否 | 漫反射-蓝 (0-255) |
|
||||
| alpha | float | 否 | 透明度 (0-255) |
|
||||
| shininess | float | 否 | 光泽度 |
|
||||
| specular_r | float | 否 | 高光-红 (0-255) |
|
||||
| specular_g | float | 否 | 高光-绿 (0-255) |
|
||||
| specular_b | float | 否 | 高光-蓝 (0-255) |
|
||||
| ambient_r | float | 否 | 环境光-红 (0-255) |
|
||||
| ambient_g | float | 否 | 环境光-绿 (0-255) |
|
||||
| ambient_b | float | 否 | 环境光-蓝 (0-255) |
|
||||
| metallic | float | 否 | 金属度 |
|
||||
| roughness | float | 否 | 粗糙度 |
|
||||
| reflectance | float | 否 | 反射率 |
|
||||
|
||||
#### 请求示例
|
||||
```bash
|
||||
curl -X POST -H "X-API-Token: seatons3d" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"name": "红色金属",
|
||||
"diffuse_r": 255,
|
||||
"diffuse_g": 50,
|
||||
"diffuse_b": 50,
|
||||
"alpha": 255,
|
||||
"metallic": 0.8,
|
||||
"roughness": 0.2
|
||||
}' \
|
||||
"http://10.99.98.248:8081/api/v1/materials"
|
||||
```
|
||||
|
||||
#### 响应示例
|
||||
```json
|
||||
{
|
||||
"code": 0,
|
||||
"message": "created",
|
||||
"data": {
|
||||
"id": 305,
|
||||
"name": "红色金属",
|
||||
"diffuse_r": 255,
|
||||
"diffuse_g": 50,
|
||||
"diffuse_b": 50,
|
||||
"alpha": 255,
|
||||
"shininess": 0,
|
||||
"specular_r": 0,
|
||||
"specular_g": 0,
|
||||
"specular_b": 0,
|
||||
"ambient_r": 0,
|
||||
"ambient_g": 0,
|
||||
"ambient_b": 0,
|
||||
"metallic": 0.8,
|
||||
"roughness": 0.2,
|
||||
"reflectance": 0.5,
|
||||
"created_at": "2025-12-09T18:02:22.846141Z",
|
||||
"updated_at": "2025-12-09T18:02:22.846141Z"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 3. 获取材质详情
|
||||
|
||||
**GET** `/materials/:id`
|
||||
|
||||
#### 路径参数
|
||||
| 参数 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| id | int | 材质ID |
|
||||
|
||||
#### 请求示例
|
||||
```bash
|
||||
curl -H "X-API-Token: seatons3d" \
|
||||
"http://10.99.98.248:8081/api/v1/materials/4"
|
||||
```
|
||||
|
||||
#### 响应示例
|
||||
```json
|
||||
{
|
||||
"code": 0,
|
||||
"message": "success",
|
||||
"data": {
|
||||
"id": 4,
|
||||
"name": "dgnColor4",
|
||||
"diffuse_r": 255,
|
||||
"diffuse_g": 0,
|
||||
"diffuse_b": 0,
|
||||
"alpha": 255,
|
||||
"shininess": 20,
|
||||
"specular_r": 230,
|
||||
"specular_g": 230,
|
||||
"specular_b": 230,
|
||||
"ambient_r": 50,
|
||||
"ambient_g": 50,
|
||||
"ambient_b": 50,
|
||||
"metallic": 0,
|
||||
"roughness": 0,
|
||||
"reflectance": 0,
|
||||
"created_at": "2025-12-09T09:53:33.209555Z",
|
||||
"updated_at": "2025-12-09T09:53:33.209555Z"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 错误响应
|
||||
```json
|
||||
{
|
||||
"code": 404,
|
||||
"message": "material not found"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 4. 编辑材质
|
||||
|
||||
**PUT** `/materials/:id`
|
||||
|
||||
#### 路径参数
|
||||
| 参数 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| id | int | 材质ID |
|
||||
|
||||
#### 请求Body
|
||||
同"添加材质"接口
|
||||
|
||||
#### 请求示例
|
||||
```bash
|
||||
curl -X PUT -H "X-API-Token: seatons3d" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"name": "红色金属-更新版",
|
||||
"diffuse_r": 200,
|
||||
"diffuse_g": 100,
|
||||
"diffuse_b": 50,
|
||||
"alpha": 255,
|
||||
"metallic": 0.9,
|
||||
"roughness": 0.1
|
||||
}' \
|
||||
"http://10.99.98.248:8081/api/v1/materials/305"
|
||||
```
|
||||
|
||||
#### 响应示例
|
||||
```json
|
||||
{
|
||||
"code": 0,
|
||||
"message": "success",
|
||||
"data": {
|
||||
"id": 305,
|
||||
"name": "红色金属-更新版",
|
||||
"diffuse_r": 200,
|
||||
"diffuse_g": 100,
|
||||
"diffuse_b": 50,
|
||||
"alpha": 255,
|
||||
...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 5. 删除材质
|
||||
|
||||
**DELETE** `/materials/:id`
|
||||
|
||||
#### 路径参数
|
||||
| 参数 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| id | int | 材质ID |
|
||||
|
||||
#### 请求示例
|
||||
```bash
|
||||
curl -X DELETE -H "X-API-Token: seatons3d" \
|
||||
"http://10.99.98.248:8081/api/v1/materials/305"
|
||||
```
|
||||
|
||||
#### 响应示例
|
||||
```json
|
||||
{
|
||||
"code": 0,
|
||||
"message": "success",
|
||||
"data": {
|
||||
"id": 305
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
> **注意**: 删除材质会级联删除所有相关的绑定关系
|
||||
|
||||
---
|
||||
|
||||
## 绑定管理接口
|
||||
|
||||
### 6. 绑定材质到多个Group
|
||||
|
||||
**POST** `/materials/:id/bindings`
|
||||
|
||||
将一个材质绑定到多个叶子节点(group_id),支持幂等操作(重复绑定不会报错)。
|
||||
|
||||
#### 路径参数
|
||||
| 参数 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| id | int | 材质ID |
|
||||
|
||||
#### 请求Body
|
||||
| 字段 | 类型 | 必填 | 说明 |
|
||||
|------|------|------|------|
|
||||
| group_ids | string[] | 是 | 叶子节点ID数组 |
|
||||
|
||||
#### 请求示例
|
||||
```bash
|
||||
curl -X POST -H "X-API-Token: seatons3d" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"group_ids": ["node_001", "node_002", "node_003"]
|
||||
}' \
|
||||
"http://10.99.98.248:8081/api/v1/materials/4/bindings"
|
||||
```
|
||||
|
||||
#### 响应示例
|
||||
```json
|
||||
{
|
||||
"code": 0,
|
||||
"message": "success",
|
||||
"data": {
|
||||
"material_id": 4,
|
||||
"group_ids": ["node_001", "node_002", "node_003"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 7. 解绑材质与Group
|
||||
|
||||
**DELETE** `/materials/:id/bindings`
|
||||
|
||||
#### 路径参数
|
||||
| 参数 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| id | int | 材质ID |
|
||||
|
||||
#### 请求Body
|
||||
| 字段 | 类型 | 必填 | 说明 |
|
||||
|------|------|------|------|
|
||||
| group_ids | string[] | 是 | 要解绑的叶子节点ID数组 |
|
||||
|
||||
#### 请求示例
|
||||
```bash
|
||||
curl -X DELETE -H "X-API-Token: seatons3d" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"group_ids": ["node_001", "node_002"]
|
||||
}' \
|
||||
"http://10.99.98.248:8081/api/v1/materials/4/bindings"
|
||||
```
|
||||
|
||||
#### 响应示例
|
||||
```json
|
||||
{
|
||||
"code": 0,
|
||||
"message": "success",
|
||||
"data": {
|
||||
"material_id": 4,
|
||||
"unbound": ["node_001", "node_002"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 8. 获取材质关联的所有Group(分页)
|
||||
|
||||
**GET** `/materials/:id/groups`
|
||||
|
||||
#### 路径参数
|
||||
| 参数 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| id | int | 材质ID |
|
||||
|
||||
#### 请求参数(Query)
|
||||
| 参数 | 类型 | 必填 | 说明 |
|
||||
|------|------|------|------|
|
||||
| page | int | 否 | 页码,默认1 |
|
||||
| page_size | int | 否 | 每页数量,默认20,最大100 |
|
||||
|
||||
#### 请求示例
|
||||
```bash
|
||||
curl -H "X-API-Token: seatons3d" \
|
||||
"http://10.99.98.248:8081/api/v1/materials/4/groups?page=1&page_size=10"
|
||||
```
|
||||
|
||||
#### 响应示例
|
||||
```json
|
||||
{
|
||||
"code": 0,
|
||||
"message": "success",
|
||||
"data": {
|
||||
"items": [
|
||||
"20251021105717834754",
|
||||
"20251021105717834758",
|
||||
"20251021105717834762"
|
||||
],
|
||||
"total": 22,
|
||||
"page": 1,
|
||||
"page_size": 10
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 9. 根据Group IDs批量查询关联材质
|
||||
|
||||
**POST** `/groups/materials`
|
||||
|
||||
根据一个或多个叶子节点ID,查询它们关联的材质详情。
|
||||
|
||||
#### 请求Body
|
||||
| 字段 | 类型 | 必填 | 说明 |
|
||||
|------|------|------|------|
|
||||
| group_ids | string[] | 是 | 叶子节点ID数组 |
|
||||
|
||||
#### 请求示例
|
||||
```bash
|
||||
curl -X POST -H "X-API-Token: seatons3d" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"group_ids": ["202510211057245681447", "aDsIXMkjxdm2CDUj9gTvf"]
|
||||
}' \
|
||||
"http://10.99.98.248:8081/api/v1/groups/materials"
|
||||
```
|
||||
|
||||
#### 响应示例
|
||||
```json
|
||||
{
|
||||
"code": 0,
|
||||
"message": "success",
|
||||
"data": [
|
||||
{
|
||||
"group_id": "202510211057245681447",
|
||||
"material": {
|
||||
"id": 4,
|
||||
"name": "dgnColor4",
|
||||
"diffuse_r": 255,
|
||||
"diffuse_g": 0,
|
||||
"diffuse_b": 0,
|
||||
"alpha": 255,
|
||||
"shininess": 20,
|
||||
"specular_r": 230,
|
||||
"specular_g": 230,
|
||||
"specular_b": 230,
|
||||
"ambient_r": 50,
|
||||
"ambient_g": 50,
|
||||
"ambient_b": 50,
|
||||
"metallic": 0,
|
||||
"roughness": 0,
|
||||
"reflectance": 0,
|
||||
"created_at": "2025-12-09T09:53:33.209555Z",
|
||||
"updated_at": "2025-12-09T09:53:33.209555Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
"group_id": "aDsIXMkjxdm2CDUj9gTvf",
|
||||
"material": {
|
||||
"id": -1,
|
||||
"name": "默认值",
|
||||
"diffuse_r": 192,
|
||||
"diffuse_g": 192,
|
||||
"diffuse_b": 192,
|
||||
...
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
> **注意**: 如果某个group_id没有绑定材质,则不会出现在返回结果中
|
||||
|
||||
---
|
||||
|
||||
## 健康检查
|
||||
|
||||
**GET** `/health`
|
||||
|
||||
此接口无需认证。
|
||||
|
||||
#### 请求示例
|
||||
```bash
|
||||
curl http://10.99.98.248:8081/health
|
||||
```
|
||||
|
||||
#### 响应示例
|
||||
```json
|
||||
{
|
||||
"status": "ok"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 数据模型
|
||||
|
||||
### Material(材质)
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| id | int64 | 材质ID(主键) |
|
||||
| name | string | 材质名称 |
|
||||
| diffuse_r | float64 | 漫反射-红 (0-255) |
|
||||
| diffuse_g | float64 | 漫反射-绿 (0-255) |
|
||||
| diffuse_b | float64 | 漫反射-蓝 (0-255) |
|
||||
| alpha | float64 | 透明度 (0-255) |
|
||||
| shininess | float64 | 光泽度 |
|
||||
| specular_r | float64 | 高光-红 (0-255) |
|
||||
| specular_g | float64 | 高光-绿 (0-255) |
|
||||
| specular_b | float64 | 高光-蓝 (0-255) |
|
||||
| ambient_r | float64 | 环境光-红 (0-255) |
|
||||
| ambient_g | float64 | 环境光-绿 (0-255) |
|
||||
| ambient_b | float64 | 环境光-蓝 (0-255) |
|
||||
| metallic | float64 | 金属度 |
|
||||
| roughness | float64 | 粗糙度 |
|
||||
| reflectance | float64 | 反射率 |
|
||||
| created_at | timestamp | 创建时间 |
|
||||
| updated_at | timestamp | 更新时间 |
|
||||
|
||||
### MaterialBinding(材质绑定)
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| id | int64 | 绑定ID(主键) |
|
||||
| material_id | int64 | 材质ID(外键) |
|
||||
| group_id | string | 叶子节点ID |
|
||||
| created_at | timestamp | 创建时间 |
|
||||
257
docs/DATABASE.md
Normal file
257
docs/DATABASE.md
Normal file
@@ -0,0 +1,257 @@
|
||||
# 数据库设计文档
|
||||
|
||||
## 概述
|
||||
|
||||
Material Texture 使用 PostgreSQL 数据库存储材质信息和绑定关系。
|
||||
|
||||
- **数据库**: PostgreSQL 15+
|
||||
- **ORM**: GORM v1.31
|
||||
- **特性**: 分区表、pg_trgm 模糊搜索
|
||||
|
||||
---
|
||||
|
||||
## ER 图
|
||||
|
||||
```mermaid
|
||||
erDiagram
|
||||
materials ||--o{ material_bindings : "has"
|
||||
|
||||
materials {
|
||||
bigint id PK "自增主键"
|
||||
varchar name "材质名称"
|
||||
float diffuse_r "漫反射-红"
|
||||
float diffuse_g "漫反射-绿"
|
||||
float diffuse_b "漫反射-蓝"
|
||||
float alpha "透明度"
|
||||
float shininess "光泽度"
|
||||
float specular_r "高光-红"
|
||||
float specular_g "高光-绿"
|
||||
float specular_b "高光-蓝"
|
||||
float ambient_r "环境光-红"
|
||||
float ambient_g "环境光-绿"
|
||||
float ambient_b "环境光-蓝"
|
||||
float metallic "金属度(PBR)"
|
||||
float roughness "粗糙度(PBR)"
|
||||
float reflectance "反射率(PBR)"
|
||||
bigint version "乐观锁版本"
|
||||
timestamp created_at "创建时间"
|
||||
timestamp updated_at "更新时间"
|
||||
}
|
||||
|
||||
material_bindings {
|
||||
bigint id PK "绑定ID"
|
||||
bigint material_id FK "材质ID"
|
||||
varchar group_id "叶子节点ID"
|
||||
timestamp created_at "创建时间"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 表结构详情
|
||||
|
||||
### 1. materials 表(材质表)
|
||||
|
||||
存储 3D 材质的属性信息,包括传统渲染属性和 PBR 属性。
|
||||
|
||||
| 字段 | 类型 | 约束 | 默认值 | 说明 |
|
||||
|------|------|------|--------|------|
|
||||
| id | BIGSERIAL | PRIMARY KEY | 自增 | 材质唯一标识 |
|
||||
| name | VARCHAR(255) | NOT NULL | - | 材质名称 |
|
||||
| diffuse_r | FLOAT | NOT NULL | 0 | 漫反射颜色-红 (0-255) |
|
||||
| diffuse_g | FLOAT | NOT NULL | 0 | 漫反射颜色-绿 (0-255) |
|
||||
| diffuse_b | FLOAT | NOT NULL | 0 | 漫反射颜色-蓝 (0-255) |
|
||||
| alpha | FLOAT | NOT NULL | 1 | 透明度 (0-255) |
|
||||
| shininess | FLOAT | NOT NULL | 0 | 光泽度/高光强度 |
|
||||
| specular_r | FLOAT | NOT NULL | 0 | 高光颜色-红 (0-255) |
|
||||
| specular_g | FLOAT | NOT NULL | 0 | 高光颜色-绿 (0-255) |
|
||||
| specular_b | FLOAT | NOT NULL | 0 | 高光颜色-蓝 (0-255) |
|
||||
| ambient_r | FLOAT | NOT NULL | 0 | 环境光颜色-红 (0-255) |
|
||||
| ambient_g | FLOAT | NOT NULL | 0 | 环境光颜色-绿 (0-255) |
|
||||
| ambient_b | FLOAT | NOT NULL | 0 | 环境光颜色-蓝 (0-255) |
|
||||
| metallic | FLOAT | NOT NULL | 0 | PBR 金属度 (0-1) |
|
||||
| roughness | FLOAT | NOT NULL | 0.5 | PBR 粗糙度 (0-1) |
|
||||
| reflectance | FLOAT | NOT NULL | 0.5 | PBR 反射率 (0-1) |
|
||||
| version | BIGINT | NOT NULL | 0 | 乐观锁版本号 |
|
||||
| created_at | TIMESTAMP | - | CURRENT_TIMESTAMP | 创建时间 |
|
||||
| updated_at | TIMESTAMP | - | CURRENT_TIMESTAMP | 更新时间 |
|
||||
|
||||
#### 索引
|
||||
|
||||
| 索引名 | 类型 | 字段 | 用途 |
|
||||
|--------|------|------|------|
|
||||
| materials_pkey | PRIMARY KEY | id | 主键 |
|
||||
| idx_materials_name_trgm | GIN | name | 模糊搜索 (ILIKE '%keyword%') |
|
||||
| idx_materials_created_at | BTREE | created_at DESC | 按创建时间排序 |
|
||||
| idx_materials_updated_at | BTREE | updated_at DESC | 按更新时间排序 |
|
||||
|
||||
---
|
||||
|
||||
### 2. material_bindings 表(材质绑定表)
|
||||
|
||||
存储材质与叶子节点(group_id)的多对多绑定关系。
|
||||
|
||||
**重要**: 此表使用 HASH 分区,按 `material_id` 分为 16 个分区,支持亿级数据规模。
|
||||
|
||||
| 字段 | 类型 | 约束 | 默认值 | 说明 |
|
||||
|------|------|------|--------|------|
|
||||
| id | BIGSERIAL | 复合主键 | 自增 | 绑定记录ID |
|
||||
| material_id | BIGINT | NOT NULL, FK | - | 关联的材质ID |
|
||||
| group_id | VARCHAR(255) | NOT NULL | - | 叶子节点ID |
|
||||
| created_at | TIMESTAMP | - | CURRENT_TIMESTAMP | 创建时间 |
|
||||
|
||||
#### 约束
|
||||
|
||||
- **主键**: `(material_id, id)` - 复合主键,支持分区
|
||||
- **外键**: `material_id` → `materials(id)` ON DELETE CASCADE
|
||||
- **唯一约束**: `(material_id, group_id)` - 防止重复绑定
|
||||
|
||||
#### 分区策略
|
||||
|
||||
```
|
||||
material_bindings (父表)
|
||||
├── material_bindings_p0 (material_id % 16 = 0)
|
||||
├── material_bindings_p1 (material_id % 16 = 1)
|
||||
├── material_bindings_p2 (material_id % 16 = 2)
|
||||
│ ...
|
||||
└── material_bindings_p15 (material_id % 16 = 15)
|
||||
```
|
||||
|
||||
#### 每个分区的索引
|
||||
|
||||
| 索引名 | 类型 | 字段 | 用途 |
|
||||
|--------|------|------|------|
|
||||
| idx_bindings_pX_unique | UNIQUE | (material_id, group_id) | 防止重复绑定 |
|
||||
| idx_bindings_pX_group | BTREE | group_id | 按 group_id 查询 |
|
||||
|
||||
---
|
||||
|
||||
## 迁移文件
|
||||
|
||||
| 文件 | 版本 | 说明 |
|
||||
|------|------|------|
|
||||
| 001_init.sql | v1.0 | 初始化基础表结构 |
|
||||
| 002_partition_bindings.sql | v1.1 | 将 bindings 表转为 16 分区 |
|
||||
| 003_add_indexes.sql | v1.2 | 添加 pg_trgm 和时间索引 |
|
||||
| 004_add_version_column.sql | v1.3 | 添加乐观锁版本字段 |
|
||||
|
||||
### 执行迁移
|
||||
|
||||
```bash
|
||||
# 进入数据库容器
|
||||
docker compose exec db psql -U postgres -d material_db
|
||||
|
||||
# 或直接执行
|
||||
psql -h localhost -p 5433 -U postgres -d material_db -f migrations/001_init.sql
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 性能优化说明
|
||||
|
||||
### 1. 分区表
|
||||
|
||||
- **目的**: 支持亿级 binding 记录
|
||||
- **策略**: HASH 分区,按 material_id 分 16 片
|
||||
- **优势**:
|
||||
- 单分区最大约 600-1000 万行
|
||||
- 查询时自动路由到相关分区
|
||||
- 可在线添加新分区
|
||||
|
||||
### 2. pg_trgm 扩展
|
||||
|
||||
```sql
|
||||
CREATE EXTENSION IF NOT EXISTS pg_trgm;
|
||||
```
|
||||
|
||||
- **目的**: 支持模糊搜索 `ILIKE '%keyword%'`
|
||||
- **原理**: 将字符串拆分为三元组 (trigram),建立 GIN 倒排索引
|
||||
|
||||
### 3. 连接池配置
|
||||
|
||||
| 参数 | 值 | 说明 |
|
||||
|------|-----|------|
|
||||
| MaxIdleConns | 50 | 最大空闲连接数 |
|
||||
| MaxOpenConns | 200 | 最大连接数 |
|
||||
| ConnMaxLifetime | 5min | 连接最大生命周期 |
|
||||
| ConnMaxIdleTime | 2min | 空闲连接最大生命周期 |
|
||||
|
||||
---
|
||||
|
||||
## 数据示例
|
||||
|
||||
### Material 示例
|
||||
|
||||
```json
|
||||
{
|
||||
"id": 4,
|
||||
"name": "红色金属",
|
||||
"diffuse_r": 255,
|
||||
"diffuse_g": 0,
|
||||
"diffuse_b": 0,
|
||||
"alpha": 255,
|
||||
"shininess": 80,
|
||||
"specular_r": 255,
|
||||
"specular_g": 200,
|
||||
"specular_b": 200,
|
||||
"ambient_r": 50,
|
||||
"ambient_g": 0,
|
||||
"ambient_b": 0,
|
||||
"metallic": 0.9,
|
||||
"roughness": 0.2,
|
||||
"reflectance": 0.8,
|
||||
"version": 0,
|
||||
"created_at": "2025-12-09T09:53:33Z",
|
||||
"updated_at": "2025-12-09T09:53:33Z"
|
||||
}
|
||||
```
|
||||
|
||||
### Binding 示例
|
||||
|
||||
```json
|
||||
{
|
||||
"id": 1,
|
||||
"material_id": 4,
|
||||
"group_id": "20251021105716587535",
|
||||
"created_at": "2025-12-09T10:00:00Z"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 常用查询
|
||||
|
||||
### 1. 按名称模糊搜索材质
|
||||
|
||||
```sql
|
||||
SELECT * FROM materials
|
||||
WHERE name ILIKE '%金属%'
|
||||
ORDER BY id DESC
|
||||
LIMIT 20;
|
||||
```
|
||||
|
||||
### 2. 获取材质关联的所有 group_id
|
||||
|
||||
```sql
|
||||
SELECT group_id FROM material_bindings
|
||||
WHERE material_id = 4
|
||||
ORDER BY created_at DESC
|
||||
LIMIT 20 OFFSET 0;
|
||||
```
|
||||
|
||||
### 3. 根据 group_id 批量查询材质
|
||||
|
||||
```sql
|
||||
SELECT mb.group_id, m.*
|
||||
FROM material_bindings mb
|
||||
JOIN materials m ON mb.material_id = m.id
|
||||
WHERE mb.group_id IN ('group1', 'group2', 'group3');
|
||||
```
|
||||
|
||||
### 4. 统计各分区数据量
|
||||
|
||||
```sql
|
||||
SELECT tableoid::regclass AS partition, COUNT(*)
|
||||
FROM material_bindings
|
||||
GROUP BY tableoid;
|
||||
```
|
||||
242
docs/Material_API.postman_collection.json
Normal file
242
docs/Material_API.postman_collection.json
Normal file
@@ -0,0 +1,242 @@
|
||||
{
|
||||
"info": {
|
||||
"_postman_id": "material-texture-api",
|
||||
"name": "材质管理系统 API",
|
||||
"description": "材质管理后端服务API集合",
|
||||
"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
|
||||
},
|
||||
"auth": {
|
||||
"type": "apikey",
|
||||
"apikey": [
|
||||
{
|
||||
"key": "key",
|
||||
"value": "X-API-Token",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"key": "value",
|
||||
"value": "{{api_token}}",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"key": "in",
|
||||
"value": "header",
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"variable": [
|
||||
{
|
||||
"key": "base_url",
|
||||
"value": "http://localhost:8081",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"key": "api_token",
|
||||
"value": "seatons3d",
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"item": [
|
||||
{
|
||||
"name": "健康检查",
|
||||
"item": [
|
||||
{
|
||||
"name": "Health Check",
|
||||
"request": {
|
||||
"auth": {
|
||||
"type": "noauth"
|
||||
},
|
||||
"method": "GET",
|
||||
"header": [],
|
||||
"url": {
|
||||
"raw": "{{base_url}}/health",
|
||||
"host": ["{{base_url}}"],
|
||||
"path": ["health"]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "材质管理",
|
||||
"item": [
|
||||
{
|
||||
"name": "1. 获取材质列表",
|
||||
"request": {
|
||||
"method": "GET",
|
||||
"header": [],
|
||||
"url": {
|
||||
"raw": "{{base_url}}/api/v1/materials?page=1&page_size=20&name=",
|
||||
"host": ["{{base_url}}"],
|
||||
"path": ["api", "v1", "materials"],
|
||||
"query": [
|
||||
{
|
||||
"key": "page",
|
||||
"value": "1",
|
||||
"description": "页码"
|
||||
},
|
||||
{
|
||||
"key": "page_size",
|
||||
"value": "20",
|
||||
"description": "每页数量"
|
||||
},
|
||||
{
|
||||
"key": "name",
|
||||
"value": "",
|
||||
"description": "按名称搜索"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "2. 添加材质",
|
||||
"request": {
|
||||
"method": "POST",
|
||||
"header": [
|
||||
{
|
||||
"key": "Content-Type",
|
||||
"value": "application/json"
|
||||
}
|
||||
],
|
||||
"body": {
|
||||
"mode": "raw",
|
||||
"raw": "{\n \"name\": \"新材质\",\n \"diffuse_r\": 255,\n \"diffuse_g\": 128,\n \"diffuse_b\": 64,\n \"alpha\": 255,\n \"shininess\": 20,\n \"specular_r\": 230,\n \"specular_g\": 230,\n \"specular_b\": 230,\n \"ambient_r\": 50,\n \"ambient_g\": 50,\n \"ambient_b\": 50,\n \"metallic\": 0.5,\n \"roughness\": 0.3,\n \"reflectance\": 0.5\n}"
|
||||
},
|
||||
"url": {
|
||||
"raw": "{{base_url}}/api/v1/materials",
|
||||
"host": ["{{base_url}}"],
|
||||
"path": ["api", "v1", "materials"]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "3. 获取材质详情",
|
||||
"request": {
|
||||
"method": "GET",
|
||||
"header": [],
|
||||
"url": {
|
||||
"raw": "{{base_url}}/api/v1/materials/4",
|
||||
"host": ["{{base_url}}"],
|
||||
"path": ["api", "v1", "materials", "4"]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "4. 编辑材质",
|
||||
"request": {
|
||||
"method": "PUT",
|
||||
"header": [
|
||||
{
|
||||
"key": "Content-Type",
|
||||
"value": "application/json"
|
||||
}
|
||||
],
|
||||
"body": {
|
||||
"mode": "raw",
|
||||
"raw": "{\n \"name\": \"更新后的材质\",\n \"diffuse_r\": 200,\n \"diffuse_g\": 100,\n \"diffuse_b\": 50,\n \"alpha\": 255,\n \"shininess\": 30,\n \"specular_r\": 200,\n \"specular_g\": 200,\n \"specular_b\": 200,\n \"ambient_r\": 60,\n \"ambient_g\": 60,\n \"ambient_b\": 60,\n \"metallic\": 0.8,\n \"roughness\": 0.2,\n \"reflectance\": 0.6\n}"
|
||||
},
|
||||
"url": {
|
||||
"raw": "{{base_url}}/api/v1/materials/4",
|
||||
"host": ["{{base_url}}"],
|
||||
"path": ["api", "v1", "materials", "4"]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "5. 删除材质",
|
||||
"request": {
|
||||
"method": "DELETE",
|
||||
"header": [],
|
||||
"url": {
|
||||
"raw": "{{base_url}}/api/v1/materials/999",
|
||||
"host": ["{{base_url}}"],
|
||||
"path": ["api", "v1", "materials", "999"]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "绑定管理",
|
||||
"item": [
|
||||
{
|
||||
"name": "6. 绑定材质到Groups",
|
||||
"request": {
|
||||
"method": "POST",
|
||||
"header": [
|
||||
{
|
||||
"key": "Content-Type",
|
||||
"value": "application/json"
|
||||
}
|
||||
],
|
||||
"body": {
|
||||
"mode": "raw",
|
||||
"raw": "{\n \"group_ids\": [\"node_001\", \"node_002\", \"node_003\"]\n}"
|
||||
},
|
||||
"url": {
|
||||
"raw": "{{base_url}}/api/v1/materials/4/bindings",
|
||||
"host": ["{{base_url}}"],
|
||||
"path": ["api", "v1", "materials", "4", "bindings"]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "7. 解绑材质与Groups",
|
||||
"request": {
|
||||
"method": "DELETE",
|
||||
"header": [
|
||||
{
|
||||
"key": "Content-Type",
|
||||
"value": "application/json"
|
||||
}
|
||||
],
|
||||
"body": {
|
||||
"mode": "raw",
|
||||
"raw": "{\n \"group_ids\": [\"node_001\", \"node_002\"]\n}"
|
||||
},
|
||||
"url": {
|
||||
"raw": "{{base_url}}/api/v1/materials/4/bindings",
|
||||
"host": ["{{base_url}}"],
|
||||
"path": ["api", "v1", "materials", "4", "bindings"]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "8. 获取材质关联的Groups",
|
||||
"request": {
|
||||
"method": "GET",
|
||||
"header": [],
|
||||
"url": {
|
||||
"raw": "{{base_url}}/api/v1/materials/4/groups",
|
||||
"host": ["{{base_url}}"],
|
||||
"path": ["api", "v1", "materials", "4", "groups"]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "9. 根据Groups批量查询材质",
|
||||
"request": {
|
||||
"method": "POST",
|
||||
"header": [
|
||||
{
|
||||
"key": "Content-Type",
|
||||
"value": "application/json"
|
||||
}
|
||||
],
|
||||
"body": {
|
||||
"mode": "raw",
|
||||
"raw": "{\n \"group_ids\": [\"202510211057245681447\", \"aDsIXMkjxdm2CDUj9gTvf\"]\n}"
|
||||
},
|
||||
"url": {
|
||||
"raw": "{{base_url}}/api/v1/groups/materials",
|
||||
"host": ["{{base_url}}"],
|
||||
"path": ["api", "v1", "groups", "materials"]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user