mirror of
https://github.com/SamyRai/turash.git
synced 2025-12-26 23:01:33 +00:00
Repository Structure:
- Move files from cluttered root directory into organized structure
- Create archive/ for archived data and scraper results
- Create bugulma/ for the complete application (frontend + backend)
- Create data/ for sample datasets and reference materials
- Create docs/ for comprehensive documentation structure
- Create scripts/ for utility scripts and API tools
Backend Implementation:
- Implement 3 missing backend endpoints identified in gap analysis:
* GET /api/v1/organizations/{id}/matching/direct - Direct symbiosis matches
* GET /api/v1/users/me/organizations - User organizations
* POST /api/v1/proposals/{id}/status - Update proposal status
- Add complete proposal domain model, repository, and service layers
- Create database migration for proposals table
- Fix CLI server command registration issue
API Documentation:
- Add comprehensive proposals.md API documentation
- Update README.md with Users and Proposals API sections
- Document all request/response formats, error codes, and business rules
Code Quality:
- Follow existing Go backend architecture patterns
- Add proper error handling and validation
- Match frontend expected response schemas
- Maintain clean separation of concerns (handler -> service -> repository)
151 lines
5.2 KiB
Go
151 lines
5.2 KiB
Go
package repository
|
|
|
|
import (
|
|
"bugulma/backend/internal/domain"
|
|
"context"
|
|
"encoding/json"
|
|
"time"
|
|
|
|
"github.com/google/uuid"
|
|
"gorm.io/datatypes"
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
// MatchRepository implements domain.MatchRepository with GORM
|
|
type MatchRepository struct {
|
|
*BaseRepository[domain.Match]
|
|
}
|
|
|
|
// NewMatchRepository creates a new GORM-based match repository
|
|
func NewMatchRepository(db *gorm.DB) domain.MatchRepository {
|
|
return &MatchRepository{
|
|
BaseRepository: NewBaseRepository[domain.Match](db),
|
|
}
|
|
}
|
|
|
|
// GetByResourceID retrieves matches involving a specific resource flow
|
|
func (r *MatchRepository) GetByResourceID(ctx context.Context, resourceID string) ([]*domain.Match, error) {
|
|
return r.FindWhereWithContext(ctx, "source_resource_id = ? OR target_resource_id = ?", resourceID, resourceID)
|
|
}
|
|
|
|
// GetByOrganizationID retrieves matches involving a specific organization
|
|
func (r *MatchRepository) GetByOrganizationID(ctx context.Context, organizationID string) ([]*domain.Match, error) {
|
|
var matches []*domain.Match
|
|
query := `
|
|
SELECT m.* FROM matches m
|
|
JOIN resource_flows rf1 ON m.source_resource_id = rf1.id
|
|
JOIN resource_flows rf2 ON m.target_resource_id = rf2.id
|
|
WHERE rf1.organization_id = ? OR rf2.organization_id = ?
|
|
`
|
|
result := r.DB().WithContext(ctx).Raw(query, organizationID, organizationID).Scan(&matches)
|
|
if result.Error != nil {
|
|
return nil, result.Error
|
|
}
|
|
return matches, nil
|
|
}
|
|
|
|
// GetByStatus retrieves matches by status
|
|
func (r *MatchRepository) GetByStatus(ctx context.Context, status domain.MatchStatus) ([]*domain.Match, error) {
|
|
return r.FindWhereWithContext(ctx, "status = ?", status)
|
|
}
|
|
|
|
// GetWithNegotiationHistory retrieves a match with its complete negotiation history
|
|
func (r *MatchRepository) GetWithNegotiationHistory(matchID string) (*domain.Match, error) {
|
|
var match domain.Match
|
|
err := r.DB().Preload("NegotiationHistory").Where("id = ?", matchID).First(&match).Error
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &match, nil
|
|
}
|
|
|
|
// GetTopMatches retrieves the top matches ordered by compatibility score
|
|
func (r *MatchRepository) GetTopMatches(ctx context.Context, limit int) ([]*domain.Match, error) {
|
|
var matches []*domain.Match
|
|
result := r.DB().WithContext(ctx).
|
|
Where("status = ?", domain.MatchStatusSuggested).
|
|
Order("compatibility_score DESC, economic_value DESC").
|
|
Limit(limit).
|
|
Find(&matches)
|
|
if result.Error != nil {
|
|
return nil, result.Error
|
|
}
|
|
return matches, nil
|
|
}
|
|
|
|
// CheckReservationConflicts checks if a resource has conflicting reservations
|
|
func (r *MatchRepository) CheckReservationConflicts(ctx context.Context, resourceID string) ([]*domain.Match, error) {
|
|
now := time.Now()
|
|
var matches []*domain.Match
|
|
result := r.DB().WithContext(ctx).Where(
|
|
"(source_resource_id = ? OR target_resource_id = ?) AND status IN (?, ?) AND (reserved_until IS NULL OR reserved_until > ?)",
|
|
resourceID, resourceID,
|
|
domain.MatchStatusReserved, domain.MatchStatusContracted,
|
|
now,
|
|
).Find(&matches)
|
|
if result.Error != nil {
|
|
return nil, result.Error
|
|
}
|
|
return matches, nil
|
|
}
|
|
|
|
// UpdateStatus updates the match status and adds a history entry
|
|
func (r *MatchRepository) UpdateStatus(ctx context.Context, matchID string, newStatus domain.MatchStatus, actor string, notes string) error {
|
|
return r.Transaction(func(tx *gorm.DB) error {
|
|
// Get the current match
|
|
var match domain.Match
|
|
if err := tx.WithContext(ctx).First(&match, "id = ?", matchID).Error; err != nil {
|
|
return err
|
|
}
|
|
|
|
oldStatus := match.Status
|
|
match.Status = newStatus
|
|
|
|
// Add negotiation history entry
|
|
negotiationEntry := &domain.NegotiationHistoryEntry{
|
|
ID: uuid.New().String(),
|
|
MatchID: matchID,
|
|
Timestamp: time.Now(),
|
|
ActorID: actor,
|
|
Action: "status_change",
|
|
Notes: notes,
|
|
}
|
|
|
|
// Set old/new values
|
|
oldValue, _ := json.Marshal(map[string]interface{}{"status": oldStatus})
|
|
newValue, _ := json.Marshal(map[string]interface{}{"status": newStatus})
|
|
negotiationEntry.OldValue = datatypes.JSON(oldValue)
|
|
negotiationEntry.NewValue = datatypes.JSON(newValue)
|
|
|
|
// Save negotiation history entry
|
|
if err := tx.WithContext(ctx).Create(negotiationEntry).Error; err != nil {
|
|
return err
|
|
}
|
|
|
|
// Save the match
|
|
return tx.WithContext(ctx).Save(&match).Error
|
|
})
|
|
}
|
|
|
|
// GetByOrganizationIDAndStatus retrieves matches for an organization with a specific status
|
|
func (r *MatchRepository) GetByOrganizationIDAndStatus(ctx context.Context, orgID string, status domain.MatchStatus) ([]*domain.Match, error) {
|
|
var matches []*domain.Match
|
|
err := r.DB().WithContext(ctx).Where("organization_id = ? AND status = ?", orgID, status).Find(&matches).Error
|
|
return matches, err
|
|
}
|
|
|
|
// GetExpiredReservations finds matches with expired reservations
|
|
func (r *MatchRepository) GetExpiredReservations() ([]*domain.Match, error) {
|
|
var matches []*domain.Match
|
|
now := time.Now()
|
|
err := r.DB().Where("status = ? AND reserved_until < ?", domain.MatchStatusReserved, now).Find(&matches).Error
|
|
return matches, err
|
|
}
|
|
|
|
// GetPendingNegotiations finds matches in negotiation status
|
|
func (r *MatchRepository) GetPendingNegotiations() ([]*domain.Match, error) {
|
|
var matches []*domain.Match
|
|
err := r.DB().Where("status = ?", domain.MatchStatusNegotiating).Find(&matches).Error
|
|
return matches, err
|
|
}
|