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:
likegears
2025-12-11 15:29:49 +08:00
commit 85ba15c564
31 changed files with 1518167 additions and 0 deletions

View File

@@ -0,0 +1,104 @@
package repository
import (
"material_texture/internal/models"
"gorm.io/gorm"
"gorm.io/gorm/clause"
)
type BindingRepository struct {
db *gorm.DB
}
func NewBindingRepository(db *gorm.DB) *BindingRepository {
return &BindingRepository{db: db}
}
// BindMaterial 绑定材质到多个group_id幂等操作使用upsert
// 优化: 分批处理,避免单条 SQL 过大
func (r *BindingRepository) BindMaterial(materialID int64, groupIDs []string) error {
const batchSize = 1000 // 每批最多 1000 条
for i := 0; i < len(groupIDs); i += batchSize {
end := i + batchSize
if end > len(groupIDs) {
end = len(groupIDs)
}
batch := groupIDs[i:end]
bindings := make([]models.MaterialBinding, len(batch))
for j, groupID := range batch {
bindings[j] = models.MaterialBinding{
MaterialID: materialID,
GroupID: groupID,
}
}
// 使用 ON CONFLICT DO NOTHING 实现幂等
if err := r.db.Clauses(clause.OnConflict{
Columns: []clause.Column{{Name: "material_id"}, {Name: "group_id"}},
DoNothing: true,
}).Create(&bindings).Error; err != nil {
return err
}
}
return nil
}
// UnbindMaterial 解绑材质与指定的group_id
func (r *BindingRepository) UnbindMaterial(materialID int64, groupIDs []string) error {
return r.db.Where("material_id = ? AND group_id IN ?", materialID, groupIDs).
Delete(&models.MaterialBinding{}).Error
}
// GetGroupsByMaterialID 根据材质ID获取所有关联的group_id (分页版本)
func (r *BindingRepository) GetGroupsByMaterialID(materialID int64, page, pageSize int) ([]string, int64, error) {
var groupIDs []string
var total int64
db := r.db.Model(&models.MaterialBinding{}).Where("material_id = ?", materialID)
// 获取总数
if err := db.Count(&total).Error; err != nil {
return nil, 0, err
}
// 分页查询
offset := (page - 1) * pageSize
err := db.Order("created_at DESC").
Offset(offset).
Limit(pageSize).
Pluck("group_id", &groupIDs).Error
return groupIDs, total, err
}
// GetMaterialsByGroupIDs 根据多个group_id获取关联的材质含材质详情
func (r *BindingRepository) GetMaterialsByGroupIDs(groupIDs []string) ([]models.GroupMaterialResult, error) {
var bindings []models.MaterialBinding
err := r.db.Preload("Material").
Where("group_id IN ?", groupIDs).
Find(&bindings).Error
if err != nil {
return nil, err
}
results := make([]models.GroupMaterialResult, len(bindings))
for i, binding := range bindings {
results[i] = models.GroupMaterialResult{
GroupID: binding.GroupID,
Material: binding.Material,
}
}
return results, nil
}
// DeleteByMaterialID 删除材质的所有绑定(材质删除时级联调用)
func (r *BindingRepository) DeleteByMaterialID(materialID int64) error {
return r.db.Where("material_id = ?", materialID).
Delete(&models.MaterialBinding{}).Error
}