| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170 |
- from datetime import date
- class ExportReceivableCostListTool:
- name = 'export_receivable_cost_list'
- route_path = '/mcp/tools/exportReceivableCostList'
- NUMBER_FIELDS = (
- 'reference_numbers',
- 'tracking_numbers',
- 'order_numbers',
- 'bill_numbers',
- )
- def __init__(self, api_client=None):
- self.api_client = api_client
- def metadata(self):
- number_array = {
- 'type': 'array',
- 'minItems': 1,
- 'maxItems': 200,
- 'items': {
- 'type': 'string',
- 'minLength': 1,
- 'maxLength': 100,
- 'pattern': '.*\\S.*',
- },
- }
- date_field = {
- 'type': 'string',
- 'format': 'date',
- 'pattern': '^\\d{4}-\\d{2}-\\d{2}$',
- }
- return {
- 'name': self.name,
- 'description': (
- '本工具只提交异步导出任务,不会在本次调用中等待文件生成,也禁止在一次调用内'
- '轮询任务状态。成功后返回任务引用(task_ref)和建议等待时间'
- '(retry_after_seconds)。稍后单独使用 query_export_task 查询任务状态或下载链接。'
- '使用场景:只有用户明确要求导出应收费用单并需要下载文件时才可调用。'
- '筛选值必须与 query_receivable_cost_list / list_receivable_cost_filter_options'
- '已确认的号码类型、日期区间和筛选项 value 完全一致;禁止根据格式猜测号码类型,'
- '禁止勾选 ID,禁止一次调用内轮询 query_export_task。'
- '参数名仅用于工具调用;向用户回答时只能使用中文业务名称,不得展示内部参数名。'
- ),
- 'input_schema': {
- 'type': 'object',
- 'properties': {
- 'reference_numbers': dict(number_array),
- 'tracking_numbers': dict(number_array),
- 'order_numbers': dict(number_array),
- 'bill_numbers': dict(number_array),
- 'business_date_start': dict(date_field),
- 'business_date_end': dict(date_field),
- 'customer_id': {'type': 'integer', 'minimum': 1},
- 'sub_customer_id': {'type': 'integer', 'minimum': 1},
- 'billing_status': {'type': 'integer', 'enum': [0, 1]},
- 'verification_status': {
- 'type': 'integer', 'enum': [-1, 0, 1],
- },
- 'document_type': {'type': 'integer', 'minimum': 0},
- 'cost_type_id': {'type': 'integer', 'minimum': 1},
- },
- 'required': [],
- 'allOf': [
- {
- 'anyOf': [
- {'required': ['reference_numbers']},
- {'required': ['tracking_numbers']},
- {'required': ['order_numbers']},
- {'required': ['bill_numbers']},
- {
- 'required': [
- 'business_date_start',
- 'business_date_end',
- ],
- },
- ],
- },
- {
- 'if': {'required': ['sub_customer_id']},
- 'then': {'required': ['customer_id']},
- },
- ],
- 'additionalProperties': False,
- },
- }
- def call(
- self,
- reference_numbers=None,
- tracking_numbers=None,
- order_numbers=None,
- bill_numbers=None,
- business_date_start=None,
- business_date_end=None,
- customer_id=None,
- sub_customer_id=None,
- billing_status=None,
- verification_status=None,
- document_type=None,
- cost_type_id=None,
- request_id='rq_export_receivable_cost_list',
- ):
- if self.api_client is None:
- raise RuntimeError(
- 'api client is required for export_receivable_cost_list'
- )
- supplied_numbers = {}
- total_numbers = 0
- for field, values in (
- ('reference_numbers', reference_numbers),
- ('tracking_numbers', tracking_numbers),
- ('order_numbers', order_numbers),
- ('bill_numbers', bill_numbers),
- ):
- if values is None:
- continue
- if not isinstance(values, list) or not values:
- raise ValueError(field + ' must be a non-empty list')
- cleaned = []
- for value in values:
- if not isinstance(value, str):
- raise ValueError(field + ' items must be strings')
- item = value.strip()
- if not item or len(item) > 100:
- raise ValueError(field + ' items must be 1 to 100 chars')
- if item not in cleaned:
- cleaned.append(item)
- supplied_numbers[field] = cleaned
- total_numbers += len(cleaned)
- if total_numbers > 200:
- raise ValueError('at most 200 numbers across all number fields')
- has_start = business_date_start is not None
- has_end = business_date_end is not None
- if has_start != has_end:
- raise ValueError('business date range requires both start and end')
- if has_start:
- start = date.fromisoformat(str(business_date_start))
- end = date.fromisoformat(str(business_date_end))
- if end < start or (end - start).days > 30:
- raise ValueError('business date range must be within 31 days')
- if not supplied_numbers and not has_start:
- raise ValueError(
- 'provide number filters or a business date range'
- )
- if sub_customer_id is not None and customer_id is None:
- raise ValueError('sub_customer_id requires customer_id')
- payload = dict(supplied_numbers)
- if has_start:
- payload['business_date_start'] = str(business_date_start)
- payload['business_date_end'] = str(business_date_end)
- for field, value in (
- ('customer_id', customer_id),
- ('sub_customer_id', sub_customer_id),
- ('billing_status', billing_status),
- ('verification_status', verification_status),
- ('document_type', document_type),
- ('cost_type_id', cost_type_id),
- ):
- if value is not None:
- payload[field] = value
- return self.api_client.call_tool(
- self.name, self.route_path, payload, request_id
- )
|