3
3
import uuid_utils .compat as uuid
4
4
from django .contrib .postgres .search import SearchVectorField
5
5
from django .db import models
6
+ from django .db .models import QuerySet
6
7
from django .db .models .signals import pre_delete
7
8
from django .dispatch import receiver
8
9
from mptt .fields import TreeForeignKey
9
10
from mptt .models import MPTTModel
10
11
11
12
from common .db .sql_execute import select_one
12
13
from common .mixins .app_model_mixin import AppModelMixin
14
+ from common .utils .common import get_sha256_hash
13
15
from models_provider .models import Model
14
16
from users .models import User
15
17
@@ -221,6 +223,19 @@ class SearchMode(models.TextChoices):
221
223
blend = 'blend'
222
224
223
225
226
+ class FileSourceType (models .TextChoices ):
227
+ # 知识库 跟随知识库被删除而被删除 source_id 为知识库id
228
+ KNOWLEDGE = "KNOWLEDGE"
229
+ # 应用 跟随应用被删除而被删除 source_id 为应用id
230
+ APPLICATION = "APPLICATION"
231
+ # 临时30分钟 数据30分钟后被清理 source_id 为TEMPORARY_30_MINUTE
232
+ TEMPORARY_30_MINUTE = "TEMPORARY_30_MINUTE"
233
+ # 临时120分钟 数据120分钟后被清理 source_id为TEMPORARY_100_MINUTE
234
+ TEMPORARY_120_MINUTE = "TEMPORARY_100_MINUTE"
235
+ # 临时1天 数据1天后被清理 source_id为TEMPORARY_1_DAY
236
+ TEMPORARY_1_DAY = "TEMPORARY_1_DAY"
237
+
238
+
224
239
class VectorField (models .Field ):
225
240
def db_type (self , connection ):
226
241
return 'vector'
@@ -246,16 +261,25 @@ class Meta:
246
261
class File (AppModelMixin ):
247
262
id = models .UUIDField (primary_key = True , max_length = 128 , default = uuid .uuid7 , editable = False , verbose_name = "主键id" )
248
263
file_name = models .CharField (max_length = 256 , verbose_name = "文件名称" , default = "" )
249
- workspace_id = models .CharField (max_length = 64 , verbose_name = "工作空间id" , default = "default" , db_index = True )
264
+ file_size = models .IntegerField (verbose_name = "文件大小" , default = 0 )
265
+ sha256_hash = models .CharField (verbose_name = "文件sha256_hash标识" , default = "" )
266
+ source_type = models .CharField (verbose_name = "资源类型" , choices = FileSourceType ,
267
+ default = FileSourceType .TEMPORARY_120_MINUTE .value )
268
+ source_id = models .CharField (verbose_name = "资源id" , default = FileSourceType .TEMPORARY_120_MINUTE .value )
250
269
loid = models .IntegerField (verbose_name = "loid" )
251
270
meta = models .JSONField (verbose_name = "文件关联数据" , default = dict )
252
271
253
272
class Meta :
254
273
db_table = "file"
255
274
256
275
def save (self , bytea = None , force_insert = False , force_update = False , using = None , update_fields = None ):
257
- result = select_one ("SELECT lo_from_bytea(%s, %s::bytea) as loid" , [0 , bytea ])
258
- self .loid = result ['loid' ]
276
+ sha256_hash = get_sha256_hash (bytea )
277
+ f = QuerySet (File ).filter (sha256_hash = sha256_hash ).first ()
278
+ if f is not None :
279
+ self .loid = f .loid
280
+ else :
281
+ result = select_one ("SELECT lo_from_bytea(%s, %s::bytea) as loid" , [0 , bytea ])
282
+ self .loid = result ['loid' ]
259
283
super ().save ()
260
284
261
285
def get_bytes (self ):
@@ -265,4 +289,6 @@ def get_bytes(self):
265
289
266
290
@receiver (pre_delete , sender = File )
267
291
def on_delete_file (sender , instance , ** kwargs ):
268
- select_one (f'SELECT lo_unlink({ instance .loid } )' , [])
292
+ exist = QuerySet (File ).filter (loid = instance .loid ).exclude (id = instance .id ).exists ()
293
+ if not exist :
294
+ select_one (f'SELECT lo_unlink({ instance .loid } )' , [])
0 commit comments