1
0
forked from erp-dev/erp

fix: query param of api_man

This commit is contained in:
2025-12-09 16:34:46 +08:00
parent d61a644d92
commit 4188bae0fc
12 changed files with 181 additions and 5 deletions

View File

@@ -67,6 +67,12 @@ class ProductSerializer(BaseSerializer):
- 如果有图片:返回完整 URL七牛云 CDN 地址)
- 如果没有图片:返回 None
"""
primary_url_resolver = getattr(obj, 'get_primary_image_url', None)
if callable(primary_url_resolver):
resolved = primary_url_resolver()
if resolved:
return resolved
if obj.image:
request = self.context.get('request')
if request:

View File

@@ -2,6 +2,8 @@ from rest_framework import viewsets
from rest_framework.exceptions import PermissionDenied
from rest_framework.pagination import LimitOffsetPagination
from rest_framework.permissions import IsAuthenticated, DjangoModelPermissions
from rest_framework.decorators import action
from rest_framework.response import Response
from django_filters.rest_framework import DjangoFilterBackend
from django_filters import rest_framework as dj_filters
@@ -68,11 +70,18 @@ class BasicInfoFilterMixin:
def filter_queryset(self, queryset):
field = self._detect_filter_field()
model = getattr(getattr(self, 'queryset', None), 'model', None)
if not field or not model:
return super().filter_queryset(queryset)
if field and model:
self.filterset_class = self._build_filterset_class(model, field)
self.filterset_class = self._build_filterset_class(model, field)
return super().filter_queryset(queryset)
qs = super().filter_queryset(queryset)
# 统一按创建时间倒序排序(如果模型存在 created_at 字段)
model = getattr(qs, 'model', model)
if model:
field_names = {f.name for f in model._meta.get_fields() if hasattr(f, 'name')}
if 'created_at' in field_names:
return qs.order_by('-created_at')
return qs
class BaseViewSet(BasicInfoFilterMixin, viewsets.ModelViewSet):
@@ -106,6 +115,11 @@ class QuickInputViewSet(BasicInfoFilterMixin, viewsets.ModelViewSet):
qs = qs.filter(group=group)
return qs
@action(detail=False, methods=['get'], url_path='groups')
def groups(self, request):
groups = serializers.basic_models.QuickInput.objects.values_list('group', flat=True).distinct()
return Response(groups)
class ProductViewSet(BaseViewSet):
queryset = serializers.basic_models.Product.objects

View File

@@ -139,6 +139,7 @@ def _map_unit(unit_label: str | None) -> int:
'': basic_models.ProductUnitEnum.METER,
'': basic_models.ProductUnitEnum.YARD,
'公斤': basic_models.ProductUnitEnum.KG,
'': basic_models.ProductUnitEnum.SEGMENT,
}
return mapping.get(unit_label, basic_models.ProductUnitEnum.METER)
@@ -152,6 +153,19 @@ def _ensure_decimal(value: str | None):
return None
def _ensure_int(value):
if value is None:
return None
try:
return int(value)
except (TypeError, ValueError, InvalidOperation):
# 有些值可能是 Decimal 或字符串数字
try:
return int(Decimal(str(value)))
except Exception:
return None
def _run_fetch(page: int, page_size: int):
return asyncio.run(fetch_products_from_mingdaoyun(page=page, page_size=page_size))
@@ -175,6 +189,12 @@ def _upsert_product(product_data, merchant, category):
width_decimal = _ensure_decimal(product_data.width)
if width_decimal is not None:
defaults['width_size'] = width_decimal
pieces_int = _ensure_int(product_data.pieces)
if pieces_int is not None:
defaults['pieces'] = pieces_int
segment_int = _ensure_int(product_data.segment_size)
if segment_int is not None:
defaults['segment_size'] = segment_int
product_obj, created = basic_models.Product.objects.get_or_create(
merchant=merchant,
@@ -387,4 +407,3 @@ def sync_mdy_customers(self, page_size: int = 300, max_pages: int | None = None,
}
logger.info('明道云客户同步完成: %s', payload)
return payload

View File

@@ -0,0 +1,18 @@
# Generated by Django 5.2.8 on 2025-12-09 06:34
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('basic_info', '0019_customer_mdy_fields'),
]
operations = [
migrations.AddField(
model_name='product',
name='mdy_image_url',
field=models.URLField(blank=True, help_text='来自明道云的图片链接', max_length=500, null=True, verbose_name='明道云图片URL'),
),
]

View File

@@ -0,0 +1,18 @@
# Generated by Django 5.2.8 on 2025-12-09 07:51
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('basic_info', '0020_product_mdy_image_url'),
]
operations = [
migrations.AlterField(
model_name='product',
name='unit',
field=models.IntegerField(choices=[(1, ''), (2, ''), (3, '公斤'), (4, '')], default=1, verbose_name='单位'),
),
]

View File

@@ -0,0 +1,23 @@
# Generated by Django 5.2.8 on 2025-12-09 07:55
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('basic_info', '0021_alter_product_unit'),
]
operations = [
migrations.AddField(
model_name='product',
name='pieces',
field=models.IntegerField(blank=True, null=True, verbose_name='件数'),
),
migrations.AddField(
model_name='product',
name='segment_size',
field=models.IntegerField(blank=True, null=True, verbose_name='一段尺寸'),
),
]

View File

@@ -1,4 +1,5 @@
from decimal import Decimal
import json
from django.db import models
from flower.common import ModelBase
@@ -36,6 +37,7 @@ class ProductUnitEnum(models.IntegerChoices):
METER = 1, ''
YARD = 2, ''
KG = 3, '公斤'
SEGMENT = 4, ''
class EmployeeTypeEnum(models.TextChoices):
@@ -226,6 +228,8 @@ class Product(ModelBase):
human_id = models.CharField(max_length=100, verbose_name='产品编号', blank=True)
color = models.CharField(max_length=50, blank=True, null=True, verbose_name='颜色')
width_size = models.DecimalField(max_digits=10, decimal_places=2, blank=True, null=True, verbose_name='宽幅')
pieces = models.IntegerField(blank=True, null=True, verbose_name='件数')
segment_size = models.IntegerField(blank=True, null=True, verbose_name='一段尺寸')
single_price_in = models.DecimalField(
max_digits=10,
decimal_places=2,
@@ -253,6 +257,13 @@ class Product(ModelBase):
null=True,
verbose_name='产品图片',
)
mdy_image_url = models.URLField(
max_length=500,
blank=True,
null=True,
verbose_name='明道云图片URL',
help_text='来自明道云的图片链接',
)
minimum_quantity = models.IntegerField(null=True, blank=True, verbose_name='最低库存量')
transform_rate = models.DecimalField(
max_digits=10,
@@ -267,6 +278,36 @@ class Product(ModelBase):
help_text='标记该产品是否通过明道云接口同步',
)
def get_primary_image_url(self):
"""
优先返回 mdy_image_url若为空则尝试从 description JSON 中提取 original_file_full_path。
"""
if self.mdy_image_url:
return self.mdy_image_url
if not self.description:
return None
try:
payload = json.loads(self.description) if isinstance(self.description, str) else self.description
except (TypeError, ValueError, json.JSONDecodeError):
return None
if isinstance(payload, dict):
items = [payload]
elif isinstance(payload, list):
items = payload
else:
return None
for item in items:
if not isinstance(item, dict):
continue
url = item.get('original_file_full_path') or item.get('DownloadUrl')
if url:
return url
return None
def __str__(self):
return self.name

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -11,6 +11,8 @@ class Product(BaseModel):
uid: str
rowid: str
name: str
pieces: int | None
segment_size: int | None
unit: str | None
color: str | None
width: str | None = None
@@ -173,9 +175,11 @@ class MingDaoYunClient:
product_type_map = {
'name': '668caa5eb80969563ecaee7d',
'pieces': '689c5aa37c175367b29fcb87',
'color': '66ed3b9ae01d5599bdb45f6d',
'uid': '668caa5eb80969563ecaee7c',
'unit': '668e2ee2ca758c8cc5d0dc1d',
'segment_size': '668e2194790c0e04058b4f3c',
'width': '668e2194790c0e04058b4f3c',
'detail_str': '668caa5eb80969563ecaee7e',
'created_at': 'ctime',

View File

@@ -0,0 +1,33 @@
# Generated by Django 5.2.8 on 2025-12-09 07:51
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('stock', '0008_alter_stockchangerecord_source_type'),
]
operations = [
migrations.AlterField(
model_name='stockchangedetail',
name='unit',
field=models.IntegerField(choices=[(1, ''), (2, ''), (3, '公斤'), (4, '')], verbose_name='单位'),
),
migrations.AlterField(
model_name='stockfreeze',
name='unit',
field=models.IntegerField(choices=[(1, ''), (2, ''), (3, '公斤'), (4, '')], verbose_name='单位'),
),
migrations.AlterField(
model_name='stocksnapshot',
name='unit',
field=models.IntegerField(choices=[(1, ''), (2, ''), (3, '公斤'), (4, '')], verbose_name='单位'),
),
migrations.AlterField(
model_name='transferorderitem',
name='unit',
field=models.IntegerField(choices=[(1, ''), (2, ''), (3, '公斤'), (4, '')], verbose_name='单位'),
),
]