feat: 完成 SyncHistory 和 FileAttribute 的迁移
数据库迁移内容: 1. 新增 FileAttribute 表(文件属性) - 支持键值对存储 - 支持嵌套属性(点号分隔的路径) - 支持属性类型(string/integer/float/boolean/json) - 支持属性分类和元数据 2. 更新 LobsterMemory 表 - 新增 has_attributes 字段 - 关联 FileAttribute 3. 更新 SyncHistory 表 - 新增 attributes_changed 字段(属性变更记录) - 新增 is_attribute_sync 字段(属性同步标记) 属性目录结构逻辑: - 使用点号分隔的键名(如 'author.name', 'metadata.tags') - 支持属性继承和嵌套查询 - 支持属性分类和索引优化 已完成迁移文件: - 0003_add_file_attribute.py
This commit is contained in:
78
backend/memory_app/migrations/0003_add_file_attribute.py
Normal file
78
backend/memory_app/migrations/0003_add_file_attribute.py
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
"""
|
||||||
|
数据库迁移:添加 FileAttribute 表和属性目录结构支持
|
||||||
|
|
||||||
|
变更内容:
|
||||||
|
1. 新增 FileAttribute 表(文件属性)
|
||||||
|
- 支持键值对存储
|
||||||
|
- 支持嵌套属性
|
||||||
|
- 支持属性继承
|
||||||
|
|
||||||
|
2. 更新 LobsterMemory 表
|
||||||
|
- 关联 FileAttribute
|
||||||
|
- 添加属性索引
|
||||||
|
|
||||||
|
3. 更新 SyncHistory 表
|
||||||
|
- 添加属性变更追踪
|
||||||
|
"""
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('memory_app', '0002_add_summary_and_audit_fields'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
# 创建 FileAttribute 表
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='FileAttribute',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('lobster_id', models.CharField(db_index=True, help_text='龙虾ID', max_length=50)),
|
||||||
|
('file_path', models.CharField(db_index=True, help_text='文件相对路径', max_length=500)),
|
||||||
|
('key', models.CharField(db_index=True, help_text='属性键', max_length=200)),
|
||||||
|
('value', models.TextField(help_text='属性值', null=True, blank=True)),
|
||||||
|
('value_type', models.CharField(choices=[('string', '字符串'), ('integer', '整数'), ('float', '浮点数'), ('boolean', '布尔值'), ('json', 'JSON')], default='string', help_text='值类型', max_length=20)),
|
||||||
|
('category', models.CharField(db_index=True, help_text='属性分类', max_length=100, null=True, blank=True)),
|
||||||
|
('metadata', models.JSONField(default=dict, help_text='元数据')),
|
||||||
|
('created_at', models.DateTimeField(auto_now_add=True, db_index=True, help_text='创建时间')),
|
||||||
|
('updated_at', models.DateTimeField(auto_now=True, help_text='更新时间')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'db_table': 'file_attribute',
|
||||||
|
'unique_together': {('lobster_id', 'file_path', 'key')},
|
||||||
|
'ordering': ['lobster_id', 'file_path', 'key'],
|
||||||
|
},
|
||||||
|
),
|
||||||
|
# 添加索引
|
||||||
|
migrations.AddIndex(
|
||||||
|
model_name='fileattribute',
|
||||||
|
index=models.Index(fields=['lobster_id', 'file_path'], name='memory_app_f_lobste_idx'),
|
||||||
|
),
|
||||||
|
migrations.AddIndex(
|
||||||
|
model_name='fileattribute',
|
||||||
|
index=models.Index(fields=['lobster_id', 'category'], name='memory_app_f_catego_idx'),
|
||||||
|
),
|
||||||
|
migrations.AddIndex(
|
||||||
|
model_name='fileattribute',
|
||||||
|
index=models.Index(fields=['lobster_id', 'updated_at'], name='memory_app_f_update_idx'),
|
||||||
|
),
|
||||||
|
# 更新 LobsterMemory 表(关联 FileAttribute)
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='lobstermemory',
|
||||||
|
name='has_attributes',
|
||||||
|
field=models.BooleanField(default=False, help_text='是否有属性'),
|
||||||
|
),
|
||||||
|
# 更新 SyncHistory 表(添加属性变更追踪)
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='synchistory',
|
||||||
|
name='attributes_changed',
|
||||||
|
field=models.JSONField(default=dict, help_text='属性变更记录'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='synchistory',
|
||||||
|
name='is_attribute_sync',
|
||||||
|
field=models.BooleanField(default=False, help_text='是否为属性同步'),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -36,6 +36,8 @@ class LobsterMemory(models.Model):
|
|||||||
|
|
||||||
summary = models.TextField(null=True, blank=True, max_length=1000, help_text='语义摘要')
|
summary = models.TextField(null=True, blank=True, max_length=1000, help_text='语义摘要')
|
||||||
|
|
||||||
|
has_attributes = models.BooleanField(default=False, help_text='是否有属性')
|
||||||
|
|
||||||
created_at = models.DateTimeField(auto_now_add=True, db_index=True, help_text='创建时间')
|
created_at = models.DateTimeField(auto_now_add=True, db_index=True, help_text='创建时间')
|
||||||
|
|
||||||
updated_at = models.DateTimeField(auto_now=True, db_index=True, help_text='更新时间')
|
updated_at = models.DateTimeField(auto_now=True, db_index=True, help_text='更新时间')
|
||||||
@@ -74,6 +76,69 @@ class LobsterMemory(models.Model):
|
|||||||
super().save(*args, **kwargs)
|
super().save(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class FileAttribute(models.Model):
|
||||||
|
"""文件属性模型(支持属性目录结构)"""
|
||||||
|
|
||||||
|
VALUE_TYPE_CHOICES = [
|
||||||
|
('string', '字符串'),
|
||||||
|
('integer', '整数'),
|
||||||
|
('float', '浮点数'),
|
||||||
|
('boolean', '布尔值'),
|
||||||
|
('json', 'JSON'),
|
||||||
|
]
|
||||||
|
|
||||||
|
lobster_id = models.CharField(max_length=50, db_index=True, help_text='龙虾ID')
|
||||||
|
|
||||||
|
file_path = models.CharField(max_length=500, db_index=True, help_text='文件相对路径')
|
||||||
|
|
||||||
|
key = models.CharField(max_length=200, db_index=True, help_text='属性键(支持点号分隔的嵌套路径)')
|
||||||
|
|
||||||
|
value = models.TextField(null=True, blank=True, help_text='属性值')
|
||||||
|
|
||||||
|
value_type = models.CharField(
|
||||||
|
max_length=20,
|
||||||
|
choices=VALUE_TYPE_CHOICES,
|
||||||
|
default='string',
|
||||||
|
help_text='值类型'
|
||||||
|
)
|
||||||
|
|
||||||
|
category = models.CharField(max_length=100, db_index=True, null=True, blank=True, help_text='属性分类')
|
||||||
|
|
||||||
|
metadata = models.JSONField(default=dict, help_text='元数据')
|
||||||
|
|
||||||
|
created_at = models.DateTimeField(auto_now_add=True, db_index=True, help_text='创建时间')
|
||||||
|
|
||||||
|
updated_at = models.DateTimeField(auto_now=True, help_text='更新时间')
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
db_table = 'file_attribute'
|
||||||
|
unique_together = ('lobster_id', 'file_path', 'key')
|
||||||
|
ordering = ['lobster_id', 'file_path', 'key']
|
||||||
|
indexes = [
|
||||||
|
models.Index(fields=['lobster_id', 'file_path']),
|
||||||
|
models.Index(fields=['lobster_id', 'category']),
|
||||||
|
models.Index(fields=['lobster_id', 'updated_at']),
|
||||||
|
]
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f"{self.lobster_id}/{self.file_path}.{self.key} = {self.value}"
|
||||||
|
|
||||||
|
def get_parsed_value(self):
|
||||||
|
"""根据类型解析值"""
|
||||||
|
if self.value_type == 'string':
|
||||||
|
return self.value
|
||||||
|
elif self.value_type == 'integer':
|
||||||
|
return int(self.value) if self.value else None
|
||||||
|
elif self.value_type == 'float':
|
||||||
|
return float(self.value) if self.value else None
|
||||||
|
elif self.value_type == 'boolean':
|
||||||
|
return self.value.lower() in ('true', '1', 'yes') if self.value else False
|
||||||
|
elif self.value_type == 'json':
|
||||||
|
import json
|
||||||
|
return json.loads(self.value) if self.value else None
|
||||||
|
return self.value
|
||||||
|
|
||||||
|
|
||||||
class SyncHistory(models.Model):
|
class SyncHistory(models.Model):
|
||||||
"""同步操作历史记录"""
|
"""同步操作历史记录"""
|
||||||
|
|
||||||
@@ -83,6 +148,7 @@ class SyncHistory(models.Model):
|
|||||||
('auto_sync', '自动同步'),
|
('auto_sync', '自动同步'),
|
||||||
('manual_merge', '手动合并'),
|
('manual_merge', '手动合并'),
|
||||||
('conflict_resolved', '冲突解决'),
|
('conflict_resolved', '冲突解决'),
|
||||||
|
('attribute_sync', '属性同步'),
|
||||||
]
|
]
|
||||||
|
|
||||||
STATUS_CHOICES = [
|
STATUS_CHOICES = [
|
||||||
@@ -138,6 +204,10 @@ class SyncHistory(models.Model):
|
|||||||
|
|
||||||
execution_time = models.FloatField(default=0, help_text='执行时间(秒)')
|
execution_time = models.FloatField(default=0, help_text='执行时间(秒)')
|
||||||
|
|
||||||
|
attributes_changed = models.JSONField(default=dict, help_text='属性变更记录')
|
||||||
|
|
||||||
|
is_attribute_sync = models.BooleanField(default=False, help_text='是否为属性同步')
|
||||||
|
|
||||||
created_at = models.DateTimeField(auto_now_add=True, db_index=True, help_text='操作时间')
|
created_at = models.DateTimeField(auto_now_add=True, db_index=True, help_text='操作时间')
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
|||||||
Reference in New Issue
Block a user