协议样本、生产产出和盘点明细按行更新并保留原 id;补齐存储证明质检预填、离职人员显示名,以及样本采集通知模板字段。 Co-authored-by: Cursor <cursoragent@cursor.com>
576 lines
22 KiB
Python
576 lines
22 KiB
Python
"""移动市场接口的白名单请求 DTO。"""
|
|
from __future__ import annotations
|
|
|
|
import re
|
|
from datetime import datetime
|
|
from decimal import Decimal
|
|
from typing import List, Literal, Optional
|
|
|
|
from pydantic import (
|
|
AliasChoices,
|
|
BaseModel,
|
|
ConfigDict,
|
|
Field,
|
|
condecimal,
|
|
constr,
|
|
field_validator,
|
|
)
|
|
|
|
|
|
MarketPaymentAmount = condecimal(
|
|
gt=Decimal('0'),
|
|
max_digits=18,
|
|
decimal_places=2,
|
|
allow_inf_nan=False,
|
|
)
|
|
MarketContractAmount = condecimal(
|
|
ge=Decimal('0'),
|
|
max_digits=18,
|
|
decimal_places=2,
|
|
allow_inf_nan=False,
|
|
)
|
|
MarketSampleTypeId = constr(min_length=1, max_length=64)
|
|
|
|
_CUSTOMER_INT_LABELS = {
|
|
'birth_order': '胎次',
|
|
'newborn_weight_gram': '新生儿体重',
|
|
}
|
|
|
|
|
|
def _blank_to_none(value):
|
|
"""移动端数字框清空后会提交 "",可选数字应视为未填。"""
|
|
if isinstance(value, str):
|
|
text = value.strip()
|
|
return None if text == '' else text
|
|
return value
|
|
|
|
|
|
def _optional_customer_int(value, field_name: str):
|
|
if value is None or isinstance(value, bool):
|
|
return value
|
|
if isinstance(value, int):
|
|
return value
|
|
label = _CUSTOMER_INT_LABELS[field_name]
|
|
if isinstance(value, float):
|
|
if value.is_integer():
|
|
return int(value)
|
|
raise ValueError(f'{label}须为整数')
|
|
if isinstance(value, str):
|
|
text = value.strip().replace(',', '')
|
|
if text == '':
|
|
return None
|
|
if re.fullmatch(r'[+-]?\d+\.?', text):
|
|
return int(text.rstrip('.'))
|
|
if re.fullmatch(r'[+-]?\d+\.\d+', text):
|
|
number = Decimal(text)
|
|
if number == number.to_integral_value():
|
|
return int(number)
|
|
raise ValueError(f'{label}须为整数')
|
|
return value
|
|
|
|
|
|
class MarketAttachmentDownloadBody(BaseModel):
|
|
model_config = ConfigDict(extra='forbid', populate_by_name=True)
|
|
|
|
file_url: str = Field(..., min_length=1, max_length=1000, alias='fileUrl')
|
|
file_name: Optional[str] = Field(None, max_length=200, alias='fileName')
|
|
|
|
|
|
class MarketPresignTransferBody(BaseModel):
|
|
model_config = ConfigDict(extra='forbid', populate_by_name=True)
|
|
|
|
transfer_amount: Optional[MarketPaymentAmount] = Field(
|
|
None, alias="transferAmount"
|
|
)
|
|
transfer_time: Optional[str] = Field(None, alias="transferTime")
|
|
transfer_type: Optional[str] = Field(
|
|
None, max_length=16, alias="transferType"
|
|
)
|
|
transfer_attachments: Optional[str] = Field(
|
|
None, max_length=4000, alias="transferAttachments"
|
|
)
|
|
remark: Optional[str] = Field(None, max_length=512)
|
|
|
|
@field_validator('transfer_time')
|
|
@classmethod
|
|
def validate_transfer_time(cls, value: Optional[str]) -> Optional[str]:
|
|
if value in (None, ''):
|
|
return value
|
|
text = str(value).strip()
|
|
if re.fullmatch(
|
|
r'(?:\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}'
|
|
r'|\d{4}-\d{2}-\d{2}T\d{2}:\d{2}'
|
|
r'(?::\d{2}(?:\.\d{1,6})?)?)',
|
|
text,
|
|
) is None:
|
|
raise ValueError('转账时间格式无效')
|
|
try:
|
|
datetime.fromisoformat(text)
|
|
except ValueError as exc:
|
|
raise ValueError('转账时间格式无效') from exc
|
|
return text
|
|
|
|
|
|
class MarketPresignOrderBody(BaseModel):
|
|
"""预签单可写字段;租户与财务字段不接受客户端输入。"""
|
|
|
|
model_config = ConfigDict(extra='forbid', populate_by_name=True)
|
|
|
|
order_date: Optional[str] = Field(None, alias="orderDate")
|
|
protocol_no: Optional[str] = Field(
|
|
None, max_length=64, alias="protocolNo"
|
|
)
|
|
mother_name: Optional[str] = Field(
|
|
None, max_length=64, alias="motherName"
|
|
)
|
|
expected_delivery_date: Optional[str] = Field(
|
|
None, alias="expectedDeliveryDate"
|
|
)
|
|
region_id: Optional[str] = Field(
|
|
None, max_length=64, alias="regionId"
|
|
)
|
|
hospital_id: Optional[str] = Field(
|
|
None, max_length=64, alias="hospitalId"
|
|
)
|
|
sample_collect_time: Optional[str] = Field(None, alias="sampleCollectTime")
|
|
pay_mode: Optional[str] = Field(None, max_length=8, alias="payMode")
|
|
contract_amount: Optional[MarketContractAmount] = Field(
|
|
None, alias="contractAmount"
|
|
)
|
|
collector_user_id: Optional[str] = Field(
|
|
None, max_length=64, alias="collectorUserId"
|
|
)
|
|
region_manager_user_id: Optional[str] = Field(
|
|
None, max_length=64, alias="regionManagerUserId"
|
|
)
|
|
order_status: Optional[
|
|
Literal['draft', 'active', 'converted', 'void']
|
|
] = Field(None, alias='orderStatus')
|
|
remark: Optional[str] = Field(None, max_length=512)
|
|
sample_type_ids: Optional[List[MarketSampleTypeId]] = Field(
|
|
None, min_length=1, max_length=100, alias="sampleTypeIds"
|
|
)
|
|
transfers: Optional[List[MarketPresignTransferBody]] = None
|
|
|
|
@field_validator('order_date', 'expected_delivery_date')
|
|
@classmethod
|
|
def validate_ymd(cls, value: Optional[str]) -> Optional[str]:
|
|
if value in (None, ''):
|
|
return value
|
|
text = str(value).strip()
|
|
if len(text) != 8 or not text.isdigit():
|
|
raise ValueError('日期必须使用 YYYYMMDD 格式')
|
|
try:
|
|
datetime.strptime(text, '%Y%m%d')
|
|
except ValueError as exc:
|
|
raise ValueError('日期必须是有效的 YYYYMMDD') from exc
|
|
return text
|
|
|
|
@field_validator('mother_name')
|
|
@classmethod
|
|
def validate_mother_name(cls, value: Optional[str]) -> Optional[str]:
|
|
if value is None:
|
|
return None
|
|
text = str(value).strip()
|
|
if not text:
|
|
raise ValueError('母亲姓名不能为空')
|
|
return text
|
|
|
|
@field_validator('sample_collect_time')
|
|
@classmethod
|
|
def validate_sample_collect_time(
|
|
cls, value: Optional[str]
|
|
) -> Optional[str]:
|
|
if value in (None, ''):
|
|
return value
|
|
text = str(value).strip()
|
|
try:
|
|
datetime.fromisoformat(text.replace('Z', '+00:00'))
|
|
except ValueError as exc:
|
|
raise ValueError('采集时间格式无效') from exc
|
|
return text
|
|
|
|
|
|
class MarketPresignOrderCreateBody(MarketPresignOrderBody):
|
|
"""新增预签单必填字段;销售专员始终由服务端身份决定。"""
|
|
|
|
mother_name: str = Field(min_length=1, max_length=64, alias='motherName')
|
|
sample_type_ids: List[MarketSampleTypeId] = Field(
|
|
min_length=1, max_length=100, alias='sampleTypeIds'
|
|
)
|
|
|
|
|
|
|
|
class MarketProtocolCustomerBody(BaseModel):
|
|
model_config = ConfigDict(extra='forbid', populate_by_name=True)
|
|
|
|
customer_id: Optional[str] = Field(None, alias='customerId')
|
|
father_customer_id: Optional[str] = Field(None, alias='fatherCustomerId')
|
|
newborn_customer_id: Optional[str] = Field(None, alias='newbornCustomerId')
|
|
mother_name: Optional[str] = Field(None, alias='motherName')
|
|
mother_phone: Optional[str] = Field(None, alias='motherPhone')
|
|
mother_id_card: Optional[str] = Field(None, alias='motherIdCard')
|
|
mother_birth_ym: Optional[str] = Field(None, alias='motherBirthYm')
|
|
mother_ethnic: Optional[str] = Field(None, alias='motherEthnic')
|
|
mother_email: Optional[str] = Field(None, alias='motherEmail')
|
|
mother_address: Optional[str] = Field(None, alias='motherAddress')
|
|
father_name: Optional[str] = Field(None, alias='fatherName')
|
|
father_phone: Optional[str] = Field(None, alias='fatherPhone')
|
|
father_id_card: Optional[str] = Field(None, alias='fatherIdCard')
|
|
newborn_name: Optional[str] = Field(None, alias='newbornName')
|
|
newborn_id_card: Optional[str] = Field(None, alias='newbornIdCard')
|
|
expected_delivery_date: Optional[str] = Field(None, alias='expectedDeliveryDate')
|
|
newborn_delivery_time: Optional[str] = Field(None, alias='newbornDeliveryTime')
|
|
newborn_sex: Optional[str] = Field(None, alias='newbornSex')
|
|
delivery_mode: Optional[str] = Field(None, alias='deliveryMode')
|
|
birth_order: Optional[int] = Field(None, alias='birthOrder')
|
|
newborn_weight_gram: Optional[int] = Field(None, alias='newbornWeightGram')
|
|
apgar_1min: Optional[float] = Field(None, alias='apgar1min')
|
|
apgar_5min: Optional[float] = Field(None, alias='apgar5min')
|
|
collector_user_id: Optional[str] = Field(None, alias='collectorUserId')
|
|
remark: Optional[str] = None
|
|
|
|
@field_validator('birth_order', 'newborn_weight_gram', mode='before')
|
|
@classmethod
|
|
def normalize_optional_int(cls, value, info):
|
|
return _optional_customer_int(value, info.field_name)
|
|
|
|
@field_validator('apgar_1min', 'apgar_5min', mode='before')
|
|
@classmethod
|
|
def normalize_optional_float(cls, value):
|
|
return _blank_to_none(value)
|
|
|
|
|
|
class MarketProtocolCollectionBody(BaseModel):
|
|
model_config = ConfigDict(extra='forbid', populate_by_name=True)
|
|
|
|
region_id: Optional[str] = Field(None, alias='regionId')
|
|
hospital_id: Optional[str] = Field(None, alias='hospitalId')
|
|
medical_staff: Optional[str] = Field(None, alias='medicalStaff')
|
|
sales_user_id: Optional[str] = Field(None, alias='salesUserId')
|
|
region_manager_user_id: Optional[str] = Field(
|
|
None, alias='regionManagerUserId'
|
|
)
|
|
sample_collect_time: Optional[str] = Field(None, alias='sampleCollectTime')
|
|
maternal_blood_collect_time: Optional[str] = Field(
|
|
None, alias='maternalBloodCollectTime'
|
|
)
|
|
remark: Optional[str] = None
|
|
|
|
|
|
class MarketProtocolSampleFreightDraftBody(BaseModel):
|
|
model_config = ConfigDict(extra='forbid', populate_by_name=True)
|
|
|
|
client_key: Optional[str] = Field(None, alias='clientKey')
|
|
transport_mode: Optional[str] = Field(None, alias='transportMode')
|
|
freight_amount: Optional[MarketContractAmount] = Field(None, alias='freightAmount')
|
|
waybill_no: Optional[str] = Field(None, alias='waybillNo')
|
|
|
|
|
|
class MarketProtocolSampleBody(BaseModel):
|
|
model_config = ConfigDict(extra='forbid', populate_by_name=True)
|
|
|
|
id: Optional[str] = Field(None, max_length=64, alias='sampleId')
|
|
sample_type_id: Optional[str] = Field(None, alias='sampleTypeId')
|
|
sample_code_id: Optional[str] = Field(None, alias='sampleCodeId')
|
|
sample_code: Optional[str] = Field(None, alias='sampleCode')
|
|
collect_amount: Optional[float] = Field(None, alias='collectAmount')
|
|
unit_id: Optional[str] = Field(None, alias='unitId')
|
|
price: Optional[MarketContractAmount] = None
|
|
original_price: Optional[MarketContractAmount] = Field(None, alias='originalPrice')
|
|
storage_years: Optional[int] = Field(None, alias='storageYears')
|
|
sample_status: Optional[str] = Field(None, alias='sampleStatus')
|
|
is_special: Optional[int] = Field(None, alias='isSpecial')
|
|
special_remark: Optional[str] = Field(None, alias='specialRemark')
|
|
remark: Optional[str] = None
|
|
eta_time: Optional[str] = Field(None, alias='etaTime')
|
|
freight_id: Optional[str] = Field(None, alias='freightId')
|
|
freight_draft: Optional[MarketProtocolSampleFreightDraftBody] = Field(
|
|
None, alias='freightDraft'
|
|
)
|
|
|
|
|
|
class MarketProtocolAttachmentsBody(BaseModel):
|
|
model_config = ConfigDict(extra='forbid', populate_by_name=True)
|
|
|
|
protocol_attachments: Optional[str] = Field(
|
|
None, max_length=4000, alias='protocolAttachments'
|
|
)
|
|
consent_form_urls: Optional[str] = Field(
|
|
None, max_length=4000, alias='consentFormUrls'
|
|
)
|
|
health_survey_form_urls: Optional[str] = Field(
|
|
None, max_length=4000, alias='healthSurveyFormUrls'
|
|
)
|
|
attachment_urls: Optional[str] = Field(
|
|
None, max_length=4000, alias='attachmentUrls'
|
|
)
|
|
infectious_disease_report_urls: Optional[str] = Field(
|
|
None, max_length=4000, alias='infectiousDiseaseReportUrls'
|
|
)
|
|
|
|
|
|
class MarketProtocolNestedTransferBody(BaseModel):
|
|
model_config = ConfigDict(extra='forbid', populate_by_name=True)
|
|
|
|
id: Optional[str] = None
|
|
transfer_amount: Optional[MarketPaymentAmount] = Field(None, alias='transferAmount')
|
|
transfer_time: Optional[str] = Field(None, alias='transferTime')
|
|
transfer_type: Optional[str] = Field(None, max_length=16, alias='transferType')
|
|
transfer_attachments: Optional[str] = Field(None, max_length=4000, alias='transferAttachments')
|
|
remark: Optional[str] = Field(None, max_length=512)
|
|
|
|
|
|
class MarketProtocolPaymentScheduleBody(BaseModel):
|
|
model_config = ConfigDict(extra='forbid', populate_by_name=True)
|
|
|
|
id: Optional[str] = None
|
|
schedule_no: Optional[int] = Field(None, alias='scheduleNo')
|
|
due_amount: Optional[MarketContractAmount] = Field(None, alias='dueAmount')
|
|
due_date: Optional[str] = Field(None, alias='dueDate')
|
|
schedule_status: Optional[str] = Field(None, alias='scheduleStatus')
|
|
transfers: Optional[List[MarketProtocolNestedTransferBody]] = None
|
|
|
|
|
|
class MarketProtocolSamplePaymentBody(BaseModel):
|
|
model_config = ConfigDict(extra='forbid', populate_by_name=True)
|
|
|
|
id: Optional[str] = None
|
|
sample_code: str = Field(..., min_length=1, alias='sampleCode')
|
|
protocol_sample_id: Optional[str] = Field(None, alias='protocolSampleId')
|
|
pay_mode: str = Field(..., alias='payMode')
|
|
contract_amount: Optional[MarketContractAmount] = Field(None, alias='contractAmount')
|
|
lump_sum_deadline: Optional[str] = Field(None, alias='lumpSumDeadline')
|
|
installment_count: Optional[int] = Field(None, alias='installmentCount')
|
|
installment_interval_months: Optional[float] = Field(
|
|
None, alias='installmentIntervalMonths'
|
|
)
|
|
installment_start_date: Optional[str] = Field(
|
|
None, alias='installmentStartDate'
|
|
)
|
|
installment_advance_days: Optional[int] = Field(
|
|
None, alias='installmentAdvanceDays'
|
|
)
|
|
installment_ratios: Optional[List[MarketContractAmount]] = Field(
|
|
None, alias='installmentRatios'
|
|
)
|
|
remark: Optional[str] = None
|
|
schedules: Optional[List[MarketProtocolPaymentScheduleBody]] = None
|
|
|
|
|
|
class MarketProtocolPaymentBody(BaseModel):
|
|
"""历史 v1 整单缴费方案;不接收协议版本或财务状态字段。"""
|
|
|
|
model_config = ConfigDict(extra='forbid', populate_by_name=True)
|
|
|
|
pay_mode: Literal['dj', 'fq', 'djfq', 'fgd'] = Field(..., alias='payMode')
|
|
contract_amount: Optional[MarketContractAmount] = Field(
|
|
None, alias='contractAmount'
|
|
)
|
|
lump_sum_deadline: Optional[str] = Field(None, max_length=8, alias='lumpSumDeadline')
|
|
installment_count: Optional[int] = Field(None, ge=1, alias='installmentCount')
|
|
installment_interval_months: Optional[Decimal] = Field(
|
|
None, ge=Decimal('0'), alias='installmentIntervalMonths'
|
|
)
|
|
installment_start_date: Optional[str] = Field(
|
|
None, max_length=8, alias='installmentStartDate'
|
|
)
|
|
installment_advance_days: Optional[int] = Field(
|
|
None, ge=0, le=365, alias='installmentAdvanceDays'
|
|
)
|
|
installment_ratios: Optional[List[MarketContractAmount]] = Field(
|
|
None, alias='installmentRatios'
|
|
)
|
|
|
|
@field_validator('lump_sum_deadline', 'installment_start_date')
|
|
@classmethod
|
|
def validate_ymd(cls, value: Optional[str]) -> Optional[str]:
|
|
if value in (None, ''):
|
|
return value
|
|
text = str(value).strip()
|
|
if len(text) != 8 or not text.isdigit():
|
|
raise ValueError('日期必须使用 YYYYMMDD 格式')
|
|
try:
|
|
datetime.strptime(text, '%Y%m%d')
|
|
except ValueError as exc:
|
|
raise ValueError('日期必须是有效的 YYYYMMDD') from exc
|
|
return text
|
|
|
|
|
|
class MarketProtocolPaymentDetailBody(BaseModel):
|
|
"""历史 v1 缴费明细的市场可写字段白名单。"""
|
|
|
|
model_config = ConfigDict(extra='forbid', populate_by_name=True)
|
|
|
|
id: Optional[str] = Field(None, max_length=64)
|
|
installment_no: Optional[int] = Field(None, ge=1, alias='installmentNo')
|
|
installment_due_date: Optional[str] = Field(
|
|
None, max_length=8, alias='installmentDueDate'
|
|
)
|
|
due_amount: Optional[MarketContractAmount] = Field(None, alias='dueAmount')
|
|
transfer_type: Optional[str] = Field(None, max_length=16, alias='transferType')
|
|
transfer_amount: Optional[MarketPaymentAmount] = Field(
|
|
None, alias='transferAmount'
|
|
)
|
|
transfer_time: Optional[str] = Field(None, alias='transferTime')
|
|
transfer_attachments: Optional[str] = Field(
|
|
None,
|
|
max_length=4000,
|
|
validation_alias=AliasChoices('transferAttachments', 'attachments'),
|
|
serialization_alias='transferAttachments',
|
|
)
|
|
remark: Optional[str] = Field(None, max_length=512)
|
|
|
|
@field_validator('installment_due_date')
|
|
@classmethod
|
|
def validate_ymd(cls, value: Optional[str]) -> Optional[str]:
|
|
if value in (None, ''):
|
|
return value
|
|
text = str(value).strip()
|
|
if len(text) != 8 or not text.isdigit():
|
|
raise ValueError('日期必须使用 YYYYMMDD 格式')
|
|
try:
|
|
datetime.strptime(text, '%Y%m%d')
|
|
except ValueError as exc:
|
|
raise ValueError('日期必须是有效的 YYYYMMDD') from exc
|
|
return text
|
|
|
|
|
|
class MarketProtocolBody(BaseModel):
|
|
"""移动协议白名单。协议/捐赠状态可改;审核、财务、退款及转换时间字段一律拒绝。"""
|
|
|
|
model_config = ConfigDict(extra='forbid', populate_by_name=True)
|
|
|
|
source: Literal['cell_storage', 'research_donation']
|
|
protocol_status: Optional[
|
|
Literal['dsx', 'sxz', 'ygq', 'yzf', 'sdfq', 'zky']
|
|
] = Field(None, alias='protocolStatus')
|
|
donation_status: Optional[
|
|
Literal['dsx', 'sxz', 'ygq', 'yzf', 'sdfq', 'zckc']
|
|
] = Field(None, alias='donationStatus')
|
|
protocol_no: Optional[str] = Field(None, alias='protocolNo')
|
|
donation_no: Optional[str] = Field(None, alias='donationNo')
|
|
register_date: Optional[str] = Field(None, alias='registerDate')
|
|
donation_date: Optional[str] = Field(None, alias='donationDate')
|
|
collector_user_id: Optional[str] = Field(None, alias='collectorUserId')
|
|
protocol_total_amount: Optional[MarketContractAmount] = Field(
|
|
None, alias='protocolTotalAmount'
|
|
)
|
|
remark: Optional[str] = None
|
|
customer: Optional[MarketProtocolCustomerBody] = None
|
|
collection: Optional[MarketProtocolCollectionBody] = None
|
|
samples: Optional[List[MarketProtocolSampleBody]] = None
|
|
attachments: Optional[MarketProtocolAttachmentsBody] = None
|
|
sample_payments: Optional[List[MarketProtocolSamplePaymentBody]] = Field(
|
|
None, alias='samplePayments'
|
|
)
|
|
payment: Optional[MarketProtocolPaymentBody] = None
|
|
payment_details: Optional[List[MarketProtocolPaymentDetailBody]] = Field(
|
|
None, alias='paymentDetails'
|
|
)
|
|
|
|
@field_validator('register_date', 'donation_date')
|
|
@classmethod
|
|
def validate_protocol_ymd(cls, value: Optional[str]) -> Optional[str]:
|
|
"""接受 YYYYMMDD / YYYY-MM-DD,统一规范为 CHAR(8) YYYYMMDD。"""
|
|
if value in (None, ''):
|
|
return value
|
|
text = str(value).strip().replace('-', '').replace('/', '')
|
|
if len(text) != 8 or not text.isdigit():
|
|
raise ValueError('日期必须使用 YYYYMMDD 格式')
|
|
try:
|
|
datetime.strptime(text, '%Y%m%d')
|
|
except ValueError as exc:
|
|
raise ValueError('日期必须是有效的 YYYYMMDD') from exc
|
|
return text
|
|
|
|
|
|
class MarketProtocolAuditBody(BaseModel):
|
|
model_config = ConfigDict(extra='forbid', populate_by_name=True)
|
|
|
|
audit_status: Literal['shtg', 'shth'] = Field(..., alias='auditStatus')
|
|
audit_remark: Optional[str] = Field(None, max_length=512, alias='auditRemark')
|
|
|
|
|
|
class MarketPaymentTransferBody(BaseModel):
|
|
"""市场侧仅可登记/编辑业务转账草稿,不接收状态及财务字段。"""
|
|
|
|
model_config = ConfigDict(extra='forbid', populate_by_name=True)
|
|
|
|
transfer_amount: Optional[MarketPaymentAmount] = Field(
|
|
None,
|
|
alias='transferAmount',
|
|
)
|
|
transfer_time: Optional[str] = Field(None, alias='transferTime')
|
|
transfer_type: Optional[str] = Field(None, max_length=16, alias='transferType')
|
|
transfer_attachments: Optional[str] = Field(
|
|
None, max_length=4000, alias='transferAttachments'
|
|
)
|
|
remark: Optional[str] = Field(None, max_length=512)
|
|
|
|
|
|
class MarketPaymentTransferCreateBody(MarketPaymentTransferBody):
|
|
schedule_id: str = Field(..., min_length=1, max_length=64, alias='scheduleId')
|
|
|
|
|
|
class MarketSampleHandleBody(BaseModel):
|
|
"""市场样本仅允许登记轻量办理信息。"""
|
|
|
|
model_config = ConfigDict(extra='forbid', populate_by_name=True)
|
|
|
|
handle_time: Optional[str] = Field(None, alias='handleTime')
|
|
handler_user_id: Optional[str] = Field(
|
|
None, max_length=64, alias='handlerUserId'
|
|
)
|
|
attachments: Optional[str] = Field(None, max_length=4000)
|
|
remark: Optional[str] = Field(None, max_length=512)
|
|
|
|
|
|
class MarketSampleStorageCertBody(MarketSampleHandleBody):
|
|
pass
|
|
|
|
|
|
class MarketSampleInsuranceBody(MarketSampleHandleBody):
|
|
pass
|
|
|
|
|
|
class MarketFreightItemBody(BaseModel):
|
|
"""客户端只提交样本身份;协议与样本编码快照由服务端派生。"""
|
|
|
|
model_config = ConfigDict(extra='forbid', populate_by_name=True)
|
|
|
|
sample_source: Literal['cell_storage', 'research_donation'] = Field(
|
|
..., alias='sampleSource'
|
|
)
|
|
sample_id: str = Field(..., min_length=1, max_length=64, alias='sampleId')
|
|
|
|
|
|
class MarketFreightItemsBody(BaseModel):
|
|
model_config = ConfigDict(extra='forbid', populate_by_name=True)
|
|
|
|
items: List[MarketFreightItemBody] = Field(..., min_length=1)
|
|
|
|
|
|
class MarketFreightCreateBody(MarketFreightItemsBody):
|
|
model_config = ConfigDict(extra='forbid', populate_by_name=True)
|
|
|
|
transport_mode: str = Field(..., min_length=1, max_length=100, alias='transportMode')
|
|
freight_amount: Optional[Decimal] = Field(
|
|
None, ge=Decimal('0'), max_digits=18, decimal_places=2,
|
|
allow_inf_nan=False, alias='freightAmount'
|
|
)
|
|
waybill_no: Optional[str] = Field(None, alias='waybillNo')
|
|
|
|
|
|
class MarketFreightUpdateBody(BaseModel):
|
|
model_config = ConfigDict(extra='forbid', populate_by_name=True)
|
|
|
|
transport_mode: Optional[str] = Field(
|
|
None, min_length=1, max_length=100, alias='transportMode'
|
|
)
|
|
freight_amount: Optional[Decimal] = Field(
|
|
None, ge=Decimal('0'), max_digits=18, decimal_places=2,
|
|
allow_inf_nan=False, alias='freightAmount'
|
|
)
|
|
waybill_no: Optional[str] = Field(None, alias='waybillNo')
|