1
0
forked from erp-dev/erp
Files
erpnew/api_v1/models.py
2026-01-12 11:52:41 +08:00

240 lines
7.4 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
API v1 通用模型
"""
import os
import uuid
from django.db import models
from django.contrib.auth import get_user_model
from django.db.models.fields.json import KeyTextTransform
from flower.common import ModelBase
User = get_user_model()
def upload_file_path(instance, filename):
"""
生成随机化的文件上传路径
格式: uploads/YYYY/MM/DD/uuid.ext
"""
# 获取文件扩展名
ext = os.path.splitext(filename)[1].lower()
# 生成随机文件名
random_filename = f"{uuid.uuid4().hex}{ext}"
# 返回完整路径
from datetime import datetime
now = datetime.now()
return f"uploads/{now.year}/{now.month:02d}/{now.day:02d}/{random_filename}"
class UploadedFile(ModelBase):
"""
通用文件上传记录
用于存储无法归类到具体业务的文件上传记录
"""
path = models.FileField(
upload_to=upload_file_path,
max_length=500,
verbose_name='文件路径',
help_text='文件存储路径,包含前缀、随机文件名和后缀'
)
owner = models.ForeignKey(
User,
on_delete=models.CASCADE,
related_name='uploaded_files',
verbose_name='上传者'
)
is_deleted = models.BooleanField(
default=False,
verbose_name='是否已删除',
help_text='软删除标记'
)
original_filename = models.CharField(
max_length=255,
blank=True,
verbose_name='原始文件名',
help_text='用户上传时的原始文件名'
)
file_size = models.BigIntegerField(
null=True,
blank=True,
verbose_name='文件大小(字节)'
)
content_type = models.CharField(
max_length=100,
blank=True,
verbose_name='文件类型',
help_text='MIME类型如 image/jpeg'
)
class Meta:
db_table = 'api_uploaded_file'
verbose_name = '上传文件'
verbose_name_plural = '上传文件'
ordering = ['-created_at']
def __str__(self):
return f"{self.original_filename or self.path.name} (by {self.owner.username})"
@property
def file_url(self):
"""获取文件访问URL"""
if self.path:
return self.path.url
return None
class DataSync(ModelBase):
"""记录外部数据同步的执行情况"""
class TableName(models.TextChoices):
PRODUCT = 'product', '产品'
CUSTOMER = 'customer', '客户'
PLATE_ORDER = 'plate_order', '开版(明道云)'
table_name = models.CharField(max_length=50, choices=TableName.choices, verbose_name='同步目标')
page_index = models.PositiveIntegerField(default=1, verbose_name='页码')
page_size = models.PositiveIntegerField(default=100, verbose_name='每页数量')
total_count = models.PositiveIntegerField(default=0, verbose_name='远端总数')
synced_rows = models.PositiveIntegerField(default=0, verbose_name='本次同步数量')
last_ctime = models.DateTimeField(null=True, blank=True, verbose_name='最新数据时间')
last_rowid = models.CharField(max_length=64, blank=True, verbose_name='最新 RowID')
note = models.CharField(max_length=255, blank=True, verbose_name='备注')
class Meta:
db_table = 'api_data_sync'
verbose_name = '数据同步记录'
verbose_name_plural = '数据同步记录'
ordering = ['-created_at']
def __str__(self):
return f'{self.table_name} @ {self.created_at:%Y-%m-%d %H:%M:%S}'
class MDYPlateOrderStaging(ModelBase):
"""明道云"开版数据表"同步暂存表(不影响 printing.PlateOrder
设计目标:
- 与现有业务模型完全隔离,避免影响业务数据与流程
- 仅保留 rowid 便于检索排查,其余信息全部落在 raw(JSON) 中
- 随着同步深入再逐步"属性化"字段(从 raw 实时提取/映射)
"""
mdy_rowid = models.CharField(
max_length=64,
unique=True,
db_index=True,
verbose_name='明道云 RowID',
help_text='明道云行记录的 rowid全局唯一',
)
ctime = models.DateTimeField(
null=True,
blank=True,
db_index=True,
verbose_name='明道云创建时间',
help_text='明道云系统字段 ctime用于排序/增量定位)',
)
utime = models.DateTimeField(
null=True,
blank=True,
db_index=True,
verbose_name='明道云更新时间',
help_text='明道云系统字段 utime用于排序/增量定位)',
)
raw = models.JSONField(
default=dict,
blank=True,
verbose_name='原始行数据',
help_text='getFilterRows 返回的整行原始数据(包含各 controlId 字段)',
)
related = models.JSONField(
default=list,
blank=True,
verbose_name='关联表数据',
help_text='开版相关跨表数据(碾平 list[dict]),用于排查与后续属性化提取',
)
class Meta:
db_table = 'api_mdy_plate_order_staging'
verbose_name = '明道云开版暂存'
verbose_name_plural = '明道云开版暂存'
ordering = ['-created_at']
indexes = [
# 明道云字段62d52f4b8d2972284492dd0e开版表"订单id/设计编号"
# 用于在暂存表中按该字段快速检索
models.Index(
KeyTextTransform('62d52f4b8d2972284492dd0e', 'raw'),
name='api_mdy_po_raw_id_idx',
),
]
def __str__(self):
return f'{self.mdy_rowid}'
class ApiAuditLog(models.Model):
"""API审计日志 - 记录创建操作的历史现场
用于保存特定模块的POST请求记录"创建"操作的完整请求信息。
通过Celery异步写入避免影响API响应性能。
"""
url = models.CharField(
max_length=500,
verbose_name='请求URL',
help_text='完整的请求路径'
)
method = models.CharField(
max_length=10,
verbose_name='请求方法',
help_text='HTTP方法如POST'
)
request_data = models.JSONField(
default=dict,
verbose_name='请求体数据',
help_text='POST请求的body数据JSON格式'
)
query_params = models.JSONField(
default=dict,
verbose_name='查询参数',
help_text='URL查询参数'
)
user_id = models.IntegerField(
null=True,
blank=True,
db_index=True,
verbose_name='用户ID',
help_text='操作用户的ID'
)
username = models.CharField(
max_length=150,
blank=True,
default='',
verbose_name='用户名',
help_text='操作用户的用户名快照'
)
response_status = models.IntegerField(
null=True,
blank=True,
verbose_name='响应状态码',
help_text='HTTP响应状态码'
)
created_at = models.DateTimeField(
auto_now_add=True,
db_index=True,
verbose_name='创建时间'
)
class Meta:
db_table = 'api_audit_log'
verbose_name = 'API审计日志'
verbose_name_plural = 'API审计日志'
ordering = ['-created_at']
indexes = [
models.Index(fields=['user_id', 'created_at']),
models.Index(fields=['url', 'created_at']),
]
def __str__(self):
return f'{self.method} {self.url} ({self.username or "anonymous"}) - {self.response_status}'