v1.0.27: 碎片处理中心(完整方案) - 完整度计算引擎(四表分层/阈值)+is_incomplete标记(写操作挂钩)+api/fragment八接口(统计/硬碎片列表详情转换废弃/软碎片列表详情补全)+fragment.html前端(指标卡+硬软碎片双区块)

This commit is contained in:
nanguaboss
2026-08-09 15:47:05 +08:00
parent b68232bff3
commit 76431c20ac
25 changed files with 1342 additions and 3 deletions
+83
View File
@@ -0,0 +1,83 @@
<?php
/**
* 软碎片列表接口 GET /api/fragment/soft_list.php
* 参数:page / limit / table(所属表 persons|companies|social_accounts|media)/ level(core|important|supplementary)/ keyword
* 返回:{ list, total, page, limit },每行含 完整度 score、缺失层级标签、名称、缺失字段、所属表、录入时间
*/
require_once __DIR__ . '/../common/db.php';
require_once __DIR__ . '/../common/response.php';
require_once __DIR__ . '/../common/auth.php';
require_once __DIR__ . '/../common/completeness.php';
checkPermission('preliminary');
[$page, $limit] = pageParams();
$table = trim($_REQUEST['table'] ?? '');
$level = trim($_REQUEST['level'] ?? '');
$keyword = trim($_REQUEST['keyword'] ?? '');
$tableMap = [
'persons' => ['label' => '人员', 'name' => 'full_name'],
'companies' => ['label' => '企业', 'name' => 'display_name'],
'social_accounts' => ['label' => '联系方式', 'name' => 'account_id'],
'media' => ['label' => '媒体', 'name' => null], // media 需 JOIN social_accounts 取名
];
$pdo = DB::getInstance()->getPdo();
$all = [];
foreach ($tableMap as $t => $cfg) {
if ($table !== '' && $table !== $t) {
continue;
}
if ($t === 'media') {
$rows = $pdo->query(
"SELECT m.social_account_id AS id, sa.account_id AS name, m.updated_at AS created_at
FROM media_commercial_attributes m
LEFT JOIN social_accounts sa ON sa.id = m.social_account_id
WHERE m.is_incomplete = 1"
)->fetchAll();
} else {
$rows = $pdo->query("SELECT id, {$cfg['name']} AS name, created_at FROM `$t` WHERE is_incomplete = 1")->fetchAll();
}
foreach ($rows as $r) {
$info = completenessInfo($pdo, $t, (int)$r['id']);
if (!$info) {
continue;
}
if ($level !== '' && $info['level'] !== $level) {
continue;
}
$missingFields = array_merge($info['missing_core'], $info['missing_important'], $info['missing_supp']);
$all[] = [
'table' => $t,
'table_label' => $cfg['label'],
'id' => (int)$r['id'],
'name' => $r['name'] ?: ('#' . $r['id']),
'score' => $info['score'],
'level' => $info['level'],
'missing_fields' => array_map(function ($f) { return COMPLETENESS_LABELS[$f] ?? $f; }, $missingFields),
'created_at' => $r['created_at'] ?? null,
];
}
}
// 按完整度升序(最残缺的排前面),再按时间倒序
usort($all, function ($a, $b) {
if ($a['score'] !== $b['score']) {
return $a['score'] <=> $b['score'];
}
return strcmp($b['created_at'] ?? '', $a['created_at'] ?? '');
});
if ($keyword !== '') {
$all = array_values(array_filter($all, function ($r) use ($keyword) {
return mb_strpos($r['name'], $keyword) !== false;
}));
}
$total = count($all);
$offset = ($page - 1) * $limit;
$list = array_slice($all, $offset, $limit);
Response::success(['list' => $list, 'total' => $total, 'page' => $page, 'limit' => $limit]);