query_customer_payment_records.py 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106
  1. import re
  2. from datetime import date
  3. class QueryCustomerPaymentRecordsTool:
  4. name = 'query_customer_payment_records'
  5. route_path = '/mcp/tools/queryCustomerPaymentRecords'
  6. def __init__(self, api_client=None):
  7. self.api_client = api_client
  8. def metadata(self):
  9. return {
  10. 'name': self.name,
  11. 'description': (
  12. '分页查询单个客户的逐笔收款记录、已核销金额和未核销金额。必须先调用'
  13. 'list_customer_filter_options并使用客户名称筛选取得customer_id;不得根据名称猜测'
  14. 'customer_id,不得模糊查询或改用其他工具试查。收款日期可按闭区间筛选,'
  15. '起止日期同时提供时闭区间最多包含366个日历日。公司、员工、菜单权限和客户数据范围由'
  16. '当前设备会话确定,调用方不得覆盖。'
  17. ),
  18. 'input_schema': {
  19. 'type': 'object',
  20. 'properties': {
  21. 'customer_id': {'type': 'integer', 'minimum': 1},
  22. 'receive_date_start': {
  23. 'type': 'string', 'format': 'date',
  24. 'pattern': '^\\d{4}-\\d{2}-\\d{2}$',
  25. },
  26. 'receive_date_end': {
  27. 'type': 'string', 'format': 'date',
  28. 'pattern': '^\\d{4}-\\d{2}-\\d{2}$',
  29. },
  30. 'page': {
  31. 'type': 'integer', 'minimum': 1, 'maximum': 100,
  32. 'default': 1,
  33. },
  34. 'limit': {
  35. 'type': 'integer', 'minimum': 1, 'maximum': 100,
  36. 'default': 20,
  37. },
  38. },
  39. 'required': ['customer_id'],
  40. 'additionalProperties': False,
  41. },
  42. }
  43. def call(
  44. self,
  45. customer_id,
  46. receive_date_start=None,
  47. receive_date_end=None,
  48. page=1,
  49. limit=20,
  50. request_id='rq_query_customer_payment_records',
  51. ):
  52. if self.api_client is None:
  53. raise RuntimeError(
  54. 'api client is required for query_customer_payment_records'
  55. )
  56. start = self._optional_date(receive_date_start, 'receive_date_start')
  57. end = self._optional_date(receive_date_end, 'receive_date_end')
  58. if start and end:
  59. span = (date.fromisoformat(end) - date.fromisoformat(start)).days
  60. if span < 0 or span > 365:
  61. raise ValueError('receive date range is invalid')
  62. payload = {
  63. 'customer_id': self._positive_integer(customer_id, 'customer_id'),
  64. 'page': self._bounded_integer(page, 'page'),
  65. 'limit': self._bounded_integer(limit, 'limit'),
  66. }
  67. if start:
  68. payload['receive_date_start'] = start
  69. if end:
  70. payload['receive_date_end'] = end
  71. return self.api_client.call_tool(
  72. self.name, self.route_path, payload, request_id
  73. )
  74. @staticmethod
  75. def _positive_integer(value, field):
  76. if isinstance(value, bool) or not isinstance(value, int) or value < 1:
  77. raise ValueError('{0} is invalid'.format(field))
  78. return value
  79. @classmethod
  80. def _bounded_integer(cls, value, field):
  81. value = cls._positive_integer(value, field)
  82. if value > 100:
  83. raise ValueError('{0} is invalid'.format(field))
  84. return value
  85. @staticmethod
  86. def _optional_date(value, field):
  87. if value is None or value == '':
  88. return ''
  89. if not isinstance(value, str):
  90. raise ValueError('{0} is invalid'.format(field))
  91. if re.fullmatch(r'[0-9]{4}-[0-9]{2}-[0-9]{2}', value) is None:
  92. raise ValueError('{0} is invalid'.format(field))
  93. try:
  94. date.fromisoformat(value)
  95. except ValueError:
  96. raise ValueError('{0} is invalid'.format(field))
  97. return value