|
|
@@ -1,5 +1,6 @@
|
|
|
import json
|
|
|
import logging
|
|
|
+import math
|
|
|
from urllib.parse import urlparse
|
|
|
|
|
|
from constants import DEVICE_INVALID_MESSAGE
|
|
|
@@ -15,6 +16,11 @@ class OutputPresenter:
|
|
|
'query_track',
|
|
|
'query_customs_declaration_files',
|
|
|
'query_outbound_list',
|
|
|
+ 'query_customer_list',
|
|
|
+ ))
|
|
|
+ PAYMENT_FOLLOWUP_TOOLS = frozenset(('query_customer_payment_followup',))
|
|
|
+ PAYMENT_DETAIL_TOOLS = frozenset((
|
|
|
+ 'query_customer_unverified_bill_details',
|
|
|
))
|
|
|
DETAIL_TOOLS = frozenset(('query_outbound_detail',))
|
|
|
ORDER_DETAIL_TOOLS = frozenset(('query_order_detail',))
|
|
|
@@ -22,6 +28,7 @@ class OutputPresenter:
|
|
|
'list_order_filter_options',
|
|
|
'list_outbound_filter_options',
|
|
|
'list_pending_outbound_export_filter_options',
|
|
|
+ 'list_customer_filter_options',
|
|
|
))
|
|
|
EXPORT_TOOLS = frozenset((
|
|
|
'export_pending_outbound_orders',
|
|
|
@@ -29,7 +36,8 @@ class OutputPresenter:
|
|
|
))
|
|
|
TASK_TOOLS = frozenset(('query_export_task',))
|
|
|
SAFE_TOOLS = (
|
|
|
- TABLE_TOOLS | DETAIL_TOOLS | ORDER_DETAIL_TOOLS
|
|
|
+ TABLE_TOOLS | DETAIL_TOOLS | ORDER_DETAIL_TOOLS | PAYMENT_FOLLOWUP_TOOLS
|
|
|
+ | PAYMENT_DETAIL_TOOLS
|
|
|
| OPTION_TOOLS | EXPORT_TOOLS | TASK_TOOLS
|
|
|
)
|
|
|
|
|
|
@@ -290,6 +298,55 @@ class OutputPresenter:
|
|
|
'package_method': ('包装类型',),
|
|
|
'order_reply': ('到货回复',),
|
|
|
},
|
|
|
+ 'query_customer_list': {
|
|
|
+ 'customer_name': ('客户名称',),
|
|
|
+ 'customer_code': ('客户代码',),
|
|
|
+ 'create_time': ('开户时间',),
|
|
|
+ 'business_type': ('业务类型',),
|
|
|
+ 'customer_attribute': ('客户属性',),
|
|
|
+ 'customer_source': ('客户来源',),
|
|
|
+ 'first_inbound_date': ('首次成交时间',),
|
|
|
+ 'last_inbound_date': ('最后一次走货时间',),
|
|
|
+ 'active_status': ('活跃状态',),
|
|
|
+ 'contract_status': ('合同状态',),
|
|
|
+ 'contract_validity': ('合同有效期',),
|
|
|
+ 'credit_limit': ('信用额度',),
|
|
|
+ 'currency_code': ('结算币种',),
|
|
|
+ 'billing_modes': ('结算模式(分业务类型)',),
|
|
|
+ 'sales_name': ('商务经理',),
|
|
|
+ 'merchandiser_name': ('客户经理',),
|
|
|
+ 'department_name': ('事业部',),
|
|
|
+ },
|
|
|
+ }
|
|
|
+
|
|
|
+ PAYMENT_FOLLOWUP_COLUMNS = {
|
|
|
+ 'customer_name': '客户名称',
|
|
|
+ 'settlement_currency': '结算币种',
|
|
|
+ 'billed_unverified_amount': '已出账未核销金额',
|
|
|
+ 'unbilled_amount': '未出账金额',
|
|
|
+ 'overdue_unpaid_amount': '逾期未回款金额',
|
|
|
+ 'billed_unverified_amount_cny': '已出账未核销金额(人民币)',
|
|
|
+ 'unbilled_amount_cny': '未出账金额(人民币)',
|
|
|
+ 'overdue_unpaid_amount_cny': '逾期未回款金额(人民币)',
|
|
|
+ 'unverified_receivable_monthly_summary': '未回款月份汇总',
|
|
|
+ 'receipt_unverified_amount': '收款单未核销金额',
|
|
|
+ 'current_balance': '当前余额',
|
|
|
+ 'credit_limit': '信用额度',
|
|
|
+ 'bad_debt_total': '坏账合计',
|
|
|
+ 'contract_status': '合同状态',
|
|
|
+ }
|
|
|
+ PAYMENT_FOLLOWUP_DETAIL_COLUMNS = {
|
|
|
+ 'bill_month': '账单月份',
|
|
|
+ 'bill_no': '账单号',
|
|
|
+ 'business_type': '业务类型',
|
|
|
+ 'settlement_mode': '结算模式',
|
|
|
+ 'unverified_amount': '未核销金额',
|
|
|
+ 'customer_receivable_date': '客户应收款日期',
|
|
|
+ }
|
|
|
+ PAYMENT_FOLLOWUP_MONTHLY_COLUMNS = {
|
|
|
+ 'receivable_month': '应收月份',
|
|
|
+ 'unverified_amount': '未核销金额',
|
|
|
+ 'is_overdue': '是否逾期',
|
|
|
}
|
|
|
|
|
|
OUTBOUND_DETAIL_SUMMARY = {
|
|
|
@@ -388,6 +445,34 @@ class OutputPresenter:
|
|
|
'page': '页码',
|
|
|
'limit': '每页数量',
|
|
|
},
|
|
|
+ 'query_customer_list': {
|
|
|
+ 'customer_id': '客户名称',
|
|
|
+ 'department_id': '事业部',
|
|
|
+ 'sales_id': '商务经理',
|
|
|
+ 'merchandiser_id': '客户经理',
|
|
|
+ 'page': '页码',
|
|
|
+ 'limit': '每页数量',
|
|
|
+ },
|
|
|
+ 'query_customer_payment_followup': {
|
|
|
+ 'customer_id': '客户名称',
|
|
|
+ 'department_id': '事业部',
|
|
|
+ 'sales_id': '商务经理',
|
|
|
+ 'merchandiser_id': '客户经理',
|
|
|
+ 'has_unverified_receivable_only': '只看有应收未核销费用的客户',
|
|
|
+ 'page': '页码',
|
|
|
+ 'limit': '每页数量',
|
|
|
+ },
|
|
|
+ 'query_customer_unverified_bill_details': {
|
|
|
+ 'customer_id': '客户名称',
|
|
|
+ 'page': '页码',
|
|
|
+ 'limit': '每页数量',
|
|
|
+ },
|
|
|
+ 'list_customer_filter_options': {
|
|
|
+ 'filter_type': '筛选项类型',
|
|
|
+ 'keyword': '关键词',
|
|
|
+ 'page': '页码',
|
|
|
+ 'limit': '每页数量',
|
|
|
+ },
|
|
|
'export_pending_outbound_orders': {
|
|
|
'number': '单号',
|
|
|
'product_type_id': '产品分类',
|
|
|
@@ -483,6 +568,14 @@ class OutputPresenter:
|
|
|
)
|
|
|
if tool_name in self.ORDER_DETAIL_TOOLS:
|
|
|
return self._present_order_detail(data, tool_result.get('meta'), meta)
|
|
|
+ if tool_name in self.PAYMENT_FOLLOWUP_TOOLS:
|
|
|
+ return self._present_customer_payment_followup(
|
|
|
+ data, tool_result.get('meta'), meta
|
|
|
+ )
|
|
|
+ if tool_name in self.PAYMENT_DETAIL_TOOLS:
|
|
|
+ return self._present_customer_unverified_bill_details(
|
|
|
+ data, tool_result.get('meta'), meta
|
|
|
+ )
|
|
|
if tool_name in self.TABLE_TOOLS:
|
|
|
return self._present_table(
|
|
|
tool_name,
|
|
|
@@ -491,6 +584,10 @@ class OutputPresenter:
|
|
|
meta,
|
|
|
)
|
|
|
if tool_name in self.OPTION_TOOLS:
|
|
|
+ if tool_name == 'list_customer_filter_options':
|
|
|
+ return self._present_customer_options(
|
|
|
+ data, tool_result.get('meta'), meta
|
|
|
+ )
|
|
|
return self._present_options(data, tool_result.get('meta'), meta)
|
|
|
if tool_name in self.EXPORT_TOOLS:
|
|
|
return self._present_export_submission(data, meta)
|
|
|
@@ -751,6 +848,15 @@ class OutputPresenter:
|
|
|
if not isinstance(allowed_columns, dict):
|
|
|
return self._format_error(meta)
|
|
|
|
|
|
+ if tool_name == 'query_customer_list':
|
|
|
+ expected = list(allowed_columns.keys())
|
|
|
+ actual = [
|
|
|
+ column.get('key') if isinstance(column, dict) else None
|
|
|
+ for column in columns
|
|
|
+ ]
|
|
|
+ if actual != expected or self._customer_pagination(raw_meta) is None:
|
|
|
+ return self._format_error(meta)
|
|
|
+
|
|
|
headers = []
|
|
|
keys = []
|
|
|
for column in columns:
|
|
|
@@ -776,24 +882,297 @@ class OutputPresenter:
|
|
|
for record in records:
|
|
|
if not isinstance(record, dict):
|
|
|
return self._format_error(meta)
|
|
|
- rows.append([
|
|
|
- '' if record.get(key) is None else record.get(key, '')
|
|
|
- for key in keys
|
|
|
- ])
|
|
|
+ if tool_name == 'query_customer_list' and set(record) != set(keys):
|
|
|
+ return self._format_error(meta)
|
|
|
+ row = []
|
|
|
+ for key in keys:
|
|
|
+ value = '' if record.get(key) is None else record.get(key, '')
|
|
|
+ if tool_name == 'query_customer_list' and key == 'billing_modes':
|
|
|
+ value = self._customer_billing_modes(value)
|
|
|
+ if value is None:
|
|
|
+ return self._format_error(meta)
|
|
|
+ elif tool_name == 'query_customer_list' and isinstance(value, (dict, list)):
|
|
|
+ return self._format_error(meta)
|
|
|
+ row.append(value)
|
|
|
+ rows.append(row)
|
|
|
|
|
|
content = {
|
|
|
'summary': self._safe_text(data.get('summary')),
|
|
|
'headers': headers,
|
|
|
'rows': rows,
|
|
|
}
|
|
|
+ if tool_name == 'query_customer_list':
|
|
|
+ content['display_rules'] = {
|
|
|
+ 'mode': 'complete',
|
|
|
+ 'allow_summary': False,
|
|
|
+ 'allow_omit_records': False,
|
|
|
+ 'allow_omit_empty_fields': False,
|
|
|
+ 'allow_rename_fields': False,
|
|
|
+ 'preserve_record_order': True,
|
|
|
+ 'required_field_count': len(headers),
|
|
|
+ 'returned_record_count': len(rows),
|
|
|
+ 'required_value_count': len(headers) * len(rows),
|
|
|
+ 'instruction': '最终回复必须逐条展示全部记录及全部17个字段,不得摘要、省略或改写',
|
|
|
+ }
|
|
|
tips = self._safe_tips(data.get('tips'))
|
|
|
if tips:
|
|
|
content['tips'] = tips
|
|
|
pagination = self._build_pagination(raw_meta)
|
|
|
if pagination:
|
|
|
content['pagination'] = pagination
|
|
|
+ return self._success_result(
|
|
|
+ content,
|
|
|
+ self._render_table(
|
|
|
+ content,
|
|
|
+ require_complete=tool_name == 'query_customer_list',
|
|
|
+ ),
|
|
|
+ meta,
|
|
|
+ )
|
|
|
+
|
|
|
+ def _present_customer_payment_followup(self, data, raw_meta, meta):
|
|
|
+ if set(data) != {'columns', 'records'}:
|
|
|
+ return self._format_error(meta)
|
|
|
+ columns = data.get('columns')
|
|
|
+ records = data.get('records')
|
|
|
+ pagination = self._customer_pagination(raw_meta)
|
|
|
+ expected_keys = list(self.PAYMENT_FOLLOWUP_COLUMNS)
|
|
|
+ if not isinstance(columns, list) or not isinstance(records, list):
|
|
|
+ return self._format_error(meta)
|
|
|
+ actual_keys = []
|
|
|
+ for column in columns:
|
|
|
+ if (
|
|
|
+ not isinstance(column, dict)
|
|
|
+ or set(column) != {'key', 'name'}
|
|
|
+ or not isinstance(column.get('key'), str)
|
|
|
+ or not isinstance(column.get('name'), str)
|
|
|
+ or not column.get('name').strip()
|
|
|
+ ):
|
|
|
+ return self._format_error(meta)
|
|
|
+ actual_keys.append(column['key'])
|
|
|
+ if actual_keys != expected_keys or pagination is None:
|
|
|
+ return self._format_error(meta)
|
|
|
+
|
|
|
+ amount_keys = {
|
|
|
+ 'billed_unverified_amount', 'unbilled_amount',
|
|
|
+ 'overdue_unpaid_amount', 'billed_unverified_amount_cny',
|
|
|
+ 'unbilled_amount_cny', 'overdue_unpaid_amount_cny',
|
|
|
+ 'receipt_unverified_amount', 'current_balance',
|
|
|
+ 'credit_limit', 'bad_debt_total',
|
|
|
+ }
|
|
|
+ text_keys = {'customer_name', 'settlement_currency', 'contract_status'}
|
|
|
+ rows = []
|
|
|
+ for record in records:
|
|
|
+ if not isinstance(record, dict) or set(record) != set(expected_keys):
|
|
|
+ return self._format_error(meta)
|
|
|
+ row = []
|
|
|
+ for key in expected_keys:
|
|
|
+ value = record[key]
|
|
|
+ if key in amount_keys:
|
|
|
+ if (
|
|
|
+ isinstance(value, bool)
|
|
|
+ or not isinstance(value, (int, float))
|
|
|
+ or not math.isfinite(float(value))
|
|
|
+ ):
|
|
|
+ return self._format_error(meta)
|
|
|
+ elif key in text_keys:
|
|
|
+ if not isinstance(value, str):
|
|
|
+ return self._format_error(meta)
|
|
|
+ else:
|
|
|
+ value = self._payment_followup_monthly_summaries(value)
|
|
|
+ if value is None:
|
|
|
+ return self._format_error(meta)
|
|
|
+ row.append(value)
|
|
|
+ rows.append(row)
|
|
|
+
|
|
|
+ headers = [
|
|
|
+ {'label': self.PAYMENT_FOLLOWUP_COLUMNS[key]}
|
|
|
+ for key in expected_keys
|
|
|
+ ]
|
|
|
+ content = {
|
|
|
+ 'headers': headers,
|
|
|
+ 'rows': rows,
|
|
|
+ 'pagination': pagination,
|
|
|
+ 'display_rules': {
|
|
|
+ 'mode': 'complete',
|
|
|
+ 'allow_summary': False,
|
|
|
+ 'allow_omit_records': False,
|
|
|
+ 'allow_omit_empty_fields': False,
|
|
|
+ 'allow_rename_fields': False,
|
|
|
+ 'preserve_record_order': True,
|
|
|
+ 'required_field_count': len(headers),
|
|
|
+ 'returned_record_count': len(rows),
|
|
|
+ 'required_value_count': len(headers) * len(rows),
|
|
|
+ 'instruction': '最终回复必须逐条展示全部客户、全部字段和全部未回款月份汇总',
|
|
|
+ },
|
|
|
+ }
|
|
|
+ return self._success_result(
|
|
|
+ content,
|
|
|
+ self._render_table(content, require_complete=True),
|
|
|
+ meta,
|
|
|
+ )
|
|
|
+
|
|
|
+ def _payment_followup_monthly_summaries(self, summaries):
|
|
|
+ if not isinstance(summaries, list):
|
|
|
+ return None
|
|
|
+ expected = set(self.PAYMENT_FOLLOWUP_MONTHLY_COLUMNS)
|
|
|
+ translated = []
|
|
|
+ for summary in summaries:
|
|
|
+ if not isinstance(summary, dict) or set(summary) != expected:
|
|
|
+ return None
|
|
|
+ amount = summary.get('unverified_amount')
|
|
|
+ if (
|
|
|
+ isinstance(amount, bool)
|
|
|
+ or not isinstance(amount, (int, float))
|
|
|
+ or not math.isfinite(float(amount))
|
|
|
+ ):
|
|
|
+ return None
|
|
|
+ if (
|
|
|
+ not isinstance(summary.get('receivable_month'), str)
|
|
|
+ or not isinstance(summary.get('is_overdue'), bool)
|
|
|
+ ):
|
|
|
+ return None
|
|
|
+ item = {}
|
|
|
+ for key, label in self.PAYMENT_FOLLOWUP_MONTHLY_COLUMNS.items():
|
|
|
+ value = summary[key]
|
|
|
+ item[label] = value
|
|
|
+ translated.append(item)
|
|
|
+ return translated
|
|
|
+
|
|
|
+ def _present_customer_unverified_bill_details(self, data, raw_meta, meta):
|
|
|
+ if set(data) != {'columns', 'records'}:
|
|
|
+ return self._format_error(meta)
|
|
|
+ columns = data.get('columns')
|
|
|
+ records = data.get('records')
|
|
|
+ pagination = self._customer_pagination(raw_meta)
|
|
|
+ expected_keys = list(self.PAYMENT_FOLLOWUP_DETAIL_COLUMNS)
|
|
|
+ if not isinstance(columns, list) or not isinstance(records, list):
|
|
|
+ return self._format_error(meta)
|
|
|
+ actual_keys = []
|
|
|
+ for column in columns:
|
|
|
+ if (
|
|
|
+ not isinstance(column, dict)
|
|
|
+ or set(column) != {'key', 'name'}
|
|
|
+ or not isinstance(column.get('key'), str)
|
|
|
+ or not isinstance(column.get('name'), str)
|
|
|
+ or not column.get('name').strip()
|
|
|
+ ):
|
|
|
+ return self._format_error(meta)
|
|
|
+ actual_keys.append(column['key'])
|
|
|
+ if actual_keys != expected_keys or pagination is None:
|
|
|
+ return self._format_error(meta)
|
|
|
+
|
|
|
+ translated = self._payment_followup_details(records)
|
|
|
+ if translated is None:
|
|
|
+ return self._format_error(meta)
|
|
|
+ headers = [
|
|
|
+ {'label': self.PAYMENT_FOLLOWUP_DETAIL_COLUMNS[key]}
|
|
|
+ for key in expected_keys
|
|
|
+ ]
|
|
|
+ rows = [[item[header['label']] for header in headers] for item in translated]
|
|
|
+ content = {'headers': headers, 'rows': rows, 'pagination': pagination}
|
|
|
+ return self._success_result(content, self._render_table(content), meta)
|
|
|
+
|
|
|
+ def _payment_followup_details(self, details):
|
|
|
+ expected = set(self.PAYMENT_FOLLOWUP_DETAIL_COLUMNS)
|
|
|
+ translated = []
|
|
|
+ for detail in details:
|
|
|
+ if not isinstance(detail, dict) or set(detail) != expected:
|
|
|
+ return None
|
|
|
+ amount = detail.get('unverified_amount')
|
|
|
+ if (
|
|
|
+ isinstance(amount, bool)
|
|
|
+ or not isinstance(amount, (int, float))
|
|
|
+ or not math.isfinite(float(amount))
|
|
|
+ ):
|
|
|
+ return None
|
|
|
+ item = {}
|
|
|
+ for key, label in self.PAYMENT_FOLLOWUP_DETAIL_COLUMNS.items():
|
|
|
+ value = detail[key]
|
|
|
+ if key != 'unverified_amount' and not isinstance(value, str):
|
|
|
+ return None
|
|
|
+ item[label] = value
|
|
|
+ translated.append(item)
|
|
|
+ return translated
|
|
|
+
|
|
|
+ @staticmethod
|
|
|
+ def _customer_billing_modes(value):
|
|
|
+ if not isinstance(value, list):
|
|
|
+ return None
|
|
|
+ result = []
|
|
|
+ business_types = set()
|
|
|
+ for item in value:
|
|
|
+ if not isinstance(item, dict) or set(item) != {
|
|
|
+ 'business_type', 'settlement_mode',
|
|
|
+ }:
|
|
|
+ return None
|
|
|
+ business_type = item.get('business_type')
|
|
|
+ settlement_mode = item.get('settlement_mode')
|
|
|
+ if (
|
|
|
+ not isinstance(business_type, str) or not business_type.strip()
|
|
|
+ or not isinstance(settlement_mode, str) or not settlement_mode.strip()
|
|
|
+ or business_type in business_types
|
|
|
+ ):
|
|
|
+ return None
|
|
|
+ business_types.add(business_type)
|
|
|
+ result.append({
|
|
|
+ '业务类型': business_type.strip(),
|
|
|
+ '结算模式': settlement_mode.strip(),
|
|
|
+ })
|
|
|
+ return result
|
|
|
+
|
|
|
+ def _present_customer_options(self, data, raw_meta, meta):
|
|
|
+ if set(data) != {'records'}:
|
|
|
+ return self._format_error(meta)
|
|
|
+ records = data.get('records')
|
|
|
+ pagination = self._customer_pagination(raw_meta)
|
|
|
+ if not isinstance(records, list) or pagination is None:
|
|
|
+ return self._format_error(meta)
|
|
|
+ rows = []
|
|
|
+ for record in records:
|
|
|
+ if not isinstance(record, dict) or set(record) != {'value', 'label', 'code'}:
|
|
|
+ return self._format_error(meta)
|
|
|
+ value = record.get('value')
|
|
|
+ label = record.get('label')
|
|
|
+ code = record.get('code')
|
|
|
+ if (
|
|
|
+ isinstance(value, bool) or not isinstance(value, int) or value < 1
|
|
|
+ or not isinstance(label, str) or not label.strip()
|
|
|
+ or not isinstance(code, str)
|
|
|
+ ):
|
|
|
+ return self._format_error(meta)
|
|
|
+ rows.append([value, label.strip(), code.strip()])
|
|
|
+ content = {
|
|
|
+ 'headers': [
|
|
|
+ {'label': '可传值'}, {'label': '显示名称'}, {'label': '业务编码'},
|
|
|
+ ],
|
|
|
+ 'rows': rows,
|
|
|
+ 'pagination': pagination,
|
|
|
+ }
|
|
|
return self._success_result(content, self._render_table(content), meta)
|
|
|
|
|
|
+ @staticmethod
|
|
|
+ def _customer_pagination(raw_meta):
|
|
|
+ if not isinstance(raw_meta, dict):
|
|
|
+ return None
|
|
|
+ required = {'page', 'limit', 'has_more'}
|
|
|
+ if (
|
|
|
+ not required.issubset(raw_meta)
|
|
|
+ or not set(raw_meta).issubset(required | {'request_id'})
|
|
|
+ ):
|
|
|
+ return None
|
|
|
+ page = raw_meta.get('page')
|
|
|
+ limit = raw_meta.get('limit')
|
|
|
+ has_more = raw_meta.get('has_more')
|
|
|
+ if (
|
|
|
+ isinstance(page, bool) or not isinstance(page, int)
|
|
|
+ or page < 1 or page > 100
|
|
|
+ or isinstance(limit, bool) or not isinstance(limit, int)
|
|
|
+ or limit < 1 or limit > 100
|
|
|
+ or not isinstance(has_more, bool)
|
|
|
+ ):
|
|
|
+ return None
|
|
|
+ return {'page': page, 'limit': limit, 'has_more': has_more}
|
|
|
+
|
|
|
def _present_outbound_detail(self, tool_name, data, raw_meta, meta):
|
|
|
summary = data.get('summary')
|
|
|
columns = data.get('columns')
|
|
|
@@ -1096,10 +1475,17 @@ class OutputPresenter:
|
|
|
return [cls._safe_text(item) for item in value if cls._safe_text(item)]
|
|
|
|
|
|
@classmethod
|
|
|
- def _render_table(cls, content):
|
|
|
+ def _render_table(cls, content, require_complete=False):
|
|
|
headers = content.get('headers') or []
|
|
|
rows = content.get('rows') or []
|
|
|
lines = []
|
|
|
+ if require_complete:
|
|
|
+ lines.append('完整客户数据,禁止摘要、合并、隐藏字段或省略空字段。')
|
|
|
+ lines.append(
|
|
|
+ '本页记录数:{0};每条字段数:{1};应展示字段值总数:{2}。'.format(
|
|
|
+ len(rows), len(headers), len(rows) * len(headers)
|
|
|
+ )
|
|
|
+ )
|
|
|
summary = cls._safe_text(content.get('summary'))
|
|
|
if summary:
|
|
|
lines.append(summary)
|
|
|
@@ -1115,6 +1501,12 @@ class OutputPresenter:
|
|
|
tips = content.get('tips') or []
|
|
|
if tips:
|
|
|
lines.append('提示:{0}'.format(';'.join(tips)))
|
|
|
+ if require_complete:
|
|
|
+ lines.append(
|
|
|
+ '展示完整性校验:已提供{0}条客户的全部{1}个字段。'.format(
|
|
|
+ len(rows), len(headers)
|
|
|
+ )
|
|
|
+ )
|
|
|
return '\n'.join(lines)
|
|
|
|
|
|
@staticmethod
|