query_order_abnormal_list.py 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  1. from typing import Literal
  2. class QueryOrderAbnormalListTool:
  3. name = 'query_order_abnormal_list'
  4. route_path = '/mcp/tools/queryOrderAbnormalList'
  5. STATUSES = (
  6. '待处理', '放行待审核', '包裹放行', '退件待审核', '退件驳回', '已退件',
  7. )
  8. def __init__(self, api_client=None):
  9. self.api_client = api_client
  10. def metadata(self):
  11. return {
  12. 'name': self.name,
  13. 'description': (
  14. '使用场景:用户要看头程问题件列表时,先调用'
  15. 'list_order_abnormal_filter_options取得六个中文问题件状态之一即可查询当前页签;'
  16. '订单号可选。对照后台admin/Order/abnormal,一行一条问题件记录。'
  17. '号码类型不明确时必须先询问用户,'
  18. '不得根据号码格式猜测,不得跨字段或跨工具试查,也不得改走query_order或作废审核。'
  19. '禁止使用:关闭异常、已读、重新取号、编辑、导出,以及混查参考号、跟踪号、柜号或SO。'
  20. '参数只用于工具内部调用;最终回答只能展示中文业务名称;'
  21. '描述筛选条件时不得展示筛选字段的英文参数名。'
  22. ),
  23. 'input_schema': {
  24. 'type': 'object',
  25. 'properties': {
  26. 'abnormal_status': {
  27. 'type': 'string',
  28. 'enum': list[Literal[
  29. '待处理', '放行待审核', '包裹放行',
  30. '退件待审核', '退件驳回', '已退件',
  31. ]](self.STATUSES),
  32. 'description': (
  33. '问题件状态。必须先调用list_order_abnormal_filter_options并选择'
  34. '“问题件状态”取得六个中文页签之一;用户未明确时必须先询问,'
  35. '不得默认待处理或六类一起查。'
  36. ),
  37. },
  38. 'order_numbers': {
  39. 'type': 'array',
  40. 'minItems': 1,
  41. 'maxItems': 200,
  42. 'items': {
  43. 'type': 'string',
  44. 'minLength': 1,
  45. 'maxLength': 100,
  46. 'pattern': '.*\\S.*',
  47. },
  48. 'description': (
  49. '订单号数组。仅当用户明确说订单号时使用;不得放入参考号、跟踪号、柜号或SO。'
  50. ),
  51. },
  52. 'page': {
  53. 'type': 'integer',
  54. 'minimum': 1,
  55. 'maximum': 100,
  56. 'default': 1,
  57. },
  58. 'limit': {
  59. 'type': 'integer',
  60. 'minimum': 1,
  61. 'maximum': 100,
  62. 'default': 20,
  63. },
  64. },
  65. 'required': ['abnormal_status'],
  66. 'additionalProperties': False,
  67. },
  68. }
  69. def call(
  70. self,
  71. abnormal_status,
  72. order_numbers=None,
  73. page=1,
  74. limit=20,
  75. request_id='rq_query_order_abnormal_list',
  76. ):
  77. if self.api_client is None:
  78. raise RuntimeError(
  79. 'api client is required for query_order_abnormal_list'
  80. )
  81. status = str(abnormal_status or '').strip()
  82. if status not in self.STATUSES:
  83. raise ValueError('abnormal_status is invalid')
  84. payload = {'abnormal_status': status}
  85. if order_numbers is not None:
  86. payload['order_numbers'] = self._number_list(
  87. order_numbers, 'order_numbers'
  88. )
  89. payload['page'] = self._bounded_integer(page, 'page')
  90. payload['limit'] = self._bounded_integer(limit, 'limit')
  91. return self.api_client.call_tool(
  92. self.name, self.route_path, payload, request_id,
  93. )
  94. @staticmethod
  95. def _number_list(values, field):
  96. if not isinstance(values, list) or not values:
  97. raise ValueError(field + ' is invalid')
  98. result = []
  99. for value in values:
  100. if not isinstance(value, str):
  101. raise ValueError(field + ' is invalid')
  102. value = value.strip()
  103. if not value or len(value) > 100:
  104. raise ValueError(field + ' is invalid')
  105. if value not in result:
  106. result.append(value)
  107. if len(result) > 200:
  108. raise ValueError('at most 200 numbers are allowed')
  109. return result
  110. @staticmethod
  111. def _bounded_integer(value, field):
  112. if isinstance(value, bool) or not isinstance(value, int):
  113. raise ValueError(field + ' is invalid')
  114. if value < 1 or value > 100:
  115. raise ValueError(field + ' is invalid')
  116. return value