v1.0.33: 全局唯一性校验 - 手机号/邮箱/身份证/企业注册号/社媒账号ID/主页链接/企业名称 新增与修改时校验,重复弹窗提示并跳转定位(后端duplicate_check引擎+code 1001,前端弹窗+person/company/media页?id=跳转)
This commit is contained in:
@@ -0,0 +1,240 @@
|
||||
<?php
|
||||
/**
|
||||
* 全局唯一性校验(2026-08-09)
|
||||
* 手机号码 / 邮箱 / 身份证号码 / 企业注册号 / 社媒账号ID / 主页链接 / 企业名称
|
||||
* 新增、修改时必须调用;重复时返回重复记录信息(供前端弹窗 + 跳转定位)
|
||||
* 说明:
|
||||
* - 手机/邮箱存在 social_accounts(platform=phone/email)
|
||||
* - 社媒账号ID 同平台内唯一(wechat/douyin 等,不含 phone/email)
|
||||
* - 主页链接 social_accounts.profile_url 全局唯一(非空才校验)
|
||||
* - 企业名称 name_zh 或 display_name 任一相同即重复
|
||||
*/
|
||||
require_once __DIR__ . '/db.php';
|
||||
|
||||
/** 字段中文名 */
|
||||
const DUP_LABELS = [
|
||||
'phone' => '手机号码',
|
||||
'email' => '邮箱',
|
||||
'id_number' => '身份证号码',
|
||||
'registration_number' => '企业注册号',
|
||||
'account_id' => '社媒账号ID',
|
||||
'profile_url' => '主页链接',
|
||||
'company_name' => '企业名称',
|
||||
];
|
||||
|
||||
/**
|
||||
* 查询 social_accounts 重复记录(phone/email/account_id/profile_url 共用)
|
||||
* @return array|null
|
||||
*/
|
||||
function findSocialDuplicate(PDO $pdo, $where, $params)
|
||||
{
|
||||
$sql =
|
||||
"SELECT sa.id, sa.owner_type, sa.owner_id,
|
||||
COALESCE(p.full_name, c.display_name) AS owner_name
|
||||
FROM social_accounts sa
|
||||
LEFT JOIN persons p ON p.id = sa.owner_id AND sa.owner_type = 'person'
|
||||
LEFT JOIN companies c ON c.id = sa.owner_id AND sa.owner_type = 'company'
|
||||
WHERE $where AND sa.is_active = 1
|
||||
LIMIT 1";
|
||||
$stmt = $pdo->prepare($sql);
|
||||
$stmt->execute($params);
|
||||
$row = $stmt->fetch();
|
||||
if (!$row) {
|
||||
return null;
|
||||
}
|
||||
return $row;
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验唯一性
|
||||
* @param PDO $pdo
|
||||
* @param string $type phone|email|id_number|registration_number|account_id|profile_url|company_name
|
||||
* @param string $value 待校验值
|
||||
* @param array $excludeIds 排除自身记录ID(编辑时;social_accounts 传其 id / persons 传 persons.id / companies 传 companies.id)
|
||||
* @param string|null $platform account_id 类型时必传(同平台内唯一)
|
||||
* @return array|null {label,value,table,id,owner_type,owner_id,name,owner_label,jump}
|
||||
*/
|
||||
function findDuplicate(PDO $pdo, $type, $value, $excludeIds = [], $platform = null)
|
||||
{
|
||||
$value = trim((string)$value);
|
||||
if ($value === '') {
|
||||
return null;
|
||||
}
|
||||
$excludeIds = array_values(array_filter(array_map('intval', (array)$excludeIds)));
|
||||
|
||||
$excludeSql = '';
|
||||
$excludeParams = [];
|
||||
if ($excludeIds) {
|
||||
$excludeSql = ' AND id NOT IN (' . implode(',', $excludeIds) . ')';
|
||||
}
|
||||
|
||||
switch ($type) {
|
||||
case 'phone':
|
||||
case 'email':
|
||||
$row = findSocialDuplicate($pdo, "sa.platform = ? AND LOWER(sa.account_id) = LOWER(?)" . str_replace('id NOT IN', 'sa.id NOT IN', $excludeSql), array_merge([$type, $value], $excludeParams));
|
||||
if (!$row) {
|
||||
return null;
|
||||
}
|
||||
return [
|
||||
'label' => $type === 'phone' ? '手机号码' : '邮箱',
|
||||
'value' => $value,
|
||||
'table' => 'social_accounts',
|
||||
'id' => (int)$row['id'],
|
||||
'owner_type' => $row['owner_type'],
|
||||
'owner_id' => (int)$row['owner_id'],
|
||||
'name' => $row['owner_name'] ?: ('#' . $row['owner_id']),
|
||||
'owner_label'=> $row['owner_type'] === 'person' ? '人员' : '企业',
|
||||
'jump' => 'media.html?id=' . (int)$row['id'],
|
||||
];
|
||||
|
||||
case 'account_id':
|
||||
// 社媒账号:同平台内唯一(phone/email 走专门校验)
|
||||
if ($platform === null || in_array($platform, ['phone', 'email'], true)) {
|
||||
return null;
|
||||
}
|
||||
$row = findSocialDuplicate($pdo, "sa.platform = ? AND LOWER(sa.account_id) = LOWER(?)" . str_replace('id NOT IN', 'sa.id NOT IN', $excludeSql), array_merge([$platform, $value], $excludeParams));
|
||||
if (!$row) {
|
||||
return null;
|
||||
}
|
||||
return [
|
||||
'label' => '社媒账号ID',
|
||||
'value' => $value,
|
||||
'table' => 'social_accounts',
|
||||
'id' => (int)$row['id'],
|
||||
'owner_type' => $row['owner_type'],
|
||||
'owner_id' => (int)$row['owner_id'],
|
||||
'name' => $row['owner_name'] ?: ('#' . $row['owner_id']),
|
||||
'owner_label'=> $row['owner_type'] === 'person' ? '人员' : '企业',
|
||||
'jump' => 'media.html?id=' . (int)$row['id'],
|
||||
];
|
||||
|
||||
case 'profile_url':
|
||||
$row = findSocialDuplicate($pdo, "sa.profile_url = ? AND sa.profile_url <> ''" . str_replace('id NOT IN', 'sa.id NOT IN', $excludeSql), array_merge([$value], $excludeParams));
|
||||
if (!$row) {
|
||||
return null;
|
||||
}
|
||||
return [
|
||||
'label' => '主页链接',
|
||||
'value' => $value,
|
||||
'table' => 'social_accounts',
|
||||
'id' => (int)$row['id'],
|
||||
'owner_type' => $row['owner_type'],
|
||||
'owner_id' => (int)$row['owner_id'],
|
||||
'name' => $row['owner_name'] ?: ('#' . $row['owner_id']),
|
||||
'owner_label'=> $row['owner_type'] === 'person' ? '人员' : '企业',
|
||||
'jump' => 'media.html?id=' . (int)$row['id'],
|
||||
];
|
||||
|
||||
case 'id_number':
|
||||
$stmt = $pdo->prepare("SELECT id, full_name FROM persons WHERE id_number = ? AND is_active = 1$excludeSql LIMIT 1");
|
||||
$stmt->execute(array_merge([$value], $excludeParams));
|
||||
$row = $stmt->fetch();
|
||||
if (!$row) {
|
||||
return null;
|
||||
}
|
||||
return [
|
||||
'label' => '身份证号码',
|
||||
'value' => $value,
|
||||
'table' => 'persons',
|
||||
'id' => (int)$row['id'],
|
||||
'name' => $row['full_name'] ?: ('#' . $row['id']),
|
||||
'jump' => 'person.html?id=' . (int)$row['id'],
|
||||
];
|
||||
|
||||
case 'registration_number':
|
||||
$stmt = $pdo->prepare("SELECT id, display_name FROM companies WHERE registration_number = ? AND is_active = 1$excludeSql LIMIT 1");
|
||||
$stmt->execute(array_merge([$value], $excludeParams));
|
||||
$row = $stmt->fetch();
|
||||
if (!$row) {
|
||||
return null;
|
||||
}
|
||||
return [
|
||||
'label' => '企业注册号',
|
||||
'value' => $value,
|
||||
'table' => 'companies',
|
||||
'id' => (int)$row['id'],
|
||||
'name' => $row['display_name'] ?: ('#' . $row['id']),
|
||||
'jump' => 'company.html?id=' . (int)$row['id'],
|
||||
];
|
||||
|
||||
case 'company_name':
|
||||
$stmt = $pdo->prepare("SELECT id, display_name FROM companies WHERE (name_zh = ? OR display_name = ?) AND is_active = 1$excludeSql LIMIT 1");
|
||||
$stmt->execute(array_merge([$value, $value], $excludeParams));
|
||||
$row = $stmt->fetch();
|
||||
if (!$row) {
|
||||
return null;
|
||||
}
|
||||
return [
|
||||
'label' => '企业名称',
|
||||
'value' => $value,
|
||||
'table' => 'companies',
|
||||
'id' => (int)$row['id'],
|
||||
'name' => $row['display_name'] ?: ('#' . $row['id']),
|
||||
'jump' => 'company.html?id=' . (int)$row['id'],
|
||||
];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验单条账号(媒体模块扁平字段)
|
||||
* @param PDO $pdo
|
||||
* @param string $platform
|
||||
* @param string $accountId
|
||||
* @param string $profileUrl
|
||||
* @param array $excludeIds
|
||||
* @return array|null
|
||||
*/
|
||||
function checkSingleAccountDuplicate(PDO $pdo, $platform, $accountId, $profileUrl = '', $excludeIds = [])
|
||||
{
|
||||
$platform = trim((string)$platform);
|
||||
$accountId = trim((string)$accountId);
|
||||
if ($platform !== '' && $accountId !== '') {
|
||||
if ($platform === 'phone' || $platform === 'email') {
|
||||
$dup = findDuplicate($pdo, $platform, $accountId, $excludeIds);
|
||||
} else {
|
||||
$dup = findDuplicate($pdo, 'account_id', $accountId, $excludeIds, $platform);
|
||||
}
|
||||
if ($dup) {
|
||||
return $dup;
|
||||
}
|
||||
}
|
||||
$profileUrl = trim((string)$profileUrl);
|
||||
if ($profileUrl !== '') {
|
||||
$dup = findDuplicate($pdo, 'profile_url', $profileUrl, $excludeIds);
|
||||
if ($dup) {
|
||||
return $dup;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验联系方式数组(persons/companies 的 contacts/accounts JSON)
|
||||
* @param PDO $pdo
|
||||
* @param array $accounts [{platform,account_id,profile_url,...}]
|
||||
* @param array $excludeIds 整表替换时排除自身既有账号ID
|
||||
* @return array|null
|
||||
*/
|
||||
function checkAccountsDuplicates(PDO $pdo, $accounts, $excludeIds = [])
|
||||
{
|
||||
if (!is_array($accounts)) {
|
||||
return null;
|
||||
}
|
||||
foreach ($accounts as $a) {
|
||||
if (!is_array($a)) {
|
||||
continue;
|
||||
}
|
||||
$dup = checkSingleAccountDuplicate(
|
||||
$pdo,
|
||||
$a['platform'] ?? '',
|
||||
$a['account_id'] ?? '',
|
||||
$a['profile_url'] ?? '',
|
||||
$excludeIds
|
||||
);
|
||||
if ($dup) {
|
||||
return $dup;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -25,6 +25,19 @@ class Response
|
||||
self::json(['code' => $code, 'msg' => $msg]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 重复数据错误(前端弹窗 + 跳转定位)
|
||||
* @param array $dup 重复记录信息:{label,value,name,owner_label,jump,...}
|
||||
*/
|
||||
public static function duplicate($dup)
|
||||
{
|
||||
self::json([
|
||||
'code' => 1001,
|
||||
'msg' => ($dup['label'] ?? '字段') . '「' . ($dup['value'] ?? '') . '」出现重复',
|
||||
'data' => $dup,
|
||||
]);
|
||||
}
|
||||
|
||||
private static function json($payload)
|
||||
{
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
|
||||
Reference in New Issue
Block a user