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
+144
View File
@@ -0,0 +1,144 @@
<?php
/**
* 软碎片补全提交接口 POST /api/fragment/soft_update.php
* 入参:table(persons|companies|social_accounts|media)/ id / 需补全字段(各表白名单)
* 逻辑:更新主表字段 → 重新计算完整度 → 达标则 is_incomplete=0
* 特殊:persons 的 phone/email 写 social_accounts(platform=phone/email)
*/
require_once __DIR__ . '/../common/db.php';
require_once __DIR__ . '/../common/response.php';
require_once __DIR__ . '/../common/auth.php';
require_once __DIR__ . '/../common/logger.php';
require_once __DIR__ . '/../common/helpers.php';
require_once __DIR__ . '/../common/completeness.php';
checkAjax();
checkPermission('preliminary');
$table = trim($_POST['table'] ?? '');
$id = (int)($_POST['id'] ?? 0);
if (!in_array($table, ['persons', 'companies', 'social_accounts', 'media'], true) || $id <= 0) {
Response::error('参数错误', 400);
}
$pdo = DB::getInstance()->getPdo();
/** 按白名单收集 POST 字段 */
$fields = [];
$collect = function ($whitelist) use (&$fields) {
foreach ($whitelist as $k => $type) {
if (!isset($_POST[$k])) {
continue;
}
$v = trim((string)$_POST[$k]);
if ($v === '') {
$fields[$k] = null;
continue;
}
switch ($type) {
case 'i':
$fields[$k] = (int)$v;
break;
case 'd':
$fields[$k] = (strtotime($v) !== false) ? date('Y-m-d', strtotime($v)) : null;
break;
default:
$fields[$k] = $v;
}
}
};
$isComplete = false;
switch ($table) {
case 'persons':
$collect([
'full_name' => 's', 'gender' => 's', 'nationality' => 's', 'education' => 's',
'graduated_from' => 's', 'hometown' => 's', 'work_location' => 's',
'id_type' => 's', 'id_number' => 's', 'source_channel' => 's', 'source_detail' => 's',
]);
if (!empty($fields)) {
[$sets, $params] = buildUpdate($fields);
$params[] = $id;
$pdo->prepare("UPDATE persons SET $sets WHERE id = ?")->execute($params);
}
// phone/email 写 social_accounts(有则更新,无则插入)
foreach (['phone', 'email'] as $plat) {
if (!isset($_POST[$plat])) {
continue;
}
$v = trim((string)$_POST[$plat]);
$exist = $pdo->prepare("SELECT id FROM social_accounts WHERE owner_type = 'person' AND owner_id = ? AND platform = ?");
$exist->execute([$id, $plat]);
$accId = (int)$exist->fetchColumn();
if ($accId > 0) {
if ($v === '') {
$pdo->prepare("DELETE FROM social_accounts WHERE id = ?")->execute([$accId]);
} else {
$pdo->prepare("UPDATE social_accounts SET account_id = ? WHERE id = ?")->execute([$v, $accId]);
}
} elseif ($v !== '') {
$pdo->prepare(
"INSERT INTO social_accounts (owner_type, owner_id, platform, account_id, is_primary, is_defult)
VALUES ('person', ?, ?, ?, 1, 1)"
)->execute([$id, $plat, $v]);
}
}
$info = updateIncomplete($pdo, 'persons', $id);
updateAccountsIncomplete($pdo, 'person', $id);
break;
case 'companies':
$collect([
'display_name' => 's', 'name_zh' => 's', 'name_en' => 's', 'business_role' => 's',
'country' => 's', 'registration_number' => 's', 'address' => 's', 'legal_form' => 's',
'legal_representative' => 's', 'business_scope' => 's', 'established_date' => 'd',
'registered_capital' => 's', 'industry' => 's', 'industry_subdivision' => 's',
'latest_employee_count' => 'i', 'latest_annual_revenue' => 's', 'is_listed' => 'i',
'stock_code' => 's', 'website' => 's',
]);
if (!empty($fields)) {
[$sets, $params] = buildUpdate($fields);
$params[] = $id;
$pdo->prepare("UPDATE companies SET $sets WHERE id = ?")->execute($params);
}
$info = updateIncomplete($pdo, 'companies', $id);
break;
case 'social_accounts':
$collect([
'platform' => 's', 'account_id' => 's', 'is_primary' => 'i', 'profile_url' => 's', 'remark' => 's',
]);
if (!empty($fields)) {
[$sets, $params] = buildUpdate($fields);
$params[] = $id;
$pdo->prepare("UPDATE social_accounts SET $sets WHERE id = ?")->execute($params);
}
$info = updateIncomplete($pdo, 'social_accounts', $id);
break;
case 'media':
$collect([
'account_level' => 's', 'content_categories' => 's', 'follower_count' => 'i',
'avg_read_count' => 'i', 'certification_type' => 's', 'special_requirements' => 's', 'media_remark' => 's',
]);
if (!empty($fields)) {
[$sets, $params] = buildUpdate($fields);
$params[] = $id;
$pdo->prepare("UPDATE media_commercial_attributes SET $sets WHERE social_account_id = ?")->execute($params);
}
$info = updateIncomplete($pdo, 'media', $id);
updateIncomplete($pdo, 'social_accounts', $id);
break;
}
if (!$info) {
Response::error('记录不存在');
}
logCurrent('complete', 'fragment', $table, $id, ['fields' => array_keys($fields), 'score' => $info['score']]);
Response::success([
'score' => $info['score'],
'level' => $info['level'],
'is_complete' => $info['is_complete'],
], $info['is_complete'] ? '补全完成' : '已更新,完整度 ' . $info['score'] . '%');