class SaveFuelRateTool: name = 'save_fuel_rate' route_path = '/mcp/tools/saveFuelRate' backend = 'base' def __init__(self, api_client=None): self.api_client = api_client def metadata(self): return { 'name': self.name, 'description': ( '使用场景:按计费条件新增燃油系数,或编辑一条草稿燃油系数。' '新增时不要传燃油代码。编辑时必须传query_fuel_rate_list返回的燃油代码,并且只能提交一条费率。' '必须带燃油类型、开始生效时间,以及至少一条费率和计费条件。' '结束生效时间不传时按9999-12-31长期有效。' '燃油类型为1或销售燃油、2或成本燃油。编辑时不能修改燃油类型。' '只能编辑草稿,且只能编辑计费条件配置。' '计费条件和条件值都可以填代码或名称,必须完整匹配,不能按包含搜索。' '销售燃油可选物流产品、物流产品分类、仓库、地址库。' '成本燃油可选物流渠道、物流渠道分类、仓库、地址库。' '地址库可以填PY编号或地址库名称。' '同一条件组第一条不要填关系;后面的关系不传时按and。同一组不能同时使用and和or。' '新增和编辑后都是草稿,不会立即参与计费。生效请使用enable_fuel_rate。' '禁止使用:失效、删除、复制、手填不存在的燃油代码。' '参数只用于工具内部调用;最终回答只能展示中文业务名称和燃油代码。' ), 'input_schema': { 'type': 'object', 'properties': { 'price_type': { 'type': 'string', 'minLength': 1, 'maxLength': 20, 'description': '燃油类型。1或销售燃油,2或成本燃油。', }, 'start_date': { 'type': 'string', 'minLength': 8, 'maxLength': 30, 'description': '开始生效时间,格式YYYY-MM-DD。', }, 'end_date': { 'type': 'string', 'minLength': 8, 'maxLength': 30, 'description': '结束生效时间,格式YYYY-MM-DD。不传表示9999-12-31。', }, 'fuel_no': { 'type': 'string', 'minLength': 1, 'maxLength': 20, 'description': '要编辑的燃油代码,格式为RY加数字。新增时不要传。', }, 'items': { 'type': 'array', 'minItems': 1, 'maxItems': 20, 'items': { 'type': 'object', 'properties': { 'fuel_ratio': { 'type': 'string', 'minLength': 1, 'maxLength': 10, 'description': '燃油费率,0到1,最多4位小数。', }, 'conditions': { 'type': 'array', 'minItems': 1, 'maxItems': 30, 'items': { 'type': 'object', 'properties': { 'condition': { 'type': 'string', 'minLength': 1, 'maxLength': 50, 'description': '计费条件代码或名称,必须完整匹配。', }, 'group': { 'type': 'string', 'minLength': 1, 'maxLength': 20, 'description': '条件组编号,同组条件写同一个组号。', }, 'condition_value': { 'type': 'string', 'minLength': 1, 'maxLength': 100, 'description': '条件值代码或名称,必须完整匹配。多选用英文逗号分隔。', }, 'condition_relation': { 'type': 'string', 'enum': ['and', 'or'], 'description': '与同组上一条的关系。同组第一条不要传。', }, }, 'required': ['condition', 'group', 'condition_value'], 'additionalProperties': False, }, }, }, 'required': ['fuel_ratio', 'conditions'], 'additionalProperties': False, }, }, }, 'required': ['price_type', 'start_date', 'items'], 'additionalProperties': False, }, } def call( self, price_type, start_date, items, end_date=None, fuel_no=None, request_id='rq_save_fuel_rate', ): if self.api_client is None: raise RuntimeError('api client is required for save_fuel_rate') built_items = _items(items) payload = { 'price_type': _choice(price_type, ('1', '2', '销售燃油', '成本燃油'), 'price_type'), 'start_date': _date(start_date, 'start_date'), 'items': built_items, } if end_date is not None: payload['end_date'] = _date(end_date, 'end_date') if fuel_no is not None: if not isinstance(fuel_no, str) or not fuel_no.strip() or len(fuel_no.strip()) > 20: raise ValueError('fuel_no is invalid') if len(built_items) != 1: raise ValueError('items is invalid') payload['fuel_no'] = fuel_no.strip() return self.api_client.call_tool(self.name, self.route_path, payload, request_id) def _choice(value, choices, key): if isinstance(value, bool): raise ValueError(key + ' is invalid') if isinstance(value, int): value = str(value) if not isinstance(value, str) or value.strip() not in choices: raise ValueError(key + ' is invalid') return value.strip() def _date(value, key): if not isinstance(value, str) or not value.strip() or len(value.strip()) > 30: raise ValueError(key + ' is invalid') return value.strip() def _items(items): if not isinstance(items, list) or not items or len(items) > 20: raise ValueError('items is invalid') rows = [] for item in items: if not isinstance(item, dict) or set(item) - {'fuel_ratio', 'conditions'}: raise ValueError('items is invalid') ratio = item.get('fuel_ratio') if isinstance(ratio, bool) or isinstance(ratio, int) or isinstance(ratio, float): ratio = '{0:.4f}'.format(ratio) if not isinstance(ratio, str) or not ratio.strip() or len(ratio.strip()) > 10: raise ValueError('fuel_ratio is invalid') rows.append({ 'fuel_ratio': ratio.strip(), 'conditions': _conditions(item.get('conditions')), }) return rows def _conditions(conditions): if not isinstance(conditions, list) or not conditions or len(conditions) > 30: raise ValueError('conditions is invalid') rows = [] allowed = {'condition', 'group', 'condition_value', 'condition_relation'} for condition in conditions: if not isinstance(condition, dict) or set(condition) - allowed: raise ValueError('conditions is invalid') row = { 'condition': _text(condition.get('condition'), 50, 'condition'), 'group': _text(condition.get('group'), 20, 'group'), 'condition_value': _text(condition.get('condition_value'), 100, 'condition_value'), } relation = condition.get('condition_relation') if relation is not None: if relation not in ('and', 'or'): raise ValueError('condition_relation is invalid') row['condition_relation'] = relation rows.append(row) return rows def _text(value, max_length, key): if not isinstance(value, str) or not value.strip() or len(value.strip()) > max_length: raise ValueError(key + ' is invalid') return value.strip()