更新
This commit is contained in:
@@ -24,7 +24,7 @@ class Tools
|
||||
self::tool('zyt_describe', '查看某个数据资源的说明:可用查询参数及含义、类型(列表/详情/统计)、口径说明。', [
|
||||
'resource' => ['type' => 'string', 'description' => '资源标识,来自 zyt_catalog,如 doctor.appointment/lists'],
|
||||
], ['resource']),
|
||||
self::tool('zyt_query', '查询列表或统计类资源,结果与该账号在甄养堂后台看到的一致(按其权限和数据范围)。列表默认每页 20 条、最多 50 条,返回 total 和 has_more。', [
|
||||
self::tool('zyt_query', '查询列表或统计类资源,结果与该账号在甄养堂后台看到的一致(按其权限和数据范围)。翻页一律用外层 page/page_size(列表默认每页 20 条、最多 50 条;自带分页的统计明细最多 100 条),结果里有 total 和 has_more(统计明细在 result.paging)。', [
|
||||
'resource' => ['type' => 'string', 'description' => '资源标识,如 tcm.diagnosis/lists'],
|
||||
'params' => ['type' => 'object', 'description' => '查询参数,名称见 zyt_describe;日期用 YYYY-MM-DD', 'additionalProperties' => true],
|
||||
'page' => ['type' => 'integer', 'minimum' => 1, 'description' => '页码,从 1 开始'],
|
||||
@@ -174,7 +174,41 @@ class Tools
|
||||
if (in_array($resource['kind'], ['list', 'table'], true)) {
|
||||
return self::runList($identity, $resource, $params, (int) ($args['page'] ?? 1), (int) ($args['page_size'] ?? McpConfig::defaultPageSize()), (array) ($args['fields'] ?? []), $audit);
|
||||
}
|
||||
return self::runReport($identity, $resource, $params, $audit);
|
||||
return self::runReport($identity, $resource, self::sinkPaging($resource, $params, $args), $audit);
|
||||
}
|
||||
|
||||
/**
|
||||
* 自带分页的统计明细(参数叫 page/page_no 和 page_size,如进线明细、被指派明细):外层的 page/page_size 放进参数。
|
||||
* 以前只看 params,模型照工具说明用外层 page 翻页时每次拿到的都是第一页,看起来像同一批记录反复出现。
|
||||
*/
|
||||
private static function sinkPaging(array $resource, array $params, array $args): array
|
||||
{
|
||||
$allowed = array_flip(Catalog::allowedParams($resource));
|
||||
$pageKey = isset($allowed['page']) ? 'page' : (isset($allowed['page_no']) ? 'page_no' : null);
|
||||
if ($pageKey !== null && !isset($params[$pageKey])) {
|
||||
$params[$pageKey] = max(1, (int) ($args['page'] ?? 1));
|
||||
}
|
||||
if (isset($allowed['page_size']) && !isset($params['page_size'])) {
|
||||
$params['page_size'] = max(1, min(100, (int) ($args['page_size'] ?? McpConfig::defaultPageSize())));
|
||||
}
|
||||
return $params;
|
||||
}
|
||||
|
||||
/** 同一页里 id 相同的行只留一条(后台列表的联表偶尔会把一条记录拆成多行);有行没有 id 时原样返回 */
|
||||
private static function dropDuplicateIds(array $rows): array
|
||||
{
|
||||
$seen = [];
|
||||
$out = [];
|
||||
foreach ($rows as $row) {
|
||||
if (!is_array($row) || !isset($row['id']) || !is_scalar($row['id'])) {
|
||||
return [$rows, 0];
|
||||
}
|
||||
if (!isset($seen[(string) $row['id']])) {
|
||||
$seen[(string) $row['id']] = true;
|
||||
$out[] = $row;
|
||||
}
|
||||
}
|
||||
return [$out, count($rows) - count($out)];
|
||||
}
|
||||
|
||||
private static function get(Identity $identity, array $args, array &$audit): array
|
||||
@@ -296,7 +330,7 @@ class Tools
|
||||
if ($envelope['code'] !== 1) {
|
||||
throw new McpException(self::failText($resource, $envelope), 'denied');
|
||||
}
|
||||
$rows = array_values((array) ($envelope['data']['lists'] ?? []));
|
||||
[$rows, $duplicates] = self::dropDuplicateIds(array_values((array) ($envelope['data']['lists'] ?? [])));
|
||||
$total = (int) ($envelope['data']['count'] ?? count($rows));
|
||||
if (count($rows) > $size) {
|
||||
// 个别列表不分页、总是返回全部行:在这里按页切片,避免超出篇幅和每日额度
|
||||
@@ -314,6 +348,9 @@ class Tools
|
||||
$audit['record_ids'] = AuditLogger::recordIds($rows);
|
||||
$data = ['resource' => $resource['key'], 'name' => $resource['name'], 'total' => $total, 'page' => $page, 'page_size' => $size,
|
||||
'has_more' => $page * $size < $total, 'rows' => $rows, 'masked' => $policy->maskedFields()];
|
||||
if ($duplicates > 0) {
|
||||
$data['duplicates_removed'] = $duplicates;
|
||||
}
|
||||
if (!empty($envelope['data']['extend'])) {
|
||||
$data['extend'] = $policy->apply($envelope['data']['extend']);
|
||||
}
|
||||
@@ -321,8 +358,8 @@ class Tools
|
||||
$data['note'] = $resource['note'];
|
||||
}
|
||||
$data = self::fit($data);
|
||||
$summary = sprintf('「%s」共 %d 条,本页第 %d 页 %d 条%s%s。', $resource['name'], $total, $page, count($data['rows']),
|
||||
$data['has_more'] ? ',还有更多(page=' . ($page + 1) . ')' : '', self::maskNote($policy));
|
||||
$summary = sprintf('「%s」共 %d 条,本页第 %d 页 %d 条%s%s%s。', $resource['name'], $total, $page, count($data['rows']),
|
||||
$data['has_more'] ? ',还有更多(page=' . ($page + 1) . ')' : '', $duplicates > 0 ? ';后台返回的重复记录 ' . $duplicates . ' 条已去掉' : '', self::maskNote($policy));
|
||||
return self::ok($summary, $data);
|
||||
}
|
||||
|
||||
@@ -335,15 +372,33 @@ class Tools
|
||||
}
|
||||
$policy = FieldPolicy::forIdentity($identity, 5000);
|
||||
$result = $policy->apply($envelope['data']);
|
||||
$rows = is_array($result) && isset($result['lists']) && is_array($result['lists']) ? count($result['lists']) : 1;
|
||||
$extra = '';
|
||||
$hasLists = is_array($result) && isset($result['lists']) && is_array($result['lists']);
|
||||
if ($hasLists) {
|
||||
[$result['lists'], $duplicates] = self::dropDuplicateIds(array_values($result['lists']));
|
||||
if ($duplicates > 0) {
|
||||
$result['duplicates_removed'] = $duplicates;
|
||||
$extra .= ';后台返回的重复记录 ' . $duplicates . ' 条已去掉';
|
||||
}
|
||||
// 自带分页的明细:告诉模型总数和下一页怎么取
|
||||
$total = $result['count'] ?? $result['total'] ?? null;
|
||||
$pageKey = isset($params['page']) ? 'page' : (isset($params['page_no']) ? 'page_no' : null);
|
||||
if (is_numeric($total) && $pageKey !== null && isset($params['page_size'])) {
|
||||
$page = (int) $params[$pageKey];
|
||||
$hasMore = $page * (int) $params['page_size'] < (int) $total;
|
||||
$result['paging'] = ['total' => (int) $total, 'page' => $page, 'page_size' => (int) $params['page_size'], 'has_more' => $hasMore];
|
||||
$extra .= sprintf(';共 %d 条,本页第 %d 页 %d 条%s', (int) $total, $page, count($result['lists']), $hasMore ? ',还有更多(page=' . ($page + 1) . ')' : '');
|
||||
}
|
||||
}
|
||||
$rows = $hasLists ? count($result['lists']) : 1;
|
||||
RateLimiter::addRows($identity->adminId, $rows);
|
||||
$audit['result_rows'] = $rows;
|
||||
if (is_array($result) && isset($result['lists']) && is_array($result['lists'])) {
|
||||
if ($hasLists) {
|
||||
$audit['record_ids'] = AuditLogger::recordIds($result['lists']);
|
||||
}
|
||||
$data = self::fit(['resource' => $resource['key'], 'name' => $resource['name'], 'params' => $params, 'result' => $result,
|
||||
'masked' => $policy->maskedFields(), 'note' => (string) ($resource['note'] ?? '')]);
|
||||
return self::ok('「' . $resource['name'] . '」统计结果' . self::maskNote($policy) . '。', $data);
|
||||
return self::ok('「' . $resource['name'] . '」统计结果' . $extra . self::maskNote($policy) . '。', $data);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -424,7 +479,7 @@ class Tools
|
||||
return array_merge($clean, (array) ($resource['force'] ?? []));
|
||||
}
|
||||
|
||||
private static function listParams(array $resource, array $params, int $page, int $size): array
|
||||
public static function listParams(array $resource, array $params, int $page, int $size): array
|
||||
{
|
||||
return array_merge($params, ['page_no' => $page, 'page_size' => $size, 'page_type' => 1], (array) ($resource['force'] ?? []));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user