package mysqldao import ( "context" "git.lzcwy.cn/zhengchiliu/zt-closet-backend/global" "git.lzcwy.cn/zhengchiliu/zt-closet-backend/internal/mysqlmodel" "gorm.io/gorm" ) // OutfitDao 穿搭数据访问 type OutfitDao struct { db *gorm.DB } // NewOutfitDao 创建穿搭 DAO func NewOutfitDao() *OutfitDao { return &OutfitDao{db: global.MysqlEngine} } // OutfitCreate 创建穿搭 func (d *OutfitDao) OutfitCreate(ctx context.Context, outfit *mysqlmodel.Outfit) error { ctx, span := global.Tracer.Start(ctx, "mysqldao.OutfitCreate") defer span.End() if err := d.db.WithContext(ctx).Create(outfit).Error; err != nil { span.RecordError(err) return err } return nil } // OutfitDeleteByUUID 按 UUID 软删除穿搭 func (d *OutfitDao) OutfitDeleteByUUID(ctx context.Context, outfitUUID string) error { ctx, span := global.Tracer.Start(ctx, "mysqldao.OutfitDeleteByUUID") defer span.End() if err := d.db.WithContext(ctx).Where("outfit_uuid = ?", outfitUUID).Delete(&mysqlmodel.Outfit{}).Error; err != nil { span.RecordError(err) return err } return nil } // OutfitUpdateByUUID 按 UUID 更新穿搭字段 func (d *OutfitDao) OutfitUpdateByUUID(ctx context.Context, outfitUUID string, updates map[string]any) error { ctx, span := global.Tracer.Start(ctx, "mysqldao.OutfitUpdateByUUID") defer span.End() if err := d.db.WithContext(ctx).Model(&mysqlmodel.Outfit{}).Where("outfit_uuid = ?", outfitUUID).Updates(updates).Error; err != nil { span.RecordError(err) return err } return nil } // OutfitUpdateIsPublic 设置穿搭公开状态 func (d *OutfitDao) OutfitUpdateIsPublic(ctx context.Context, outfitUUID string, isPublic int8) error { ctx, span := global.Tracer.Start(ctx, "mysqldao.OutfitUpdateIsPublic") defer span.End() if err := d.db.WithContext(ctx).Model(&mysqlmodel.Outfit{}).Where("outfit_uuid = ?", outfitUUID).UpdateColumn("is_public", isPublic).Error; err != nil { span.RecordError(err) return err } return nil } // OutfitPublish 条件公开穿搭(仅私密 -> 公开 + 审核通过),返回是否更新成功,防并发重复消耗额度。 // 状态字面量:is_public 1=私密 2=公开;audit_status 2=审核通过。 func (d *OutfitDao) OutfitPublish(ctx context.Context, outfitUUID string) (bool, error) { ctx, span := global.Tracer.Start(ctx, "mysqldao.OutfitPublish") defer span.End() res := d.db.WithContext(ctx).Model(&mysqlmodel.Outfit{}). Where("outfit_uuid = ? AND is_public = ?", outfitUUID, int8(1)). Updates(map[string]any{"is_public": int8(2), "audit_status": int8(2)}) if res.Error != nil { span.RecordError(res.Error) return false, res.Error } return res.RowsAffected > 0, nil } // OutfitGetByUUID 按 UUID 查询穿搭 func (d *OutfitDao) OutfitGetByUUID(ctx context.Context, outfitUUID string) (*mysqlmodel.Outfit, error) { ctx, span := global.Tracer.Start(ctx, "mysqldao.OutfitGetByUUID") defer span.End() var outfit mysqlmodel.Outfit if err := d.db.WithContext(ctx).Where("outfit_uuid = ?", outfitUUID).First(&outfit).Error; err != nil { span.RecordError(err) return nil, err } return &outfit, nil } // OutfitGetByID 按主键 ID 查询穿搭 func (d *OutfitDao) OutfitGetByID(ctx context.Context, id int64) (*mysqlmodel.Outfit, error) { ctx, span := global.Tracer.Start(ctx, "mysqldao.OutfitGetByID") defer span.End() var outfit mysqlmodel.Outfit if err := d.db.WithContext(ctx).First(&outfit, id).Error; err != nil { span.RecordError(err) return nil, err } return &outfit, nil } // OutfitGetByIDs 按主键 ID 批量查询穿搭 func (d *OutfitDao) OutfitGetByIDs(ctx context.Context, ids []int64) ([]mysqlmodel.Outfit, error) { ctx, span := global.Tracer.Start(ctx, "mysqldao.OutfitGetByIDs") defer span.End() var list []mysqlmodel.Outfit if len(ids) == 0 { return list, nil } if err := d.db.WithContext(ctx).Where("id IN ?", ids).Find(&list).Error; err != nil { span.RecordError(err) return nil, err } return list, nil } // OutfitGetByUUIDs 按 UUID 批量查询穿搭 func (d *OutfitDao) OutfitGetByUUIDs(ctx context.Context, uuids []string) ([]mysqlmodel.Outfit, error) { ctx, span := global.Tracer.Start(ctx, "mysqldao.OutfitGetByUUIDs") defer span.End() var list []mysqlmodel.Outfit if len(uuids) == 0 { return list, nil } if err := d.db.WithContext(ctx).Where("outfit_uuid IN ?", uuids).Find(&list).Error; err != nil { span.RecordError(err) return nil, err } return list, nil } // OutfitListByUserID 分页查询用户穿搭(isPublic/source 为 0 不筛选) func (d *OutfitDao) OutfitListByUserID(ctx context.Context, userID int64, isPublic, source int8, page, pageSize int) ([]mysqlmodel.Outfit, int64, error) { ctx, span := global.Tracer.Start(ctx, "mysqldao.OutfitListByUserID") defer span.End() var list []mysqlmodel.Outfit var total int64 q := d.db.WithContext(ctx).Model(&mysqlmodel.Outfit{}).Where("user_id = ?", userID) if isPublic != 0 { q = q.Where("is_public = ?", isPublic) } if source != 0 { q = q.Where("source = ?", source) } if err := q.Count(&total).Error; err != nil { span.RecordError(err) return nil, 0, err } if err := q.Offset((page - 1) * pageSize).Limit(pageSize).Order("id DESC").Find(&list).Error; err != nil { span.RecordError(err) return nil, 0, err } return list, total, nil } // OutfitListCommunity 分页查询社区穿搭(其他用户「公开 + 审核通过 + 状态正常」的穿搭)。 // 状态字面量与 service.isOutfitPublicVisible 保持一致:is_public 2=公开;audit_status 2=审核通过;status 1=正常。 func (d *OutfitDao) OutfitListCommunity(ctx context.Context, excludeUserID int64, page, pageSize int) ([]mysqlmodel.Outfit, int64, error) { ctx, span := global.Tracer.Start(ctx, "mysqldao.OutfitListCommunity") defer span.End() var list []mysqlmodel.Outfit var total int64 q := d.db.WithContext(ctx).Model(&mysqlmodel.Outfit{}). Where("is_public = ? AND audit_status = ? AND status = ?", int8(2), int8(2), int8(1)). Where("user_id != ?", excludeUserID) if err := q.Count(&total).Error; err != nil { span.RecordError(err) return nil, 0, err } if err := q.Offset((page - 1) * pageSize).Limit(pageSize).Order("id DESC").Find(&list).Error; err != nil { span.RecordError(err) return nil, 0, err } return list, total, nil } // OutfitCleanupExpiredUnsavedAI 清理「超过保留期仍未保存」的 AI 穿搭(连带明细),返回清理条数。 // 状态字面量:source 2=AI 推荐;status 3=待用户保存。软删除后列表与详情立即不可见。 // 分批 + 限量:单次最多处理 limit 条,避免长事务持锁。 func (d *OutfitDao) OutfitCleanupExpiredUnsavedAI(ctx context.Context, createdBefore int64, limit int) (int64, error) { ctx, span := global.Tracer.Start(ctx, "mysqldao.OutfitCleanupExpiredUnsavedAI") defer span.End() var ids []int64 if err := d.db.WithContext(ctx).Model(&mysqlmodel.Outfit{}). Where("source = ? AND status = ? AND created_at < ?", int8(2), int8(3), createdBefore). Order("id ASC").Limit(limit).Pluck("id", &ids).Error; err != nil { span.RecordError(err) return 0, err } if len(ids) == 0 { return 0, nil } if err := d.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error { if err := tx.Where("outfit_id IN ?", ids).Delete(&mysqlmodel.OutfitItem{}).Error; err != nil { return err } return tx.Where("id IN ?", ids).Delete(&mysqlmodel.Outfit{}).Error }); err != nil { span.RecordError(err) return 0, err } return int64(len(ids)), nil } // OutfitItemDao 穿搭明细数据访问 type OutfitItemDao struct { db *gorm.DB } // NewOutfitItemDao 创建穿搭明细 DAO func NewOutfitItemDao() *OutfitItemDao { return &OutfitItemDao{db: global.MysqlEngine} } // OutfitItemCreateBatch 批量创建穿搭明细 func (d *OutfitItemDao) OutfitItemCreateBatch(ctx context.Context, items []mysqlmodel.OutfitItem) error { ctx, span := global.Tracer.Start(ctx, "mysqldao.OutfitItemCreateBatch") defer span.End() if len(items) == 0 { return nil } if err := d.db.WithContext(ctx).Create(&items).Error; err != nil { span.RecordError(err) return err } return nil } // OutfitItemDeleteByOutfitID 删除穿搭明细 func (d *OutfitItemDao) OutfitItemDeleteByOutfitID(ctx context.Context, outfitID int64) error { ctx, span := global.Tracer.Start(ctx, "mysqldao.OutfitItemDeleteByOutfitID") defer span.End() if err := d.db.WithContext(ctx).Where("outfit_id = ?", outfitID).Delete(&mysqlmodel.OutfitItem{}).Error; err != nil { span.RecordError(err) return err } return nil } // OutfitItemListByOutfitID 查询穿搭明细 func (d *OutfitItemDao) OutfitItemListByOutfitID(ctx context.Context, outfitID int64) ([]mysqlmodel.OutfitItem, error) { ctx, span := global.Tracer.Start(ctx, "mysqldao.OutfitItemListByOutfitID") defer span.End() var list []mysqlmodel.OutfitItem if err := d.db.WithContext(ctx).Where("outfit_id = ?", outfitID).Find(&list).Error; err != nil { span.RecordError(err) return nil, err } return list, nil } // OutfitItemListByOutfitIDs 批量查询穿搭明细 func (d *OutfitItemDao) OutfitItemListByOutfitIDs(ctx context.Context, outfitIDs []int64) ([]mysqlmodel.OutfitItem, error) { ctx, span := global.Tracer.Start(ctx, "mysqldao.OutfitItemListByOutfitIDs") defer span.End() var list []mysqlmodel.OutfitItem if len(outfitIDs) == 0 { return list, nil } if err := d.db.WithContext(ctx).Where("outfit_id IN ?", outfitIDs).Find(&list).Error; err != nil { span.RecordError(err) return nil, err } return list, nil }