forked from erp-dev/erp
feat: notifier beta
This commit is contained in:
86
mission/migrations/0001_initial.py
Normal file
86
mission/migrations/0001_initial.py
Normal file
@@ -0,0 +1,86 @@
|
||||
# Generated by Django 5.2.8 on 2026-04-10 02:39
|
||||
|
||||
import django.db.models.deletion
|
||||
import django.utils.timezone
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
("basic_info", "0025_customer_uniq_customer_merchant_name"),
|
||||
("contenttypes", "0002_remove_content_type_name"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name="Mission",
|
||||
fields=[
|
||||
("created_at", models.DateTimeField(auto_now_add=True, verbose_name="创建时间")),
|
||||
("updated_at", models.DateTimeField(auto_now=True, verbose_name="更新时间")),
|
||||
("id", models.BigAutoField(primary_key=True, serialize=False)),
|
||||
("description", models.TextField(verbose_name="任务描述")),
|
||||
("category", models.CharField(choices=[("general", "通用")], db_index=True, default="general", max_length=50, verbose_name="任务类型")),
|
||||
("is_urgent", models.BooleanField(db_index=True, default=False, verbose_name="是否紧急")),
|
||||
("is_completed", models.BooleanField(db_index=True, default=False, verbose_name="是否完成")),
|
||||
("is_cancelled", models.BooleanField(db_index=True, default=False, verbose_name="是否取消")),
|
||||
("cancelled_at", models.DateTimeField(blank=True, null=True, verbose_name="取消时间")),
|
||||
("content_id", models.PositiveBigIntegerField(blank=True, db_index=True, null=True, verbose_name="关联对象ID")),
|
||||
("cancelled_by", models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name="cancelled_missions", to="basic_info.employee", verbose_name="取消人")),
|
||||
("content_type", models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name="missions", to="contenttypes.contenttype", verbose_name="关联对象类型")),
|
||||
("creator", models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name="created_missions", to="basic_info.employee", verbose_name="任务创建者")),
|
||||
],
|
||||
options={
|
||||
"verbose_name": "任务",
|
||||
"verbose_name_plural": "任务",
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name="MissionParticipant",
|
||||
fields=[
|
||||
("created_at", models.DateTimeField(auto_now_add=True, verbose_name="创建时间")),
|
||||
("updated_at", models.DateTimeField(auto_now=True, verbose_name="更新时间")),
|
||||
("id", models.BigAutoField(primary_key=True, serialize=False)),
|
||||
("employee", models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name="mission_participations", to="basic_info.employee", verbose_name="参与者")),
|
||||
("mission", models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name="participants", to="mission.mission", verbose_name="任务")),
|
||||
],
|
||||
options={
|
||||
"verbose_name": "任务参与者",
|
||||
"verbose_name_plural": "任务参与者",
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name="MissionReply",
|
||||
fields=[
|
||||
("created_at", models.DateTimeField(auto_now_add=True, verbose_name="创建时间")),
|
||||
("updated_at", models.DateTimeField(auto_now=True, verbose_name="更新时间")),
|
||||
("id", models.BigAutoField(primary_key=True, serialize=False)),
|
||||
("content", models.TextField(verbose_name="回应内容")),
|
||||
("replied_at", models.DateTimeField(db_index=True, default=django.utils.timezone.now, verbose_name="回应时间")),
|
||||
("ends_task", models.BooleanField(db_index=True, default=False, verbose_name="是否结束任务")),
|
||||
("is_rejected", models.BooleanField(db_index=True, default=False, verbose_name="是否被撤销")),
|
||||
("rejected_at", models.DateTimeField(blank=True, null=True, verbose_name="撤销时间")),
|
||||
("mission", models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name="replies", to="mission.mission", verbose_name="任务")),
|
||||
("responder", models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name="mission_replies", to="basic_info.employee", verbose_name="回应者")),
|
||||
],
|
||||
options={
|
||||
"verbose_name": "任务回应",
|
||||
"verbose_name_plural": "任务回应",
|
||||
"ordering": ["replied_at", "id"],
|
||||
},
|
||||
),
|
||||
migrations.AddIndex(
|
||||
model_name="mission",
|
||||
index=models.Index(fields=["content_type", "content_id"], name="mission_mis_content_be6081_idx"),
|
||||
),
|
||||
migrations.AddIndex(
|
||||
model_name="mission",
|
||||
index=models.Index(fields=["is_completed", "is_cancelled"], name="mission_mis_is_comp_299039_idx"),
|
||||
),
|
||||
migrations.AddConstraint(
|
||||
model_name="missionparticipant",
|
||||
constraint=models.UniqueConstraint(fields=("mission", "employee"), name="unique_mission_participant"),
|
||||
),
|
||||
]
|
||||
125
mission/migrations/0002_add_merchant_and_reply_rejected_by.py
Normal file
125
mission/migrations/0002_add_merchant_and_reply_rejected_by.py
Normal file
@@ -0,0 +1,125 @@
|
||||
# Generated by Django 5.2.8 on 2026-04-10 03:36
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
def populate_merchant_fields(apps, schema_editor):
|
||||
Mission = apps.get_model("mission", "Mission")
|
||||
MissionParticipant = apps.get_model("mission", "MissionParticipant")
|
||||
MissionReply = apps.get_model("mission", "MissionReply")
|
||||
|
||||
for mission in Mission.objects.select_related("creator").filter(merchant__isnull=True):
|
||||
mission.merchant_id = mission.creator.merchant_id
|
||||
mission.save(update_fields=["merchant"])
|
||||
|
||||
for participant in MissionParticipant.objects.select_related("mission").filter(merchant__isnull=True):
|
||||
participant.merchant_id = participant.mission.merchant_id
|
||||
participant.save(update_fields=["merchant"])
|
||||
|
||||
for reply in MissionReply.objects.select_related("mission").filter(merchant__isnull=True):
|
||||
reply.merchant_id = reply.mission.merchant_id
|
||||
reply.save(update_fields=["merchant"])
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("basic_info", "0025_customer_uniq_customer_merchant_name"),
|
||||
("mission", "0001_initial"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveIndex(
|
||||
model_name="mission",
|
||||
name="mission_mis_content_be6081_idx",
|
||||
),
|
||||
migrations.RemoveIndex(
|
||||
model_name="mission",
|
||||
name="mission_mis_is_comp_299039_idx",
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="mission",
|
||||
name="merchant",
|
||||
field=models.ForeignKey(
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.PROTECT,
|
||||
related_name="missions",
|
||||
to="basic_info.merchant",
|
||||
verbose_name="所属商户",
|
||||
),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="missionparticipant",
|
||||
name="merchant",
|
||||
field=models.ForeignKey(
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.PROTECT,
|
||||
related_name="mission_participants",
|
||||
to="basic_info.merchant",
|
||||
verbose_name="所属商户",
|
||||
),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="missionreply",
|
||||
name="merchant",
|
||||
field=models.ForeignKey(
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.PROTECT,
|
||||
related_name="mission_replies",
|
||||
to="basic_info.merchant",
|
||||
verbose_name="所属商户",
|
||||
),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="missionreply",
|
||||
name="rejected_by",
|
||||
field=models.ForeignKey(
|
||||
blank=True,
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.PROTECT,
|
||||
related_name="rejected_mission_replies",
|
||||
to="basic_info.employee",
|
||||
verbose_name="撤销人",
|
||||
),
|
||||
),
|
||||
migrations.RunPython(populate_merchant_fields, migrations.RunPython.noop),
|
||||
migrations.AlterField(
|
||||
model_name="mission",
|
||||
name="merchant",
|
||||
field=models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.PROTECT,
|
||||
related_name="missions",
|
||||
to="basic_info.merchant",
|
||||
verbose_name="所属商户",
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="missionparticipant",
|
||||
name="merchant",
|
||||
field=models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.PROTECT,
|
||||
related_name="mission_participants",
|
||||
to="basic_info.merchant",
|
||||
verbose_name="所属商户",
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="missionreply",
|
||||
name="merchant",
|
||||
field=models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.PROTECT,
|
||||
related_name="mission_replies",
|
||||
to="basic_info.merchant",
|
||||
verbose_name="所属商户",
|
||||
),
|
||||
),
|
||||
migrations.AddIndex(
|
||||
model_name="mission",
|
||||
index=models.Index(fields=["merchant", "content_type", "content_id"], name="mission_mis_merchan_af589f_idx"),
|
||||
),
|
||||
migrations.AddIndex(
|
||||
model_name="mission",
|
||||
index=models.Index(fields=["merchant", "is_completed", "is_cancelled"], name="mission_mis_merchan_6d6d37_idx"),
|
||||
),
|
||||
]
|
||||
17
mission/migrations/0003_alter_mission_options.py
Normal file
17
mission/migrations/0003_alter_mission_options.py
Normal file
@@ -0,0 +1,17 @@
|
||||
# Generated by Django 5.2.8 on 2026-04-10 04:40
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('mission', '0002_add_merchant_and_reply_rejected_by'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='mission',
|
||||
options={'permissions': [('reopen_mission', 'Can reopen mission'), ('reject_mission_reply', 'Can reject mission reply')], 'verbose_name': '任务', 'verbose_name_plural': '任务'},
|
||||
),
|
||||
]
|
||||
101
mission/migrations/0004_missioncategory_refactor.py
Normal file
101
mission/migrations/0004_missioncategory_refactor.py
Normal file
@@ -0,0 +1,101 @@
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
LEGACY_CATEGORY_NAME_MAP = {
|
||||
"general": "通用",
|
||||
}
|
||||
|
||||
|
||||
def forwards(apps, schema_editor):
|
||||
Mission = apps.get_model("mission", "Mission")
|
||||
MissionCategory = apps.get_model("mission", "MissionCategory")
|
||||
db_alias = schema_editor.connection.alias
|
||||
category_cache = {}
|
||||
|
||||
for mission in Mission.objects.using(db_alias).all().only("id", "merchant_id", "category").iterator():
|
||||
legacy_value = (mission.category or "").strip() or "general"
|
||||
category_name = LEGACY_CATEGORY_NAME_MAP.get(legacy_value, legacy_value)
|
||||
cache_key = (mission.merchant_id, category_name)
|
||||
|
||||
category = category_cache.get(cache_key)
|
||||
if category is None:
|
||||
category, _ = MissionCategory.objects.using(db_alias).get_or_create(
|
||||
merchant_id=mission.merchant_id,
|
||||
name=category_name,
|
||||
)
|
||||
category_cache[cache_key] = category
|
||||
|
||||
Mission.objects.using(db_alias).filter(id=mission.id).update(category_ref_id=category.id)
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("basic_info", "0025_customer_uniq_customer_merchant_name"),
|
||||
("mission", "0003_alter_mission_options"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name="MissionCategory",
|
||||
fields=[
|
||||
("created_at", models.DateTimeField(auto_now_add=True, verbose_name="创建时间")),
|
||||
("updated_at", models.DateTimeField(auto_now=True, verbose_name="更新时间")),
|
||||
("id", models.BigAutoField(primary_key=True, serialize=False)),
|
||||
("name", models.CharField(max_length=50, verbose_name="分类名称")),
|
||||
(
|
||||
"merchant",
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.PROTECT,
|
||||
related_name="mission_categories",
|
||||
to="basic_info.merchant",
|
||||
verbose_name="所属商户",
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
"verbose_name": "任务分类",
|
||||
"verbose_name_plural": "任务分类",
|
||||
"ordering": ["id"],
|
||||
"constraints": [
|
||||
models.UniqueConstraint(
|
||||
fields=("merchant", "name"),
|
||||
name="unique_mission_category_name_per_merchant",
|
||||
),
|
||||
],
|
||||
},
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="mission",
|
||||
name="category_ref",
|
||||
field=models.ForeignKey(
|
||||
blank=True,
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.PROTECT,
|
||||
related_name="+",
|
||||
to="mission.missioncategory",
|
||||
verbose_name="任务类型",
|
||||
),
|
||||
),
|
||||
migrations.RunPython(forwards, migrations.RunPython.noop),
|
||||
migrations.RemoveField(
|
||||
model_name="mission",
|
||||
name="category",
|
||||
),
|
||||
migrations.RenameField(
|
||||
model_name="mission",
|
||||
old_name="category_ref",
|
||||
new_name="category",
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="mission",
|
||||
name="category",
|
||||
field=models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.PROTECT,
|
||||
related_name="missions",
|
||||
to="mission.missioncategory",
|
||||
verbose_name="任务类型",
|
||||
),
|
||||
),
|
||||
]
|
||||
0
mission/migrations/__init__.py
Normal file
0
mission/migrations/__init__.py
Normal file
Reference in New Issue
Block a user