feat: add software entitlement foundations
This commit is contained in:
+149
@@ -0,0 +1,149 @@
|
||||
# Generated by Django 5.2.15 on 2026-07-21 01:45
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('licensing', '0001_initial'),
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='SoftwareEntitlement',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('product_code', models.CharField(choices=[('cmshopee', '虾皮圈优化助手')], max_length=32, verbose_name='产品代码')),
|
||||
('plan_name', models.CharField(max_length=120, verbose_name='套餐名称快照')),
|
||||
('plan_duration_days', models.PositiveIntegerField(verbose_name='套餐有效天数快照')),
|
||||
('plan_price', models.DecimalField(decimal_places=2, max_digits=12, verbose_name='套餐价格快照')),
|
||||
('plan_device_limit', models.PositiveSmallIntegerField(verbose_name='设备数量快照')),
|
||||
('plan_grace_days', models.PositiveSmallIntegerField(default=0, verbose_name='宽限天数快照')),
|
||||
('status', models.CharField(choices=[('active', '有效'), ('revoked', '已撤销'), ('expired', '已过期')], default='active', max_length=20, verbose_name='状态')),
|
||||
('starts_at', models.DateTimeField(verbose_name='开始时间')),
|
||||
('expires_at', models.DateTimeField(verbose_name='到期时间')),
|
||||
('grace_expires_at', models.DateTimeField(verbose_name='宽限截止时间')),
|
||||
('revoked_at', models.DateTimeField(blank=True, null=True, verbose_name='撤销时间')),
|
||||
('created_at', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
|
||||
('updated_at', models.DateTimeField(auto_now=True, verbose_name='更新时间')),
|
||||
('user', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='software_entitlements', to=settings.AUTH_USER_MODEL, verbose_name='用户')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': '软件权益',
|
||||
'verbose_name_plural': '软件权益',
|
||||
'db_table': 'software_entitlement',
|
||||
'ordering': ('-expires_at', '-id'),
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='LicenseSeat',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('seat_number', models.PositiveSmallIntegerField(verbose_name='席位序号')),
|
||||
('bound_at', models.DateTimeField(blank=True, null=True, verbose_name='绑定时间')),
|
||||
('released_at', models.DateTimeField(blank=True, null=True, verbose_name='解绑时间')),
|
||||
('created_at', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
|
||||
('updated_at', models.DateTimeField(auto_now=True, verbose_name='更新时间')),
|
||||
('device', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='license_seats', to='licensing.clientdevice', verbose_name='绑定设备')),
|
||||
('entitlement', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='seats', to='licensing.softwareentitlement', verbose_name='软件权益')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': '授权席位',
|
||||
'verbose_name_plural': '授权席位',
|
||||
'db_table': 'license_seat',
|
||||
'ordering': ('entitlement_id', 'seat_number'),
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='LicenseEvent',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('action', models.CharField(choices=[('granted', '人工授予'), ('renewed', '续期'), ('revoked', '撤销'), ('seat_assigned', '绑定席位'), ('seat_released', '解绑席位')], max_length=32, verbose_name='动作')),
|
||||
('reason', models.CharField(max_length=255, verbose_name='原因')),
|
||||
('metadata', models.JSONField(blank=True, default=dict, verbose_name='附加信息')),
|
||||
('created_at', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
|
||||
('actor', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='license_events_performed', to=settings.AUTH_USER_MODEL, verbose_name='操作人')),
|
||||
('device', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='license_events', to='licensing.clientdevice', verbose_name='关联设备')),
|
||||
('seat', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='events', to='licensing.licenseseat', verbose_name='授权席位')),
|
||||
('entitlement', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='events', to='licensing.softwareentitlement', verbose_name='软件权益')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': '授权事件',
|
||||
'verbose_name_plural': '授权事件',
|
||||
'db_table': 'license_event',
|
||||
'ordering': ('-created_at', '-id'),
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='SoftwarePlan',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('product_code', models.CharField(choices=[('cmshopee', '虾皮圈优化助手')], max_length=32, verbose_name='产品代码')),
|
||||
('name', models.CharField(max_length=120, verbose_name='套餐名称')),
|
||||
('duration_days', models.PositiveIntegerField(verbose_name='有效天数')),
|
||||
('price', models.DecimalField(decimal_places=2, max_digits=12, verbose_name='套餐价格')),
|
||||
('device_limit', models.PositiveSmallIntegerField(verbose_name='设备数量')),
|
||||
('grace_days', models.PositiveSmallIntegerField(default=0, verbose_name='宽限天数')),
|
||||
('status', models.CharField(choices=[('active', '启用'), ('inactive', '停用')], default='active', max_length=20, verbose_name='状态')),
|
||||
('created_at', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
|
||||
('updated_at', models.DateTimeField(auto_now=True, verbose_name='更新时间')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': '软件套餐',
|
||||
'verbose_name_plural': '软件套餐',
|
||||
'db_table': 'software_plan',
|
||||
'ordering': ('product_code', 'name', 'id'),
|
||||
'indexes': [models.Index(fields=['product_code', 'status'], name='software_pl_product_3580ff_idx')],
|
||||
'constraints': [models.CheckConstraint(condition=models.Q(('duration_days__gt', 0)), name='software_plan_duration_days_positive'), models.CheckConstraint(condition=models.Q(('price__gt', 0)), name='software_plan_price_positive'), models.CheckConstraint(condition=models.Q(('device_limit__gt', 0)), name='software_plan_device_limit_positive')],
|
||||
},
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='softwareentitlement',
|
||||
name='source_plan',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='entitlements', to='licensing.softwareplan', verbose_name='来源套餐'),
|
||||
),
|
||||
migrations.AddIndex(
|
||||
model_name='licenseseat',
|
||||
index=models.Index(fields=['device', 'updated_at'], name='license_sea_device__85c03f_idx'),
|
||||
),
|
||||
migrations.AddConstraint(
|
||||
model_name='licenseseat',
|
||||
constraint=models.UniqueConstraint(fields=('entitlement', 'seat_number'), name='license_seat_entitlement_number_unique'),
|
||||
),
|
||||
migrations.AddConstraint(
|
||||
model_name='licenseseat',
|
||||
constraint=models.CheckConstraint(condition=models.Q(('seat_number__gt', 0)), name='license_seat_number_positive'),
|
||||
),
|
||||
migrations.AddIndex(
|
||||
model_name='licenseevent',
|
||||
index=models.Index(fields=['entitlement', 'created_at'], name='license_eve_entitle_a66e88_idx'),
|
||||
),
|
||||
migrations.AddIndex(
|
||||
model_name='licenseevent',
|
||||
index=models.Index(fields=['device', 'created_at'], name='license_eve_device__e70841_idx'),
|
||||
),
|
||||
migrations.AddIndex(
|
||||
model_name='softwareentitlement',
|
||||
index=models.Index(fields=['user', 'product_code', 'status'], name='software_en_user_id_cd6e26_idx'),
|
||||
),
|
||||
migrations.AddIndex(
|
||||
model_name='softwareentitlement',
|
||||
index=models.Index(fields=['product_code', 'expires_at'], name='software_en_product_c6ffbf_idx'),
|
||||
),
|
||||
migrations.AddConstraint(
|
||||
model_name='softwareentitlement',
|
||||
constraint=models.CheckConstraint(condition=models.Q(('plan_duration_days__gt', 0)), name='software_entitlement_duration_positive'),
|
||||
),
|
||||
migrations.AddConstraint(
|
||||
model_name='softwareentitlement',
|
||||
constraint=models.CheckConstraint(condition=models.Q(('plan_price__gt', 0)), name='software_entitlement_price_positive'),
|
||||
),
|
||||
migrations.AddConstraint(
|
||||
model_name='softwareentitlement',
|
||||
constraint=models.CheckConstraint(condition=models.Q(('plan_device_limit__gt', 0)), name='software_entitlement_device_limit_positive'),
|
||||
),
|
||||
]
|
||||
Reference in New Issue
Block a user