hensei-api/docs/planning/crew-feature-plan.md

13 KiB

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

- 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

- 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

- 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

- 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

- 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)

- 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

# 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

# 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