Files
frontend/skinserviceREADME .md

319 lines
10 KiB
Markdown
Raw Normal View History

# 皮肤服务 (SkinService)
此项目是一个使用 **Go-zero** 框架构建的、专注于材质管理的纯 RPC 微服务。它为CarrotSkin皮肤站提供了完整的材质上传、存储、查询和管理功能并集成了基于 MinIO 的高效文件存储系统。
## 核心功能
- **安全材质上传**: 利用 MinIO 预签名 POST URL实现客户端直传支持PNG格式限制和文件大小控制1KB-1MB
- **智能去重机制**: 基于 SHA-256 哈希值自动检测并复用相同材质,节省存储空间
- **完整材质管理**: 提供材质的增、删、改、查CRUD等全套操作
- **分类存储**: 支持皮肤SKIN和披风CAPE两种材质类型的分类管理
- **个人皮肤库**: 用户可查看和管理自己上传的所有材质
- **皮肤广场**: 公开材质展示平台,支持按类型浏览和搜索
- **收藏夹功能**: 支持用户收藏和管理自己的收藏列表
- **高性能查询**: 支持分页查询、批量获取和条件搜索
- **权限控制**: 严格的材质所有权验证,确保用户只能操作自己的材质
## 技术特性
- **微服务架构**: 基于 gRPC 的高性能服务间通信
- **对象存储**: MinIO 分布式存储,支持海量文件管理
- **数据库缓存**: go-zero 内置缓存机制,提升查询性能
- **类型安全**: 完整的 protobuf 定义和数据验证
- **容错设计**: 优雅的错误处理和日志记录
- **代码生成**: 使用 `goctl``.proto``.sql` 文件自动生成代码
## API 接口参考
服务通过 gRPC 暴露,接口定义于 `docs/textures.proto`
### 材质上传流程
| 方法名 | 功能描述 | 请求类型 | 响应类型 |
|--------|----------|----------|----------|
| `GenerateTextureUploadURL` | 生成材质上传预签名URL | `GenerateTextureUploadURLRequest` | `GenerateTextureUploadURLResponse` |
| `CreateTexture` | 创建材质记录(上传完成后调用) | `CreateTextureRequest` | `CreateTextureResponse` |
**上传流程说明**
1. 客户端调用 `GenerateTextureUploadURL` 获取预签名上传URL和表单数据
2. 客户端使用返回的 `post_url``form_data` 直接向MinIO上传文件
3. 上传成功后,客户端调用 `CreateTexture` 将材质信息记录到数据库
### 材质管理接口
| 方法名 | 功能描述 | 请求类型 | 响应类型 |
|--------|----------|----------|----------|
| `GetTexture` | 获取单个材质信息 | `GetTextureRequest` | `GetTextureResponse` |
| `UpdateTexture` | 更新材质信息(公开/私有状态) | `UpdateTextureRequest` | `UpdateTextureResponse` |
| `DeleteTexture` | 删除材质含MinIO文件清理 | `DeleteTextureRequest` | `DeleteTextureResponse` |
### 查询接口
| 方法名 | 功能描述 | 请求类型 | 响应类型 |
|--------|----------|----------|----------|
| `GetUserTextures` | 获取用户个人材质库 | `GetUserTexturesRequest` | `GetUserTexturesResponse` |
| `GetPublicTextures` | 获取皮肤广场公开材质 | `GetPublicTexturesRequest` | `GetPublicTexturesResponse` |
| `SearchTextures` | 搜索材质 | `SearchTexturesRequest` | `SearchTexturesResponse` |
### 高级功能接口
| 方法名 | 功能描述 | 请求类型 | 响应类型 |
|--------|----------|----------|----------|
| `GetTextureByHash` | 根据哈希值查找材质(防重复上传) | `GetTextureByHashRequest` | `GetTextureByHashResponse` |
| `GetTexturesByIds` | 批量获取材质信息 | `GetTexturesByIdsRequest` | `GetTexturesByIdsResponse` |
### 收藏夹功能接口
| 方法名 | 功能描述 | 请求类型 | 响应类型 |
|---|---|---|---|
| `AddFavorite` | 添加材质到收藏夹 | `AddFavoriteRequest` | `AddFavoriteResponse` |
| `RemoveFavorite` | 从收藏夹移除材质 | `RemoveFavoriteRequest` | `RemoveFavoriteResponse` |
| `GetUserFavorites` | 获取用户收藏列表 | `GetUserFavoritesRequest` | `GetUserFavoritesResponse` |
| `CheckFavoriteStatus` | 检查材质收藏状态 | `CheckFavoriteStatusRequest` | `CheckFavoriteStatusResponse` |
## 数据模型
### 材质信息 (TextureInfo)
```protobuf
message TextureInfo {
int64 id = 1; // 材质ID
int64 uploader_id = 2; // 上传者用户ID
TextureType type = 3; // 材质类型SKIN/CAPE
string url = 4; // MinIO中的永久访问URL
string hash = 5; // SHA-256哈希值
bool is_public = 6; // 是否公开到皮肤广场
string created_at = 7; // 创建时间
string updated_at = 8; // 更新时间
}
```
### 材质类型枚举
```protobuf
enum TextureType {
SKIN = 0; // 皮肤
CAPE = 1; // 披风
}
```
### 收藏夹相关数据模型
#### 收藏材质信息 (FavoriteTextureInfo)
```protobuf
message FavoriteTextureInfo {
TextureInfo texture = 1; // 材质信息
string favorite_at = 2; // 收藏时间
}
```
## 存储结构
### MinIO 对象存储结构
```
textures/ # 存储桶根目录
├── skins/ # 皮肤材质目录
│ └── user_{userId}/ # 按用户分组
│ └── {timestamp}_{filename}.png
└── capes/ # 披风材质目录
└── user_{userId}/ # 按用户分组
└── {timestamp}_{filename}.png
```
### 数据库表结构
```sql
CREATE TABLE textures (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
uploader_id BIGINT NOT NULL,
type VARCHAR(10) NOT NULL, -- 'SKIN' 或 'CAPE'
url VARCHAR(500) NOT NULL, -- MinIO访问URL
hash VARCHAR(64) NOT NULL UNIQUE, -- SHA-256哈希值
is_public BOOLEAN DEFAULT FALSE, -- 是否公开
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX idx_uploader_type (uploader_id, type),
INDEX idx_hash (hash),
INDEX idx_public_type (is_public, type, created_at)
);
```
### `user_texture_favorites` 表
```sql
-- 用户材质收藏表
CREATE TABLE `user_texture_favorites` (
`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '收藏记录的唯一ID',
`user_id` BIGINT UNSIGNED NOT NULL COMMENT '用户ID (对应UserService中的users.id)',
`texture_id` BIGINT UNSIGNED NOT NULL COMMENT '收藏的材质ID (对应textures.id)',
`created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '收藏时间',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_user_texture` (`user_id`, `texture_id`),
INDEX `idx_user_id` (`user_id`),
INDEX `idx_texture_id` (`texture_id`),
INDEX `idx_created_at` (`created_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='用户材质收藏表';
```
## 配置说明
### 服务配置 (etc/textures.yaml)
```yaml
Name: textures.rpc
ListenOn: 0.0.0.0:8080
# 数据库配置
DataSource: root:password@tcp(localhost:3306)/carrot_skin?charset=utf8mb4&parseTime=true&loc=Local
# 缓存配置
CacheRedis:
- Host: localhost:6379
Pass: ""
Type: node
# MinIO配置
MinIO:
Endpoint: "localhost:9000"
AccessKeyID: "minioadmin"
SecretAccessKey: "minioadmin"
UseSSL: false
Buckets:
Textures: "carrot-skin-textures"
```
## 安全特性
### 文件上传安全
- **格式限制**: 仅允许PNG格式文件
- **大小限制**: 文件大小限制在1KB-1MB之间
- **时效控制**: 预签名URL 15分钟过期
- **内容验证**: MinIO层面的Content-Type验证
### 权限控制
- **所有权验证**: 用户只能删除/更新自己上传的材质
- **参数校验**: 所有接口都有完整的输入验证
- **错误处理**: 不暴露敏感的系统信息
### 数据完整性
- **哈希去重**: SHA-256确保文件唯一性
- **事务处理**: 数据库操作的原子性保证
- **文件同步**: 删除材质时同步清理MinIO文件
## 性能优化
### 查询优化
- **分页查询**: 支持高效的大数据量分页
- **索引优化**: 针对常用查询场景建立复合索引
- **缓存机制**: go-zero内置的Redis缓存层
### 存储优化
- **智能去重**: 相同文件自动复用,节省存储空间
- **分类存储**: 按材质类型和用户分目录存储
## 部署说明
### 环境要求
- Go 1.19+
- MySQL 8.0+
- Redis 6.0+
- MinIO Server
### 启动服务
```bash
# 1. 安装依赖
go mod tidy
# 2. 生成代码如果修改了proto或sql文件
goctl rpc protoc docs/textures.proto --go_out=./pb --go-grpc_out=./pb --zrpc_out=.
goctl model mysql ddl --src="docs/textures.sql" --dir="./internal/model"
# 3. 启动服务
go run textures.go -f etc/textures.yaml
```
### Docker 部署
```dockerfile
FROM golang:1.19-alpine AS builder
WORKDIR /app
COPY . .
RUN go mod tidy && go build -o textures textures.go
FROM alpine:latest
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY --from=builder /app/textures .
COPY --from=builder /app/etc ./etc
CMD ["./textures", "-f", "etc/textures.yaml"]
```
## 监控与日志
### 日志记录
- **操作日志**: 记录所有材质操作的详细信息
- **错误日志**: 详细的错误堆栈和上下文信息
- **性能日志**: 查询耗时和系统性能指标
### 监控指标
- **接口调用量**: 各接口的QPS统计
- **存储使用量**: MinIO存储空间使用情况
- **缓存命中率**: Redis缓存效果监控
- **错误率统计**: 接口错误率和错误类型分析
## 开发指南
### 添加新接口
1.`docs/textures.proto` 中定义新的RPC方法
2. 运行 `goctl rpc protoc` 生成代码
3.`internal/logic/` 中实现业务逻辑
4. 添加相应的数据库查询方法(如需要)
### 自定义数据库查询
`internal/model/texturesModel.go` 中添加自定义查询方法:
```go
// 在 TexturesModel 接口中添加方法声明
type TexturesModel interface {
texturesModel
// 自定义方法
FindByCustomCondition(ctx context.Context, condition string) ([]*Textures, error)
}
// 在 customTexturesModel 中实现方法
func (m *customTexturesModel) FindByCustomCondition(ctx context.Context, condition string) ([]*Textures, error) {
query := `SELECT * FROM textures WHERE custom_field = ?`
var resp []*Textures
err := m.QueryRowsNoCacheCtx(ctx, &resp, query, condition)
return resp, err
}
```
## 故障排查
### 常见问题
1. **上传失败**: 检查MinIO连接和存储桶配置
2. **查询缓慢**: 检查数据库索引和Redis缓存
3. **文件不一致**: 检查MinIO文件清理逻辑
4. **权限错误**: 检查用户ID和材质所有权
### 调试技巧
- 启用详细日志: 设置日志级别为 `debug`
- 检查MinIO状态: 使用MinIO控制台查看文件状态
- 监控数据库: 使用慢查询日志分析性能问题