v1.0.29: 碎片处理按dp方案2修订 - 完整度改三层独立判定(核心100%/重要60%/非必要40%,任一不达标即碎片)+字段分层调整(persons email升核心,companies核心扩5字段)+指标卡5张(新增累计处理,本月/累计=硬+软合计)+软碎片列表加各层完整度列+硬碎片完整度改已填/总可填
This commit is contained in:
@@ -1,45 +1,31 @@
|
||||
<?php
|
||||
/**
|
||||
* 硬碎片公共逻辑:字段分层 / 完整度计算 / 内容摘要
|
||||
* 硬碎片公共逻辑:完整度计算(按非空字段直接计数)+ 内容摘要
|
||||
* 被 hard_list.php 与 hard_detail.php 共用
|
||||
*/
|
||||
|
||||
/** 硬碎片字段分层(按 source_type) */
|
||||
const HARD_LAYERS = [
|
||||
'company' => ['core' => ['company_name', 'contact_phone'], 'important' => ['contact_email'], 'supplementary' => ['person_name', 'description', 'target_product_category']],
|
||||
'person' => ['core' => ['person_name', 'contact_phone'], 'important' => ['contact_email'], 'supplementary' => ['company_name', 'description', 'target_product_category']],
|
||||
'product' => ['core' => ['company_name', 'target_product_category'], 'important' => ['contact_phone', 'contact_email'], 'supplementary' => ['person_name', 'description']],
|
||||
'need' => ['core' => ['company_name', 'target_product_category'], 'important' => ['contact_phone', 'contact_email'], 'supplementary' => ['person_name', 'description']],
|
||||
'mixed' => ['core' => ['company_name', 'person_name'], 'important' => ['contact_phone', 'contact_email'], 'supplementary' => ['description', 'target_product_category']],
|
||||
];
|
||||
/** 硬碎片可统计字段(公司名/联系人/电话/邮箱/品类/描述) */
|
||||
const HARD_FIELDS = ['company_name', 'person_name', 'contact_phone', 'contact_email', 'target_product_category', 'description'];
|
||||
const HARD_LABELS = [
|
||||
'company_name' => '公司名称', 'person_name' => '联系人', 'contact_phone' => '电话',
|
||||
'contact_email' => '邮箱', 'target_product_category' => '产品品类', 'description' => '描述',
|
||||
];
|
||||
|
||||
/** 计算硬碎片完整度与缺失层级 */
|
||||
/** 计算硬碎片完整度:已填字段数 ÷ 总可填字段数 × 100% */
|
||||
function hardCompleteness($row)
|
||||
{
|
||||
$layers = HARD_LAYERS[$row['source_type']] ?? HARD_LAYERS['mixed'];
|
||||
$missing = ['core' => [], 'important' => [], 'supplementary' => []];
|
||||
foreach (['core', 'important', 'supplementary'] as $layer) {
|
||||
foreach ($layers[$layer] as $field) {
|
||||
if ($row[$field] === null || trim((string)$row[$field]) === '') {
|
||||
$missing[$layer][] = $field;
|
||||
}
|
||||
$filled = 0;
|
||||
$missing = [];
|
||||
foreach (HARD_FIELDS as $field) {
|
||||
if ($row[$field] !== null && trim((string)$row[$field]) !== '') {
|
||||
$filled++;
|
||||
} else {
|
||||
$missing[] = $field;
|
||||
}
|
||||
}
|
||||
$score = 100 - count($missing['core']) * 20 - count($missing['important']) * 8 - count($missing['supplementary']) * 2;
|
||||
$score = max(0, $score);
|
||||
$level = 'complete';
|
||||
if (!empty($missing['core'])) {
|
||||
$level = 'core';
|
||||
} elseif (!empty($missing['important'])) {
|
||||
$level = 'important';
|
||||
} elseif (!empty($missing['supplementary'])) {
|
||||
$level = 'supplementary';
|
||||
}
|
||||
return ['score' => $score, 'level' => $level, 'missing' => $missing];
|
||||
$total = count(HARD_FIELDS);
|
||||
$score = $total > 0 ? (int)round($filled / $total * 100) : 0;
|
||||
return ['score' => $score, 'missing' => $missing];
|
||||
}
|
||||
|
||||
/** 内容摘要:拼接已有关键信息 */
|
||||
|
||||
@@ -24,7 +24,6 @@ if (!$row) {
|
||||
|
||||
$calc = hardCompleteness($row);
|
||||
$row['completeness'] = $calc['score'];
|
||||
$row['level'] = $calc['level'];
|
||||
$row['missing'] = $calc['missing'];
|
||||
$row['summary'] = hardSummary($row);
|
||||
$row['source_type_label'] = ['company' => '企业', 'person' => '人员', 'product' => '产品', 'need' => '需求', 'mixed' => '混合'][$row['source_type']] ?? $row['source_type'];
|
||||
|
||||
@@ -52,7 +52,6 @@ $list = [];
|
||||
foreach ($rows as $r) {
|
||||
$calc = hardCompleteness($r);
|
||||
$r['completeness'] = $calc['score'];
|
||||
$r['level'] = $calc['level'];
|
||||
$r['missing'] = $calc['missing'];
|
||||
$r['summary'] = hardSummary($r);
|
||||
$r['source_type_label'] = ['company' => '企业', 'person' => '人员', 'product' => '产品', 'need' => '需求', 'mixed' => '混合'][$r['source_type']] ?? $r['source_type'];
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
<?php
|
||||
/**
|
||||
* 软碎片详情接口 GET /api/fragment/soft_detail.php?table=persons&id=1
|
||||
* 返回:{ table, id, name, score, level, missing_core, missing_important, missing_supp, threshold, row }
|
||||
* 返回:{ table, table_label, id, name, overall, level, layers, missing_core, missing_important,
|
||||
* missing_optional, missing_labels, row }
|
||||
*/
|
||||
require_once __DIR__ . '/../common/db.php';
|
||||
require_once __DIR__ . '/../common/response.php';
|
||||
@@ -41,16 +42,20 @@ switch ($table) {
|
||||
break;
|
||||
}
|
||||
|
||||
$label = function ($f) { return ['key' => $f, 'label' => COMPLETENESS_LABELS[$f] ?? $f]; };
|
||||
|
||||
Response::success([
|
||||
'table' => $table,
|
||||
'table_label' => $tableLabels[$table],
|
||||
'id' => $id,
|
||||
'name' => $name ?: ('#' . $id),
|
||||
'score' => $info['score'],
|
||||
'overall' => $info['overall'],
|
||||
'level' => $info['level'],
|
||||
'threshold' => $info['threshold'],
|
||||
'missing_core' => array_map(function ($f) { return ['key' => $f, 'label' => COMPLETENESS_LABELS[$f] ?? $f]; }, $info['missing_core']),
|
||||
'missing_important' => array_map(function ($f) { return ['key' => $f, 'label' => COMPLETENESS_LABELS[$f] ?? $f]; }, $info['missing_important']),
|
||||
'missing_supp' => array_map(function ($f) { return ['key' => $f, 'label' => COMPLETENESS_LABELS[$f] ?? $f]; }, $info['missing_supp']),
|
||||
'layers' => $info['layers'],
|
||||
'missing_core' => array_map($label, $info['missing']['core']),
|
||||
'missing_important' => array_map($label, $info['missing']['important']),
|
||||
'missing_optional' => array_map($label, $info['missing']['optional']),
|
||||
'missing_labels' => array_map(function ($f) { return COMPLETENESS_LABELS[$f] ?? $f; },
|
||||
array_merge($info['missing']['core'], $info['missing']['important'], $info['missing']['optional'])),
|
||||
'row' => $info['row'],
|
||||
]);
|
||||
|
||||
+26
-18
@@ -1,8 +1,10 @@
|
||||
<?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、缺失层级标签、名称、缺失字段、所属表、录入时间
|
||||
* 参数:page / limit / table(persons|companies|social_accounts|media)/ level(core|important|optional)/ keyword
|
||||
* 返回:{ list, total, page, limit }
|
||||
* 每行:{ table, table_label, id, name, overall 整体完整度, level 缺失层级,
|
||||
* layer_rates {core,important,optional}, missing_fields 缺失字段(按核心→重要→非必要优先级), created_at }
|
||||
*/
|
||||
require_once __DIR__ . '/../common/db.php';
|
||||
require_once __DIR__ . '/../common/response.php';
|
||||
@@ -17,10 +19,10 @@ $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 取名
|
||||
'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();
|
||||
@@ -32,7 +34,7 @@ foreach ($tableMap as $t => $cfg) {
|
||||
}
|
||||
if ($t === 'media') {
|
||||
$rows = $pdo->query(
|
||||
"SELECT m.social_account_id AS id, sa.account_id AS name, m.updated_at AS created_at
|
||||
"SELECT m.social_account_id AS id, sa.account_id AS name, sa.created_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"
|
||||
@@ -48,24 +50,30 @@ foreach ($tableMap as $t => $cfg) {
|
||||
if ($level !== '' && $info['level'] !== $level) {
|
||||
continue;
|
||||
}
|
||||
$missingFields = array_merge($info['missing_core'], $info['missing_important'], $info['missing_supp']);
|
||||
// 缺失字段按优先级:核心 → 重要 → 非必要
|
||||
$missingFields = array_merge($info['missing']['core'], $info['missing']['important'], $info['missing']['optional']);
|
||||
$all[] = [
|
||||
'table' => $t,
|
||||
'table_label' => $cfg['label'],
|
||||
'id' => (int)$r['id'],
|
||||
'name' => $r['name'] ?: ('#' . $r['id']),
|
||||
'score' => $info['score'],
|
||||
'level' => $info['level'],
|
||||
'table' => $t,
|
||||
'table_label' => $cfg['label'],
|
||||
'id' => (int)$r['id'],
|
||||
'name' => $r['name'] ?: ('#' . $r['id']),
|
||||
'overall' => $info['overall'],
|
||||
'level' => $info['level'],
|
||||
'layer_rates' => [
|
||||
'core' => $info['layers']['core']['rate'],
|
||||
'important' => $info['layers']['important']['rate'],
|
||||
'optional' => $info['layers']['optional']['rate'],
|
||||
],
|
||||
'missing_fields' => array_map(function ($f) { return COMPLETENESS_LABELS[$f] ?? $f; }, $missingFields),
|
||||
'created_at' => $r['created_at'] ?? null,
|
||||
'created_at' => $r['created_at'] ?? null,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
// 按完整度升序(最残缺的排前面),再按时间倒序
|
||||
// 排序:整体完整度升序(最残缺在前),同分按时间倒序
|
||||
usort($all, function ($a, $b) {
|
||||
if ($a['score'] !== $b['score']) {
|
||||
return $a['score'] <=> $b['score'];
|
||||
if ($a['overall'] !== $b['overall']) {
|
||||
return $a['overall'] <=> $b['overall'];
|
||||
}
|
||||
return strcmp($b['created_at'] ?? '', $a['created_at'] ?? '');
|
||||
});
|
||||
|
||||
@@ -135,10 +135,10 @@ if (!$info) {
|
||||
Response::error('记录不存在');
|
||||
}
|
||||
|
||||
logCurrent('complete', 'fragment', $table, $id, ['fields' => array_keys($fields), 'score' => $info['score']]);
|
||||
logCurrent('complete', 'fragment', $table, $id, ['fields' => array_keys($fields), 'overall' => $info['overall']]);
|
||||
|
||||
Response::success([
|
||||
'score' => $info['score'],
|
||||
'overall' => $info['overall'],
|
||||
'level' => $info['level'],
|
||||
'is_complete' => $info['is_complete'],
|
||||
], $info['is_complete'] ? '补全完成' : '已更新,完整度 ' . $info['score'] . '%');
|
||||
], $info['is_complete'] ? '补全完成' : '已更新,整体完整度 ' . $info['overall'] . '%');
|
||||
|
||||
@@ -1,8 +1,13 @@
|
||||
<?php
|
||||
/**
|
||||
* 碎片处理中心 - 顶部指标卡接口 GET /api/fragment/stats_overview.php
|
||||
* 返回:{ hard_total 硬碎片总数, soft_total 软碎片总数, month_new 本月新增,
|
||||
* month_processed 本月处理, pending 待处理碎片 }
|
||||
* 碎片处理中心 - 顶部概览接口 GET /api/fragment/stats_overview.php
|
||||
* 指标:
|
||||
* - hard_total 硬碎片待处理数(preliminary_data 未终结)
|
||||
* - soft_total 软碎片待处理数(四主表 is_incomplete=1)
|
||||
* - month_new 本月新增(硬碎片本月新增 + 软碎片本月新增)
|
||||
* - month_processed 本月处理(本月已转换硬碎片 + 本月已补全软碎片)
|
||||
* - cumulative 累计处理(历史已转换硬碎片 + 历史已补全软碎片)
|
||||
* - pending 待处理碎片(硬待处理 + 软待处理)
|
||||
*/
|
||||
require_once __DIR__ . '/../common/db.php';
|
||||
require_once __DIR__ . '/../common/response.php';
|
||||
@@ -11,39 +16,63 @@ require_once __DIR__ . '/../common/auth.php';
|
||||
checkPermission('preliminary');
|
||||
|
||||
$pdo = DB::getInstance()->getPdo();
|
||||
$monthStart = "DATE_FORMAT(CURDATE(), '%Y-%m-01')";
|
||||
|
||||
// 硬碎片:preliminary_data 中未终结(未转换/未废弃)的记录
|
||||
// 硬碎片待处理数
|
||||
$hardTotal = (int)$pdo->query(
|
||||
"SELECT COUNT(*) FROM preliminary_data WHERE is_active = 1 AND status NOT IN ('已转换','已废弃')"
|
||||
)->fetchColumn();
|
||||
|
||||
// 软碎片:四张主表 is_incomplete=1 之和
|
||||
// 软碎片待处理数(四主表 is_incomplete=1 之和)
|
||||
$softTotal = 0;
|
||||
foreach (['companies', 'persons', 'social_accounts', 'media_commercial_attributes'] as $t) {
|
||||
$softTotal += (int)$pdo->query("SELECT COUNT(*) FROM `$t` WHERE is_incomplete = 1")->fetchColumn();
|
||||
}
|
||||
|
||||
// 本月新增(硬碎片按 created_at)
|
||||
$monthNew = (int)$pdo->query(
|
||||
"SELECT COUNT(*) FROM preliminary_data WHERE is_active = 1 AND created_at >= DATE_FORMAT(CURDATE(), '%Y-%m-01')"
|
||||
// 本月新增硬碎片(created_at 在本月)
|
||||
$hardMonthNew = (int)$pdo->query(
|
||||
"SELECT COUNT(*) FROM preliminary_data WHERE is_active = 1 AND created_at >= $monthStart"
|
||||
)->fetchColumn();
|
||||
|
||||
// 本月处理(已转换/已废弃且 processed_at 在本月)
|
||||
$monthProcessed = (int)$pdo->query(
|
||||
// 本月新增软碎片(is_incomplete=1 且本月创建;media 的创建时间取关联 social_accounts)
|
||||
$softMonthNew = 0;
|
||||
foreach (['companies', 'persons', 'social_accounts'] as $t) {
|
||||
$softMonthNew += (int)$pdo->query(
|
||||
"SELECT COUNT(*) FROM `$t` WHERE is_incomplete = 1 AND created_at >= $monthStart"
|
||||
)->fetchColumn();
|
||||
}
|
||||
$softMonthNew += (int)$pdo->query(
|
||||
"SELECT COUNT(*) FROM media_commercial_attributes m
|
||||
WHERE m.is_incomplete = 1
|
||||
AND EXISTS (SELECT 1 FROM social_accounts sa WHERE sa.id = m.social_account_id AND sa.created_at >= $monthStart)"
|
||||
)->fetchColumn();
|
||||
|
||||
// 本月已转换硬碎片
|
||||
$hardConvertedMonth = (int)$pdo->query(
|
||||
"SELECT COUNT(*) FROM preliminary_data
|
||||
WHERE is_active = 1 AND status IN ('已转换','已废弃')
|
||||
AND processed_at >= DATE_FORMAT(CURDATE(), '%Y-%m-01')"
|
||||
WHERE is_active = 1 AND status = '已转换' AND processed_at >= $monthStart"
|
||||
)->fetchColumn();
|
||||
|
||||
// 待处理碎片
|
||||
$pending = (int)$pdo->query(
|
||||
"SELECT COUNT(*) FROM preliminary_data WHERE is_active = 1 AND status = '待处理'"
|
||||
// 本月已补全软碎片(fragment 模块 complete 日志)
|
||||
$softCompletedMonth = (int)$pdo->query(
|
||||
"SELECT COUNT(*) FROM system_logs WHERE action = 'complete' AND module = 'fragment' AND created_at >= $monthStart"
|
||||
)->fetchColumn();
|
||||
|
||||
// 历史累计已转换硬碎片
|
||||
$hardConvertedTotal = (int)$pdo->query(
|
||||
"SELECT COUNT(*) FROM preliminary_data WHERE is_active = 1 AND status = '已转换'"
|
||||
)->fetchColumn();
|
||||
|
||||
// 历史累计已补全软碎片
|
||||
$softCompletedTotal = (int)$pdo->query(
|
||||
"SELECT COUNT(*) FROM system_logs WHERE action = 'complete' AND module = 'fragment'"
|
||||
)->fetchColumn();
|
||||
|
||||
Response::success([
|
||||
'hard_total' => $hardTotal,
|
||||
'soft_total' => $softTotal,
|
||||
'month_new' => $monthNew,
|
||||
'month_processed' => $monthProcessed,
|
||||
'pending' => $pending,
|
||||
'hard_total' => $hardTotal,
|
||||
'soft_total' => $softTotal,
|
||||
'month_new' => $hardMonthNew + $softMonthNew,
|
||||
'month_processed' => $hardConvertedMonth + $softCompletedMonth,
|
||||
'cumulative' => $hardConvertedTotal + $softCompletedTotal,
|
||||
'pending' => $hardTotal + $softTotal,
|
||||
]);
|
||||
|
||||
Reference in New Issue
Block a user