152 lines
5.6 KiB
Python
152 lines
5.6 KiB
Python
import uuid
|
|
|
|
from django.conf import settings
|
|
from django.db import models
|
|
|
|
|
|
class MathIdentity(models.Model):
|
|
code = models.CharField(max_length=8, unique=True)
|
|
name = models.CharField(max_length=80)
|
|
clan = models.CharField(max_length=40)
|
|
mathematician = models.CharField(max_length=80)
|
|
description = models.TextField(blank=True)
|
|
initial_abilities = models.JSONField(default=dict, blank=True)
|
|
portrait = models.CharField(max_length=200, blank=True)
|
|
|
|
def __str__(self):
|
|
return f"{self.code} {self.name}"
|
|
|
|
|
|
class MathBTIAssessment(models.Model):
|
|
version = models.CharField(max_length=30, unique=True)
|
|
title = models.CharField(max_length=120)
|
|
definition = models.JSONField()
|
|
is_published = models.BooleanField(default=False)
|
|
published_at = models.DateTimeField(null=True, blank=True)
|
|
|
|
def __str__(self):
|
|
return self.title
|
|
|
|
|
|
class MathBTIResult(models.Model):
|
|
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
|
|
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name="mathbti_results")
|
|
assessment = models.ForeignKey(MathBTIAssessment, on_delete=models.PROTECT)
|
|
identity = models.ForeignKey(MathIdentity, on_delete=models.PROTECT)
|
|
answers = models.JSONField(default=list)
|
|
axis_scores = models.JSONField(default=dict)
|
|
created_at = models.DateTimeField(auto_now_add=True)
|
|
|
|
class Meta:
|
|
ordering = ["-created_at"]
|
|
|
|
|
|
class Character(models.Model):
|
|
slug = models.SlugField(unique=True)
|
|
name = models.CharField(max_length=80)
|
|
profile = models.TextField(blank=True)
|
|
metadata = models.JSONField(default=dict, blank=True)
|
|
|
|
def __str__(self):
|
|
return self.name
|
|
|
|
|
|
class Story(models.Model):
|
|
class Kind(models.TextChoices):
|
|
FLAGSHIP = "flagship", "旗舰人生"
|
|
SKILL = "skill", "人物 Skill"
|
|
SPECIAL = "special", "特别篇"
|
|
|
|
slug = models.SlugField(unique=True)
|
|
title = models.CharField(max_length=120)
|
|
summary = models.TextField(blank=True)
|
|
kind = models.CharField(max_length=16, choices=Kind.choices, default=Kind.FLAGSHIP)
|
|
estimated_minutes = models.PositiveIntegerField(default=20)
|
|
is_visible = models.BooleanField(default=True)
|
|
|
|
def __str__(self):
|
|
return self.title
|
|
|
|
|
|
class StoryVersion(models.Model):
|
|
story = models.ForeignKey(Story, on_delete=models.CASCADE, related_name="versions")
|
|
version = models.PositiveIntegerField()
|
|
content = models.JSONField()
|
|
is_published = models.BooleanField(default=False)
|
|
published_at = models.DateTimeField(null=True, blank=True)
|
|
created_at = models.DateTimeField(auto_now_add=True)
|
|
|
|
class Meta:
|
|
constraints = [
|
|
models.UniqueConstraint(fields=("story", "version"), name="unique_story_version")
|
|
]
|
|
ordering = ["story", "-version"]
|
|
|
|
def __str__(self):
|
|
return f"{self.story} v{self.version}"
|
|
|
|
|
|
class StoryRun(models.Model):
|
|
class Status(models.TextChoices):
|
|
ACTIVE = "active", "进行中"
|
|
COMPLETED = "completed", "已完成"
|
|
ABANDONED = "abandoned", "已放弃"
|
|
|
|
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
|
|
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name="story_runs")
|
|
story_version = models.ForeignKey(StoryVersion, on_delete=models.PROTECT, related_name="runs")
|
|
current_node = models.CharField(max_length=100)
|
|
state = models.JSONField(default=dict)
|
|
status = models.CharField(max_length=16, choices=Status.choices, default=Status.ACTIVE)
|
|
ending_code = models.CharField(max_length=80, blank=True)
|
|
started_at = models.DateTimeField(auto_now_add=True)
|
|
updated_at = models.DateTimeField(auto_now=True)
|
|
completed_at = models.DateTimeField(null=True, blank=True)
|
|
|
|
class Meta:
|
|
ordering = ["-updated_at"]
|
|
|
|
|
|
class StoryChoice(models.Model):
|
|
run = models.ForeignKey(StoryRun, on_delete=models.CASCADE, related_name="choices")
|
|
sequence = models.PositiveIntegerField()
|
|
idempotency_key = models.CharField(max_length=80, null=True, blank=True)
|
|
node_id = models.CharField(max_length=100)
|
|
choice_index = models.PositiveIntegerField()
|
|
choice_text = models.CharField(max_length=300)
|
|
effects = models.JSONField(default=dict, blank=True)
|
|
created_at = models.DateTimeField(auto_now_add=True)
|
|
|
|
class Meta:
|
|
constraints = [
|
|
models.UniqueConstraint(fields=("run", "sequence"), name="unique_run_choice_sequence"),
|
|
models.UniqueConstraint(
|
|
fields=("run", "idempotency_key"), name="unique_run_choice_idempotency"
|
|
),
|
|
]
|
|
ordering = ["sequence"]
|
|
|
|
|
|
class UserRelationship(models.Model):
|
|
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
|
|
character = models.ForeignKey(Character, on_delete=models.CASCADE)
|
|
trust = models.SmallIntegerField(default=0)
|
|
rivalry = models.SmallIntegerField(default=0)
|
|
debt = models.SmallIntegerField(default=0)
|
|
cooperation = models.SmallIntegerField(default=0)
|
|
metadata = models.JSONField(default=dict, blank=True)
|
|
|
|
class Meta:
|
|
constraints = [
|
|
models.UniqueConstraint(fields=("user", "character"), name="unique_user_character")
|
|
]
|
|
|
|
|
|
class SkillPackage(models.Model):
|
|
story = models.OneToOneField(Story, on_delete=models.CASCADE, related_name="skill_package")
|
|
real_person = models.CharField(max_length=120)
|
|
historical_context = models.TextField()
|
|
fact_sources = models.JSONField(default=list)
|
|
fictional_scope = models.TextField()
|
|
creator = models.CharField(max_length=120)
|