v1.0.38: 邮箱/国内手机号/身份证正则格式校验 - 后端validate.php(接入person add/update、duplicate_check账号校验、soft_update、hard_add/convert)前端common.js三函数+person/media/fragment表单提交校验
This commit is contained in:
@@ -10,6 +10,8 @@
|
|||||||
* - 企业名称 name_zh 或 display_name 任一相同即重复
|
* - 企业名称 name_zh 或 display_name 任一相同即重复
|
||||||
*/
|
*/
|
||||||
require_once __DIR__ . '/db.php';
|
require_once __DIR__ . '/db.php';
|
||||||
|
require_once __DIR__ . '/response.php';
|
||||||
|
require_once __DIR__ . '/validate.php';
|
||||||
|
|
||||||
/** 字段中文名 */
|
/** 字段中文名 */
|
||||||
const DUP_LABELS = [
|
const DUP_LABELS = [
|
||||||
@@ -191,6 +193,12 @@ function checkSingleAccountDuplicate(PDO $pdo, $platform, $accountId, $profileUr
|
|||||||
$platform = trim((string)$platform);
|
$platform = trim((string)$platform);
|
||||||
$accountId = trim((string)$accountId);
|
$accountId = trim((string)$accountId);
|
||||||
if ($platform !== '' && $accountId !== '') {
|
if ($platform !== '' && $accountId !== '') {
|
||||||
|
// 格式校验:手机/邮箱平台必须符合对应格式
|
||||||
|
if ($platform === 'phone') {
|
||||||
|
checkFieldFormat('phone', $accountId);
|
||||||
|
} elseif ($platform === 'email') {
|
||||||
|
checkFieldFormat('email', $accountId);
|
||||||
|
}
|
||||||
if ($platform === 'phone' || $platform === 'email') {
|
if ($platform === 'phone' || $platform === 'email') {
|
||||||
$dup = findDuplicate($pdo, $platform, $accountId, $excludeIds);
|
$dup = findDuplicate($pdo, $platform, $accountId, $excludeIds);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -0,0 +1,55 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* 格式校验(2026-08-09)
|
||||||
|
* 邮箱 / 国内手机号码 / 身份证号码 正则验证
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** 国内手机号码:11 位,1 开头,第二位 3-9 */
|
||||||
|
function validatePhoneCN($v)
|
||||||
|
{
|
||||||
|
return (bool)preg_match('/^1[3-9]\d{9}$/', trim((string)$v));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 邮箱 */
|
||||||
|
function validateEmail($v)
|
||||||
|
{
|
||||||
|
return (bool)preg_match('/^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$/', trim((string)$v));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 身份证号码:15 位 或 18 位(末位可为 X/x) */
|
||||||
|
function validateIdCard($v)
|
||||||
|
{
|
||||||
|
return (bool)preg_match('/^\d{15}$|^\d{17}[\dXx]$/', trim((string)$v));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 统一格式校验:不合法时直接返回错误响应
|
||||||
|
* @param string $type phone|email|id_number
|
||||||
|
* @param string $value
|
||||||
|
* @param bool $allowEmpty 空值是否放行(默认 true:未填不校验)
|
||||||
|
*/
|
||||||
|
function checkFieldFormat($type, $value, $allowEmpty = true)
|
||||||
|
{
|
||||||
|
$value = trim((string)$value);
|
||||||
|
if ($value === '') {
|
||||||
|
return $allowEmpty;
|
||||||
|
}
|
||||||
|
switch ($type) {
|
||||||
|
case 'phone':
|
||||||
|
if (!validatePhoneCN($value)) {
|
||||||
|
Response::error('手机号码格式不正确(应为国内 11 位手机号,如 13812345678)', 400);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'email':
|
||||||
|
if (!validateEmail($value)) {
|
||||||
|
Response::error('邮箱格式不正确', 400);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'id_number':
|
||||||
|
if (!validateIdCard($value)) {
|
||||||
|
Response::error('身份证号码格式不正确(应为 15 或 18 位)', 400);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
@@ -10,6 +10,7 @@ require_once __DIR__ . '/../common/db.php';
|
|||||||
require_once __DIR__ . '/../common/response.php';
|
require_once __DIR__ . '/../common/response.php';
|
||||||
require_once __DIR__ . '/../common/auth.php';
|
require_once __DIR__ . '/../common/auth.php';
|
||||||
require_once __DIR__ . '/../common/logger.php';
|
require_once __DIR__ . '/../common/logger.php';
|
||||||
|
require_once __DIR__ . '/../common/validate.php';
|
||||||
|
|
||||||
checkAjax();
|
checkAjax();
|
||||||
checkPermission('preliminary');
|
checkPermission('preliminary');
|
||||||
@@ -30,6 +31,10 @@ if ($sourceChannel === '') {
|
|||||||
$sourceChannel = '手动录入';
|
$sourceChannel = '手动录入';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 格式校验:手机 / 邮箱
|
||||||
|
checkFieldFormat('phone', $phone);
|
||||||
|
checkFieldFormat('email', $email);
|
||||||
|
|
||||||
// 至少有一项内容才能录为碎片
|
// 至少有一项内容才能录为碎片
|
||||||
if ($companyName === '' && $personName === '' && $phone === '' && $email === '' && $category === '' && $description === '') {
|
if ($companyName === '' && $personName === '' && $phone === '' && $email === '' && $category === '' && $description === '') {
|
||||||
Response::error('请至少填写一项内容', 400);
|
Response::error('请至少填写一项内容', 400);
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ require_once __DIR__ . '/../common/auth.php';
|
|||||||
require_once __DIR__ . '/../common/logger.php';
|
require_once __DIR__ . '/../common/logger.php';
|
||||||
require_once __DIR__ . '/../common/completeness.php';
|
require_once __DIR__ . '/../common/completeness.php';
|
||||||
require_once __DIR__ . '/../common/duplicate_check.php';
|
require_once __DIR__ . '/../common/duplicate_check.php';
|
||||||
|
require_once __DIR__ . '/../common/validate.php';
|
||||||
|
|
||||||
checkAjax();
|
checkAjax();
|
||||||
checkPermission('preliminary');
|
checkPermission('preliminary');
|
||||||
@@ -45,6 +46,10 @@ $sourceChannel = $frag['source_channel'] ?? null;
|
|||||||
|
|
||||||
$convertedId = 0;
|
$convertedId = 0;
|
||||||
|
|
||||||
|
// 格式校验:手机 / 邮箱
|
||||||
|
checkFieldFormat('phone', $phone);
|
||||||
|
checkFieldFormat('email', $email);
|
||||||
|
|
||||||
// 唯一性校验:按目标类型,收集全部重复
|
// 唯一性校验:按目标类型,收集全部重复
|
||||||
$dups = [];
|
$dups = [];
|
||||||
if ($targetType === 'company' && $companyName !== '') {
|
if ($targetType === 'company' && $companyName !== '') {
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ require_once __DIR__ . '/../common/logger.php';
|
|||||||
require_once __DIR__ . '/../common/helpers.php';
|
require_once __DIR__ . '/../common/helpers.php';
|
||||||
require_once __DIR__ . '/../common/completeness.php';
|
require_once __DIR__ . '/../common/completeness.php';
|
||||||
require_once __DIR__ . '/../common/duplicate_check.php';
|
require_once __DIR__ . '/../common/duplicate_check.php';
|
||||||
|
require_once __DIR__ . '/../common/validate.php';
|
||||||
|
|
||||||
checkAjax();
|
checkAjax();
|
||||||
checkPermission('preliminary');
|
checkPermission('preliminary');
|
||||||
@@ -57,6 +58,16 @@ switch ($table) {
|
|||||||
'graduated_from' => 's', 'hometown' => 's', 'work_location' => 's',
|
'graduated_from' => 's', 'hometown' => 's', 'work_location' => 's',
|
||||||
'id_type' => 's', 'id_number' => 's', 'source_channel' => 's', 'source_detail' => 's',
|
'id_type' => 's', 'id_number' => 's', 'source_channel' => 's', 'source_detail' => 's',
|
||||||
]);
|
]);
|
||||||
|
// 格式校验:身份证 / 手机 / 邮箱
|
||||||
|
if (!empty($fields['id_number'])) {
|
||||||
|
checkFieldFormat('id_number', $fields['id_number']);
|
||||||
|
}
|
||||||
|
if (isset($_POST['phone'])) {
|
||||||
|
checkFieldFormat('phone', $_POST['phone']);
|
||||||
|
}
|
||||||
|
if (isset($_POST['email'])) {
|
||||||
|
checkFieldFormat('email', $_POST['email']);
|
||||||
|
}
|
||||||
// 唯一性校验(排除自身):身份证 / 手机 / 邮箱
|
// 唯一性校验(排除自身):身份证 / 手机 / 邮箱
|
||||||
$dups = [];
|
$dups = [];
|
||||||
if (!empty($fields['id_number'])) {
|
if (!empty($fields['id_number'])) {
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ require_once __DIR__ . '/../common/logger.php';
|
|||||||
require_once __DIR__ . '/../common/helpers.php';
|
require_once __DIR__ . '/../common/helpers.php';
|
||||||
require_once __DIR__ . '/../common/completeness.php';
|
require_once __DIR__ . '/../common/completeness.php';
|
||||||
require_once __DIR__ . '/../common/duplicate_check.php';
|
require_once __DIR__ . '/../common/duplicate_check.php';
|
||||||
|
require_once __DIR__ . '/../common/validate.php';
|
||||||
|
|
||||||
checkAjax();
|
checkAjax();
|
||||||
checkPermission('person');
|
checkPermission('person');
|
||||||
@@ -35,6 +36,7 @@ if ((int)$chk->fetchColumn() > 0) {
|
|||||||
// 唯一性校验:身份证号码 / 联系方式(手机/邮箱/社媒账号/主页链接),收集全部重复
|
// 唯一性校验:身份证号码 / 联系方式(手机/邮箱/社媒账号/主页链接),收集全部重复
|
||||||
$dups = [];
|
$dups = [];
|
||||||
if (!empty($data['id_number'])) {
|
if (!empty($data['id_number'])) {
|
||||||
|
checkFieldFormat('id_number', $data['id_number']);
|
||||||
$dup = findDuplicate($pdo, 'id_number', $data['id_number']);
|
$dup = findDuplicate($pdo, 'id_number', $data['id_number']);
|
||||||
if ($dup) $dups[] = $dup;
|
if ($dup) $dups[] = $dup;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ require_once __DIR__ . '/../common/logger.php';
|
|||||||
require_once __DIR__ . '/../common/helpers.php';
|
require_once __DIR__ . '/../common/helpers.php';
|
||||||
require_once __DIR__ . '/../common/completeness.php';
|
require_once __DIR__ . '/../common/completeness.php';
|
||||||
require_once __DIR__ . '/../common/duplicate_check.php';
|
require_once __DIR__ . '/../common/duplicate_check.php';
|
||||||
|
require_once __DIR__ . '/../common/validate.php';
|
||||||
|
|
||||||
checkAjax();
|
checkAjax();
|
||||||
checkPermission('person');
|
checkPermission('person');
|
||||||
@@ -33,6 +34,7 @@ if (!$old) {
|
|||||||
// 唯一性校验(排除自身):身份证号码 / 联系方式,收集全部重复
|
// 唯一性校验(排除自身):身份证号码 / 联系方式,收集全部重复
|
||||||
$dups = [];
|
$dups = [];
|
||||||
if (!empty($data['id_number'])) {
|
if (!empty($data['id_number'])) {
|
||||||
|
checkFieldFormat('id_number', $data['id_number']);
|
||||||
$dup = findDuplicate($pdo, 'id_number', $data['id_number'], [$id]);
|
$dup = findDuplicate($pdo, 'id_number', $data['id_number'], [$id]);
|
||||||
if ($dup) $dups[] = $dup;
|
if ($dup) $dups[] = $dup;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -138,6 +138,11 @@ function cleanEmail(v) {
|
|||||||
return String(v == null ? '' : v).trim();
|
return String(v == null ? '' : v).trim();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 格式校验:邮箱 / 国内手机号 / 身份证 */
|
||||||
|
function validateEmail(v) { return /^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$/.test(String(v || '').trim()); }
|
||||||
|
function validatePhoneCN(v) { return /^1[3-9]\d{9}$/.test(String(v || '').trim()); }
|
||||||
|
function validateIdCard(v) { return /^\d{15}$|^\d{17}[\dXx]$/.test(String(v || '').trim()); }
|
||||||
|
|
||||||
/* ============ 会话与权限 ============ */
|
/* ============ 会话与权限 ============ */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
+1
-1
@@ -5,7 +5,7 @@ var BASE_URL = '/api/';
|
|||||||
var PAGE_SIZE = 20;
|
var PAGE_SIZE = 20;
|
||||||
|
|
||||||
/** 系统版本号(logo旁展示):修改代码后运行 tools/bump_version.php 自动递增 */
|
/** 系统版本号(logo旁展示):修改代码后运行 tools/bump_version.php 自动递增 */
|
||||||
var APP_VERSION = 'v1.0.37';
|
var APP_VERSION = 'v1.0.38';
|
||||||
|
|
||||||
/** 页脚版权/备案信息(在 config.js 中修改) */
|
/** 页脚版权/备案信息(在 config.js 中修改) */
|
||||||
var FOOTER_TEXT = '© 2026 SuperLink 管理系统 版权所有 | 备案号:请替换为真实备案号';
|
var FOOTER_TEXT = '© 2026 SuperLink 管理系统 版权所有 | 备案号:请替换为真实备案号';
|
||||||
|
|||||||
@@ -253,6 +253,9 @@ $(function () {
|
|||||||
if (name) fd[name] = $(this).val();
|
if (name) fd[name] = $(this).val();
|
||||||
});
|
});
|
||||||
fd.source_type = $('#ha-type').val();
|
fd.source_type = $('#ha-type').val();
|
||||||
|
// 格式校验:手机 / 邮箱
|
||||||
|
if (fd.contact_phone && !validatePhoneCN(fd.contact_phone)) { Dialog.error('手机号码格式不正确(应为国内 11 位手机号,如 13812345678)'); return; }
|
||||||
|
if (fd.contact_email && !validateEmail(fd.contact_email)) { Dialog.error('邮箱格式不正确'); return; }
|
||||||
httpPost('fragment/hard_add.php', fd).then(function () {
|
httpPost('fragment/hard_add.php', fd).then(function () {
|
||||||
Dialog.success('新增成功', function () {
|
Dialog.success('新增成功', function () {
|
||||||
Dialog.close(idx);
|
Dialog.close(idx);
|
||||||
@@ -344,6 +347,9 @@ $(function () {
|
|||||||
});
|
});
|
||||||
fd.id = id;
|
fd.id = id;
|
||||||
fd.target_type = $('#cv-type').val();
|
fd.target_type = $('#cv-type').val();
|
||||||
|
// 格式校验:手机 / 邮箱
|
||||||
|
if (fd.contact_phone && !validatePhoneCN(fd.contact_phone)) { Dialog.error('手机号码格式不正确(应为国内 11 位手机号,如 13812345678)'); return; }
|
||||||
|
if (fd.contact_email && !validateEmail(fd.contact_email)) { Dialog.error('邮箱格式不正确'); return; }
|
||||||
httpPost('fragment/hard_convert.php', fd).then(function () {
|
httpPost('fragment/hard_convert.php', fd).then(function () {
|
||||||
Dialog.success('转换成功', function () {
|
Dialog.success('转换成功', function () {
|
||||||
Dialog.close(idx);
|
Dialog.close(idx);
|
||||||
@@ -404,6 +410,10 @@ $(function () {
|
|||||||
var name = $(this).attr('name');
|
var name = $(this).attr('name');
|
||||||
if (name) fd[name] = $(this).val();
|
if (name) fd[name] = $(this).val();
|
||||||
});
|
});
|
||||||
|
// 格式校验:身份证 / 手机 / 邮箱
|
||||||
|
if (fd.id_number && !validateIdCard(fd.id_number)) { Dialog.error('身份证号码格式不正确(应为 15 或 18 位)'); return; }
|
||||||
|
if (fd.phone && !validatePhoneCN(fd.phone)) { Dialog.error('手机号码格式不正确(应为国内 11 位手机号,如 13812345678)'); return; }
|
||||||
|
if (fd.email && !validateEmail(fd.email)) { Dialog.error('邮箱格式不正确'); return; }
|
||||||
httpPost('fragment/soft_update.php', fd).then(function (res) {
|
httpPost('fragment/soft_update.php', fd).then(function (res) {
|
||||||
Dialog.success(res && res.is_complete ? '补全完成' : '已保存(整体完整度 ' + (res && res.overall) + '%)', function () {
|
Dialog.success(res && res.is_complete ? '补全完成' : '已保存(整体完整度 ' + (res && res.overall) + '%)', function () {
|
||||||
Dialog.close(idx);
|
Dialog.close(idx);
|
||||||
|
|||||||
@@ -203,6 +203,9 @@ $(function () {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (!data.platform || !data.account_id) { Dialog.error('平台和账号为必填项'); return; }
|
if (!data.platform || !data.account_id) { Dialog.error('平台和账号为必填项'); return; }
|
||||||
|
// 格式校验:平台为手机/邮箱时校验账号格式
|
||||||
|
if (data.platform === 'phone' && !validatePhoneCN(data.account_id)) { Dialog.error('手机号码格式不正确(应为国内 11 位手机号,如 13812345678)'); return; }
|
||||||
|
if (data.platform === 'email' && !validateEmail(data.account_id)) { Dialog.error('邮箱格式不正确'); return; }
|
||||||
if (!isEdit && (!data.owner_type || !data.owner_id)) { Dialog.error('归属类型和归属ID为必填项'); return; }
|
if (!isEdit && (!data.owner_type || !data.owner_id)) { Dialog.error('归属类型和归属ID为必填项'); return; }
|
||||||
// 录入清洗:手机号去所有空格(含中间),邮箱去首尾空格
|
// 录入清洗:手机号去所有空格(含中间),邮箱去首尾空格
|
||||||
if (data.platform === 'phone') data.account_id = cleanPhone(data.account_id);
|
if (data.platform === 'phone') data.account_id = cleanPhone(data.account_id);
|
||||||
|
|||||||
@@ -419,6 +419,11 @@ $(function () {
|
|||||||
});
|
});
|
||||||
if (!data.full_name) { Dialog.error('姓名为必填项'); return; }
|
if (!data.full_name) { Dialog.error('姓名为必填项'); return; }
|
||||||
|
|
||||||
|
// 格式校验:身份证 / 手机 / 邮箱
|
||||||
|
if (data.id_number && !validateIdCard(data.id_number)) { Dialog.error('身份证号码格式不正确(应为 15 或 18 位)'); return; }
|
||||||
|
if (data.phone && !validatePhoneCN(data.phone)) { Dialog.error('手机号码格式不正确(应为国内 11 位手机号,如 13812345678)'); return; }
|
||||||
|
if (data.email && !validateEmail(data.email)) { Dialog.error('邮箱格式不正确'); return; }
|
||||||
|
|
||||||
// 联系方式(手机/邮箱,均为常用)
|
// 联系方式(手机/邮箱,均为常用)
|
||||||
data.phone = cleanPhone(data.phone);
|
data.phone = cleanPhone(data.phone);
|
||||||
data.email = cleanEmail(data.email);
|
data.email = cleanEmail(data.email);
|
||||||
|
|||||||
Reference in New Issue
Block a user