Files

188 lines
6.5 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 StoryMark(models.Model):
user = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
related_name="story_marks",
)
story = models.ForeignKey(
Story,
on_delete=models.CASCADE,
related_name="collected_marks",
)
source_run = models.ForeignKey(
StoryRun,
null=True,
blank=True,
on_delete=models.SET_NULL,
related_name="acquired_marks",
)
code = models.CharField(max_length=60)
name = models.CharField(max_length=80)
domain = models.CharField(max_length=40)
acquired_at = models.DateTimeField(auto_now_add=True)
class Meta:
constraints = [
models.UniqueConstraint(
fields=("user", "story", "code"),
name="unique_user_story_mark",
)
]
ordering = ["acquired_at"]
def __str__(self):
return f"{self.user} · {self.name}"
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)