458 lines
No EOL
13 KiB
Markdown
458 lines
No EOL
13 KiB
Markdown
# Crew Feature Plan
|
|
|
|
## Overview
|
|
|
|
The Crew system enables players to form groups of up to 30 members to collaborate, share strategies, and compete in Unite and Fight events. Crews provide a social layer to the application with hierarchical roles, shared content visibility, and performance tracking capabilities.
|
|
|
|
## Business Requirements
|
|
|
|
### Core Concepts
|
|
|
|
#### Crew Structure
|
|
- **Size Limit**: Maximum 30 members per crew
|
|
- **Roles Hierarchy**:
|
|
- **Captain**: The crew creator with full administrative privileges
|
|
- **Subcaptains**: Up to 3 members with elevated permissions
|
|
- **Members**: Regular crew participants with standard access
|
|
|
|
#### Key Features
|
|
1. **Crew Management**: Creation, invitation, membership control
|
|
2. **Gamertags**: 4-character tags displayed alongside member names
|
|
3. **Unite and Fight**: Event participation and score tracking
|
|
4. **Crew Feed**: Private content stream for crew-only parties, guides, and future posts
|
|
5. **Performance Analytics**: Historical tracking and visualization of member contributions
|
|
|
|
### User Stories
|
|
|
|
#### As a Captain
|
|
- I want to create a crew and invite players to join
|
|
- I want to appoint up to 3 subcaptains to help manage the crew
|
|
- I want to remove members who are inactive or problematic
|
|
- I want to set crew rules that all members can view
|
|
- I want to track member performance in Unite and Fight events
|
|
|
|
#### As a Subcaptain
|
|
- I want to help manage crew by inviting new members
|
|
- I want to update crew rules and information
|
|
- I want to record Unite and Fight scores for members
|
|
- I want to set gamertags for crew representation
|
|
|
|
#### As a Member
|
|
- I want to join a crew via invitation link
|
|
- I want to view crew rules and information
|
|
- I want to see my Unite and Fight performance history
|
|
- I want to choose whether to display my crew's gamertag
|
|
- I want to see crew-only parties and guides in the feed
|
|
|
|
#### As a System Administrator
|
|
- I want to add new Unite and Fight events when announced by Cygames
|
|
- I want to manage event dates and parameters
|
|
- I want to monitor crew activities for policy violations
|
|
|
|
## Technical Design
|
|
|
|
### Database Schema
|
|
|
|
#### crews
|
|
```sql
|
|
- id: uuid (primary key)
|
|
- name: string (unique, not null)
|
|
- captain_id: uuid (foreign key to users, not null)
|
|
- gamertag: string (4 characters, unique, can be null)
|
|
- rules: text
|
|
- member_count: integer (counter cache, default 1)
|
|
- created_at: timestamp
|
|
- updated_at: timestamp
|
|
|
|
Indexes:
|
|
- unique index on name
|
|
- unique index on gamertag (where not null)
|
|
- index on captain_id
|
|
- index on created_at
|
|
```
|
|
|
|
#### crew_memberships
|
|
```sql
|
|
- id: uuid (primary key)
|
|
- crew_id: uuid (foreign key to crews, not null)
|
|
- user_id: uuid (foreign key to users, not null)
|
|
- role: integer (0=member, 1=subcaptain, 2=captain)
|
|
- display_gamertag: boolean (default true)
|
|
- joined_at: timestamp (default now)
|
|
- created_at: timestamp
|
|
- updated_at: timestamp
|
|
|
|
Indexes:
|
|
- unique index on [crew_id, user_id]
|
|
- index on crew_id
|
|
- index on user_id
|
|
- index on role
|
|
- index on joined_at
|
|
```
|
|
|
|
#### crew_invitations
|
|
```sql
|
|
- id: uuid (primary key)
|
|
- crew_id: uuid (foreign key to crews, not null)
|
|
- invited_by_id: uuid (foreign key to users, not null)
|
|
- token: string (unique, not null)
|
|
- expires_at: timestamp (default 7 days from creation)
|
|
- used_at: timestamp (null)
|
|
- used_by_id: uuid (foreign key to users, null)
|
|
- created_at: timestamp
|
|
- updated_at: timestamp
|
|
|
|
Indexes:
|
|
- unique index on token
|
|
- index on crew_id
|
|
- index on expires_at
|
|
- index on [crew_id, used_at] (for tracking active invitations)
|
|
```
|
|
|
|
#### unite_and_fights
|
|
```sql
|
|
- id: uuid (primary key)
|
|
- name: string (not null)
|
|
- event_number: integer (sequential, unique)
|
|
- starts_at: timestamp (not null)
|
|
- ends_at: timestamp (not null)
|
|
- created_by_id: uuid (foreign key to users, not null)
|
|
- created_at: timestamp
|
|
- updated_at: timestamp
|
|
|
|
Indexes:
|
|
- unique index on event_number
|
|
- index on starts_at
|
|
- index on ends_at
|
|
- index on [starts_at, ends_at] (for finding active events)
|
|
```
|
|
|
|
#### unf_scores
|
|
```sql
|
|
- id: uuid (primary key)
|
|
- unite_and_fight_id: uuid (foreign key to unite_and_fights, not null)
|
|
- crew_id: uuid (foreign key to crews, not null)
|
|
- user_id: uuid (foreign key to users, not null)
|
|
- honors: bigint (not null, default 0)
|
|
- recorded_by_id: uuid (foreign key to users, not null)
|
|
- day_number: integer (1-7, not null)
|
|
- created_at: timestamp
|
|
- updated_at: timestamp
|
|
|
|
Indexes:
|
|
- unique index on [unite_and_fight_id, crew_id, user_id, day_number]
|
|
- index on unite_and_fight_id
|
|
- index on crew_id
|
|
- index on user_id
|
|
- index on [crew_id, unite_and_fight_id] (for crew performance queries)
|
|
- index on honors (for rankings)
|
|
```
|
|
|
|
#### crew_feeds (future table for reference)
|
|
```sql
|
|
- id: uuid (primary key)
|
|
- crew_id: uuid (foreign key to crews, not null)
|
|
- feedable_type: string (Party, Guide, Post, etc.)
|
|
- feedable_id: uuid (polymorphic reference)
|
|
- created_at: timestamp
|
|
|
|
Indexes:
|
|
- index on [crew_id, created_at] (for feed queries)
|
|
- index on [feedable_type, feedable_id]
|
|
```
|
|
|
|
### Model Relationships
|
|
|
|
```ruby
|
|
# User model additions
|
|
has_one :crew_membership, dependent: :destroy
|
|
has_one :crew, through: :crew_membership
|
|
has_many :captained_crews, class_name: 'Crew', foreign_key: :captain_id
|
|
has_many :crew_invitations_sent, class_name: 'CrewInvitation', foreign_key: :invited_by_id
|
|
has_many :unf_scores
|
|
has_many :recorded_unf_scores, class_name: 'UnfScore', foreign_key: :recorded_by_id
|
|
|
|
# Crew model
|
|
belongs_to :captain, class_name: 'User'
|
|
has_many :crew_memberships, dependent: :destroy
|
|
has_many :members, through: :crew_memberships, source: :user
|
|
has_many :crew_invitations, dependent: :destroy
|
|
has_many :unf_scores, dependent: :destroy
|
|
has_many :subcaptains, -> { where(crew_memberships: { role: 1 }) },
|
|
through: :crew_memberships, source: :user
|
|
|
|
# CrewMembership model
|
|
belongs_to :crew, counter_cache: :member_count
|
|
belongs_to :user
|
|
enum role: { member: 0, subcaptain: 1, captain: 2 }
|
|
|
|
# CrewInvitation model
|
|
belongs_to :crew
|
|
belongs_to :invited_by, class_name: 'User'
|
|
belongs_to :used_by, class_name: 'User', optional: true
|
|
|
|
# UniteAndFight model
|
|
has_many :unf_scores, dependent: :destroy
|
|
belongs_to :created_by, class_name: 'User'
|
|
|
|
# UnfScore model
|
|
belongs_to :unite_and_fight
|
|
belongs_to :crew
|
|
belongs_to :user
|
|
belongs_to :recorded_by, class_name: 'User'
|
|
```
|
|
|
|
### API Design
|
|
|
|
#### Crew Management
|
|
|
|
##### Crew CRUD
|
|
```
|
|
POST /api/v1/crews
|
|
Body: { name, rules?, gamertag? }
|
|
Response: Created crew with captain membership
|
|
|
|
GET /api/v1/crews/:id
|
|
Response: Crew details with members list
|
|
|
|
PUT /api/v1/crews/:id
|
|
Body: { name?, rules?, gamertag? }
|
|
Response: Updated crew (captain/subcaptain only)
|
|
|
|
DELETE /api/v1/crews/:id
|
|
Response: Success (captain only, disbands crew)
|
|
|
|
GET /api/v1/crews/my
|
|
Response: Current user's crew with full details
|
|
```
|
|
|
|
##### Member Management
|
|
```
|
|
GET /api/v1/crews/:id/members
|
|
Response: Paginated list of crew members with roles
|
|
|
|
POST /api/v1/crews/:id/members/promote
|
|
Body: { user_id, role: "subcaptain" }
|
|
Response: Updated membership (captain only)
|
|
|
|
DELETE /api/v1/crews/:id/members/:user_id
|
|
Response: Success (captain only)
|
|
|
|
PUT /api/v1/crews/:id/members/me
|
|
Body: { display_gamertag }
|
|
Response: Updated own membership settings
|
|
```
|
|
|
|
##### Invitations
|
|
```
|
|
POST /api/v1/crews/:id/invitations
|
|
Response: { invitation_url, token, expires_at }
|
|
Note: Captain/subcaptain only
|
|
|
|
GET /api/v1/crews/:id/invitations
|
|
Response: List of pending invitations (captain/subcaptain)
|
|
|
|
DELETE /api/v1/crews/:id/invitations/:id
|
|
Response: Revoke invitation (captain/subcaptain)
|
|
|
|
POST /api/v1/crews/join
|
|
Body: { token }
|
|
Response: Joined crew details
|
|
```
|
|
|
|
#### Unite and Fight
|
|
|
|
##### Event Management (Admin)
|
|
```
|
|
GET /api/v1/unite_and_fights
|
|
Response: List of all UnF events
|
|
|
|
POST /api/v1/unite_and_fights
|
|
Body: { name, event_number, starts_at, ends_at }
|
|
Response: Created event (requires level 7+ permissions)
|
|
|
|
PUT /api/v1/unite_and_fights/:id
|
|
Body: { name?, starts_at?, ends_at? }
|
|
Response: Updated event (admin only)
|
|
```
|
|
|
|
##### Score Management
|
|
```
|
|
POST /api/v1/unf_scores
|
|
Body: { unite_and_fight_id, user_id, honors, day_number }
|
|
Response: Created/updated score (captain/subcaptain only)
|
|
|
|
GET /api/v1/crews/:crew_id/unf_scores
|
|
Query: unite_and_fight_id?, user_id?
|
|
Response: Scores for crew, optionally filtered
|
|
|
|
GET /api/v1/unf_scores/performance
|
|
Query: crew_id, user_id?, from_date?, to_date?
|
|
Response: Performance data for graphing
|
|
```
|
|
|
|
#### Crew Feed
|
|
```
|
|
GET /api/v1/crews/:id/feed
|
|
Query: page, limit, type?
|
|
Response: Paginated feed of crew-only content
|
|
```
|
|
|
|
### Authorization & Permissions
|
|
|
|
#### Permission Matrix
|
|
|
|
| Action | Captain | Subcaptain | Member | Non-member |
|
|
|--------|---------|------------|---------|------------|
|
|
| View crew info | ✓ | ✓ | ✓ | ✓ |
|
|
| View member list | ✓ | ✓ | ✓ | ✗ |
|
|
| Update crew info | ✓ | ✓ | ✗ | ✗ |
|
|
| Set gamertag | ✓ | ✓ | ✗ | ✗ |
|
|
| Invite members | ✓ | ✓ | ✗ | ✗ |
|
|
| Remove members | ✓ | ✗ | ✗ | ✗ |
|
|
| Promote to subcaptain | ✓ | ✗ | ✗ | ✗ |
|
|
| Record UnF scores | ✓ | ✓ | ✗ | ✗ |
|
|
| View UnF scores | ✓ | ✓ | ✓ | ✗ |
|
|
| View crew feed | ✓ | ✓ | ✓ | ✗ |
|
|
| Leave crew | ✗* | ✓ | ✓ | ✗ |
|
|
|
|
*Captain must transfer ownership or disband crew
|
|
|
|
#### Authorization Helpers
|
|
|
|
```ruby
|
|
# app/models/user.rb
|
|
def captain_of?(crew)
|
|
crew.captain_id == id
|
|
end
|
|
|
|
def subcaptain_of?(crew)
|
|
crew_membership&.subcaptain? && crew_membership.crew_id == crew.id
|
|
end
|
|
|
|
def member_of?(crew)
|
|
crew_membership&.crew_id == crew.id
|
|
end
|
|
|
|
def can_manage_crew?(crew)
|
|
captain_of?(crew) || subcaptain_of?(crew)
|
|
end
|
|
|
|
def can_invite_to_crew?(crew)
|
|
can_manage_crew?(crew)
|
|
end
|
|
|
|
def can_remove_from_crew?(crew)
|
|
captain_of?(crew)
|
|
end
|
|
|
|
def can_record_unf_scores?(crew)
|
|
can_manage_crew?(crew)
|
|
end
|
|
```
|
|
|
|
### Security Considerations
|
|
|
|
1. **Invitation Security**:
|
|
- Tokens are cryptographically secure random strings
|
|
- Automatic expiration after 7 days
|
|
- One-time use only
|
|
- Rate limiting on join attempts
|
|
|
|
2. **Member Limits**:
|
|
- Enforce 30 member maximum at database level
|
|
- Check before invitation acceptance
|
|
- Atomic operations for membership changes
|
|
|
|
3. **Role Management**:
|
|
- Only captain can promote/demote
|
|
- Maximum 3 subcaptains enforced
|
|
- Captain role transfer requires explicit action
|
|
|
|
4. **Data Privacy**:
|
|
- Crew-only content respects visibility settings
|
|
- UnF scores only visible to crew members
|
|
- Member list public, but details restricted
|
|
|
|
### Performance Considerations
|
|
|
|
1. **Caching**:
|
|
- Cache crew member lists (5-minute TTL)
|
|
- Cache UnF leaderboards (1-hour TTL)
|
|
- Cache crew feed content
|
|
|
|
2. **Database Optimization**:
|
|
- Counter cache for member_count
|
|
- Composite indexes for common queries
|
|
- Partial indexes for active records
|
|
|
|
3. **Query Optimization**:
|
|
- Eager load associations
|
|
- Pagination for member lists and feeds
|
|
- Batch operations for UnF score updates
|
|
|
|
4. **Background Jobs**:
|
|
- Async invitation email sending
|
|
- Scheduled cleanup of expired invitations
|
|
- UnF score aggregation calculations
|
|
|
|
## Implementation Phases
|
|
|
|
### Phase 1: Core Crew System (Week 1-2)
|
|
- Database migrations and models
|
|
- Basic CRUD operations
|
|
- Captain and member roles
|
|
- Invitation system
|
|
|
|
### Phase 2: Advanced Roles & Permissions (Week 3)
|
|
- Subcaptain functionality
|
|
- Permission system
|
|
- Gamertag management
|
|
- Crew rules
|
|
|
|
### Phase 3: Unite and Fight Integration (Week 4-5)
|
|
- UnF event management
|
|
- Score recording system
|
|
- Performance queries
|
|
- Basic reporting
|
|
|
|
### Phase 4: Feed & Analytics (Week 6)
|
|
- Crew feed implementation
|
|
- Integration with parties/guides
|
|
- Performance graphs
|
|
- Historical tracking
|
|
|
|
### Phase 5: Polish & Optimization (Week 7)
|
|
- Performance tuning
|
|
- Caching layer
|
|
- Background jobs
|
|
- Admin tools
|
|
|
|
## Success Metrics
|
|
|
|
1. **Adoption**: 60% of active users join a crew within 3 months
|
|
2. **Engagement**: Average crew has 15+ active members
|
|
3. **Performance**: All crew operations complete within 200ms
|
|
4. **Reliability**: 99.9% uptime for crew services
|
|
5. **UnF Participation**: 80% score recording during events
|
|
|
|
## Future Enhancements
|
|
|
|
1. **Crew Battles**: Inter-crew competitions outside UnF
|
|
2. **Crew Chat**: Real-time messaging system
|
|
3. **Crew Achievements**: Badges and milestones
|
|
4. **Crew Resources**: Shared guides and strategies library
|
|
5. **Crew Recruitment**: Public crew discovery and application system
|
|
6. **Officer Roles**: Additional permission tiers
|
|
7. **Crew Alliances**: Multi-crew coordination
|
|
8. **Automated Scoring**: API integration with game data (if available)
|
|
9. **Mobile Notifications**: Push notifications for crew events
|
|
10. **Crew Statistics**: Advanced analytics and insights
|
|
|
|
## Risk Mitigation
|
|
|
|
1. **Toxic Behavior**: Implement reporting system and moderation tools
|
|
2. **Inactive Crews**: Automatic leadership transfer after inactivity
|
|
3. **Database Load**: Implement read replicas for heavy queries
|
|
4. **Invitation Spam**: Rate limiting and abuse detection
|
|
5. **Score Manipulation**: Audit logs and validation rules |