From df8439fcb59ab139fc8fadee856b764028751aeb Mon Sep 17 00:00:00 2001 From: nanguaboss <602995148@qq.com> Date: Sun, 9 Aug 2026 23:15:39 +0800 Subject: [PATCH] =?UTF-8?q?v1.0.40:=20=E7=A2=8E=E7=89=87=E5=A4=84=E7=90=86?= =?UTF-8?q?=E6=9D=BF=E5=9D=97=E8=B0=83=E6=95=B4=20-=20=E5=8C=BA=E5=9D=97?= =?UTF-8?q?=E6=94=B9=E5=90=8D(=E7=A1=AC=E6=95=B0=E6=8D=AE=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E4=B8=8E=E5=A4=84=E7=90=86/=E8=BD=AF=E6=95=B0?= =?UTF-8?q?=E6=8D=AE=E7=A2=8E=E7=89=87=E5=A4=84=E7=90=86)+=E8=BD=AF?= =?UTF-8?q?=E7=A2=8E=E7=89=87=E5=88=97=E8=A1=A8=E6=97=B6=E9=97=B4=E5=88=97?= =?UTF-8?q?=E6=94=B9updated=5Fat(social=5Faccounts=E8=A1=A5=E5=88=97?= =?UTF-8?q?=E8=BF=81=E7=A7=BB)+=E7=A1=AC=E6=95=B0=E6=8D=AE=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=85=A8=E5=AD=97=E6=AE=B5=E5=94=AF=E4=B8=80?= =?UTF-8?q?=E6=80=A7=E6=A0=A1=E9=AA=8C(=E4=BC=81=E4=B8=9A=E5=90=8D/?= =?UTF-8?q?=E6=89=8B=E6=9C=BA/=E9=82=AE=E7=AE=B1/=E8=BA=AB=E4=BB=BD?= =?UTF-8?q?=E8=AF=81/=E8=B4=A6=E5=8F=B7ID=E5=90=8C=E5=B9=B3=E5=8F=B0/?= =?UTF-8?q?=E4=B8=BB=E9=A1=B5=E9=93=BE=E6=8E=A5)+=E6=A0=BC=E5=BC=8F?= =?UTF-8?q?=E6=A0=A1=E9=AA=8C(=E8=BA=AB=E4=BB=BD=E8=AF=81/=E4=B8=BB?= =?UTF-8?q?=E9=A1=B5URL)+extra=5Finfo=E5=AD=98=E5=82=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/fragment/hard_add.php | 49 ++++++++++++++++++++++++++++++++++---- api/fragment/soft_list.php | 10 ++++---- sql/migration_v1.0.40.sql | 8 +++++++ static/js/config.js | 2 +- static/js/fragment.js | 19 +++++++++++---- 5 files changed, 72 insertions(+), 16 deletions(-) create mode 100644 sql/migration_v1.0.40.sql diff --git a/api/fragment/hard_add.php b/api/fragment/hard_add.php index 89a5df6..30b337b 100644 --- a/api/fragment/hard_add.php +++ b/api/fragment/hard_add.php @@ -11,6 +11,7 @@ require_once __DIR__ . '/../common/response.php'; require_once __DIR__ . '/../common/auth.php'; require_once __DIR__ . '/../common/logger.php'; require_once __DIR__ . '/../common/validate.php'; +require_once __DIR__ . '/../common/duplicate_check.php'; checkAjax(); checkPermission('preliminary'); @@ -24,6 +25,10 @@ $companyName = trim($_POST['company_name'] ?? ''); $personName = trim($_POST['person_name'] ?? ''); $phone = trim($_POST['contact_phone'] ?? ''); $email = trim($_POST['contact_email'] ?? ''); +$idNumber = trim($_POST['id_number'] ?? ''); +$platform = trim($_POST['platform'] ?? ''); +$accountId = trim($_POST['account_id'] ?? ''); +$profileUrl = trim($_POST['profile_url'] ?? ''); $category = trim($_POST['target_product_category'] ?? ''); $description = trim($_POST['description'] ?? ''); $sourceChannel = trim($_POST['source_channel'] ?? ''); @@ -31,21 +36,53 @@ if ($sourceChannel === '') { $sourceChannel = '手动录入'; } -// 格式校验:手机 / 邮箱 +// 格式校验:手机 / 邮箱 / 身份证 / 主页链接 checkFieldFormat('phone', $phone); checkFieldFormat('email', $email); +checkFieldFormat('id_number', $idNumber); +if ($profileUrl !== '' && !preg_match('/^https?:\/\//i', $profileUrl)) { + Response::error('主页链接格式不正确(应以 http:// 或 https:// 开头)', 400); +} + +// 唯一性校验(收集全部重复):企业名称 / 手机 / 邮箱 / 身份证 / 账号ID(同平台)/ 主页链接 +$pdo = DB::getInstance()->getPdo(); +$dups = []; +if ($companyName !== '') { + $dup = findDuplicate($pdo, 'company_name', $companyName); + if ($dup) $dups[] = $dup; +} +$dups = array_merge($dups, checkSingleAccountDuplicate($pdo, $phone !== '' ? 'phone' : '', $phone, $profileUrl)); +if ($email !== '') { + $dup = findDuplicate($pdo, 'email', $email); + if ($dup) $dups[] = $dup; +} +if ($idNumber !== '') { + $dup = findDuplicate($pdo, 'id_number', $idNumber); + if ($dup) $dups[] = $dup; +} +if ($accountId !== '' && $platform !== '') { + $dup = findDuplicate($pdo, 'account_id', $accountId, [], $platform); + if ($dup) $dups[] = $dup; +} +if (!empty($dups)) Response::duplicates($dups); + +// 额外信息(身份证/社媒平台/账号ID/主页链接)存 extra_info +$extra = []; +if ($idNumber !== '') $extra['id_number'] = $idNumber; +if ($platform !== '') $extra['platform'] = $platform; +if ($accountId !== '') $extra['account_id'] = $accountId; +if ($profileUrl !== '') $extra['profile_url'] = $profileUrl; // 至少有一项内容才能录为碎片 -if ($companyName === '' && $personName === '' && $phone === '' && $email === '' && $category === '' && $description === '') { +if ($companyName === '' && $personName === '' && $phone === '' && $email === '' && $category === '' && $description === '' && !$extra) { Response::error('请至少填写一项内容', 400); } -$pdo = DB::getInstance()->getPdo(); $pdo->prepare( "INSERT INTO preliminary_data (source_type, company_name, person_name, contact_phone, contact_email, - target_product_category, description, source_channel, recorded_by, status) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, '待处理')" + target_product_category, description, source_channel, recorded_by, status, extra_info) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, '待处理', ?)" )->execute([ $sourceType, $companyName !== '' ? $companyName : null, @@ -56,6 +93,7 @@ $pdo->prepare( $description !== '' ? $description : null, $sourceChannel, $_SESSION['username'] ?? null, + $extra ? json_encode($extra, JSON_UNESCAPED_UNICODE) : null, ]); $newId = (int)$pdo->lastInsertId(); @@ -65,6 +103,7 @@ logCurrent('add', 'fragment', 'preliminary_data', $newId, [ 'person_name' => $personName, 'phone' => $phone, 'email' => $email, + 'extra' => $extra, ]); Response::success(['id' => $newId], '新增成功'); diff --git a/api/fragment/soft_list.php b/api/fragment/soft_list.php index 13baad0..058c6da 100644 --- a/api/fragment/soft_list.php +++ b/api/fragment/soft_list.php @@ -34,13 +34,13 @@ foreach ($tableMap as $t => $cfg) { } if ($t === 'media') { $rows = $pdo->query( - "SELECT m.social_account_id AS id, sa.account_id AS name, sa.created_at AS created_at + "SELECT m.social_account_id AS id, sa.account_id AS name, m.updated_at AS updated_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(); + $rows = $pdo->query("SELECT id, {$cfg['name']} AS name, updated_at FROM `$t` WHERE is_incomplete = 1")->fetchAll(); } foreach ($rows as $r) { $info = completenessInfo($pdo, $t, (int)$r['id']); @@ -65,17 +65,17 @@ foreach ($tableMap as $t => $cfg) { 'optional' => $info['layers']['optional']['rate'], ], 'missing_fields' => array_map(function ($f) { return COMPLETENESS_LABELS[$f] ?? $f; }, $missingFields), - 'created_at' => $r['created_at'] ?? null, + 'updated_at' => $r['updated_at'] ?? null, ]; } } -// 排序:整体完整度升序(最残缺在前),同分按时间倒序 +// 排序:整体完整度升序(最残缺在前),同分按更新时间倒序 usort($all, function ($a, $b) { if ($a['overall'] !== $b['overall']) { return $a['overall'] <=> $b['overall']; } - return strcmp($b['created_at'] ?? '', $a['created_at'] ?? ''); + return strcmp($b['updated_at'] ?? '', $a['updated_at'] ?? ''); }); if ($keyword !== '') { diff --git a/sql/migration_v1.0.40.sql b/sql/migration_v1.0.40.sql new file mode 100644 index 0000000..f3d45ad --- /dev/null +++ b/sql/migration_v1.0.40.sql @@ -0,0 +1,8 @@ +-- ============================================================ +-- SuperLink Web - v1.0.40 数据库结构变更 +-- social_accounts 增加 updated_at(软碎片列表「上一次更新」时间用) +-- ============================================================ +SET NAMES utf8mb4; + +ALTER TABLE `social_accounts` + ADD COLUMN `updated_at` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最近更新时间' AFTER `created_at`; diff --git a/static/js/config.js b/static/js/config.js index d788fe4..d4f49b3 100644 --- a/static/js/config.js +++ b/static/js/config.js @@ -5,7 +5,7 @@ var BASE_URL = '/api/'; var PAGE_SIZE = 20; /** 系统版本号(logo旁展示):修改代码后运行 tools/bump_version.php 自动递增 */ -var APP_VERSION = 'v1.0.39'; +var APP_VERSION = 'v1.0.40'; /** 页脚版权/备案信息(在 config.js 中修改) */ var FOOTER_TEXT = '© 2026 SuperLink 管理系统 版权所有 | 备案号:请替换为真实备案号'; diff --git a/static/js/fragment.js b/static/js/fragment.js index 45df6dc..833a9ff 100644 --- a/static/js/fragment.js +++ b/static/js/fragment.js @@ -42,7 +42,7 @@ $(function () { // ================= 硬碎片 ================= '
' + '
' + - ' 【硬碎片】' + + ' 【硬数据新增与处理】' + ' 关键字段缺失、未入主表的原始线索(不含已转换/已废弃)' + '
' + '
' + @@ -68,7 +68,7 @@ $(function () { // ================= 软碎片 ================= '
' + '
' + - ' 【软碎片】' + + ' 【软数据碎片处理】' + ' 已入主表但完整度低于阈值的记录(is_incomplete=1)' + '
' + '
' + @@ -83,7 +83,7 @@ $(function () { ' ' + '
' + '
' + - ' ' + + ' ' + ' ' + '
完整度缺失层级名称各层完整度缺失字段所属表录入时间操作
完整度缺失层级名称各层完整度缺失字段所属表上一次更新操作
' + ' ' + @@ -187,7 +187,7 @@ $(function () { '' + layerHtml + '' + '' + escHtml(missing) + '' + '' + escHtml(r.table_label) + '' + - '' + fmtDate(r.created_at) + '' + + '' + fmtDate(r.updated_at) + '' + '补全'; }); if (!softRows.length) { @@ -211,8 +211,15 @@ $(function () { '
' + '
' + '
' + + '
' + '
' + '
' + + '
' + + '
' + + '
' + '
' + '
' + '