v1.0.44: 继续补全不再直接转换 - 新增hard_update.php仅保存硬碎片字段(含extra身份证/平台/账号/主页,标准+格式+唯一性校验),前端继续补全弹窗改为保存表单,转换仅由转软碎片触发
This commit is contained in:
@@ -0,0 +1,116 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* 硬碎片补全保存接口 POST /api/fragment/hard_update.php
|
||||||
|
* 仅更新硬碎片字段(企业名/联系人/手机/邮箱/身份证/社媒账号/主页链接),不执行转换。
|
||||||
|
* 转换请走 hard_convert.php(前端「转软碎片」按钮)。
|
||||||
|
*/
|
||||||
|
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/validate.php';
|
||||||
|
require_once __DIR__ . '/../common/duplicate_check.php';
|
||||||
|
|
||||||
|
checkAjax();
|
||||||
|
checkPermission('preliminary');
|
||||||
|
|
||||||
|
$id = (int)($_POST['id'] ?? 0);
|
||||||
|
if ($id <= 0) {
|
||||||
|
Response::error('参数错误', 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
$pdo = DB::getInstance()->getPdo();
|
||||||
|
$stmt = $pdo->prepare("SELECT * FROM preliminary_data WHERE id = ? AND is_active = 1");
|
||||||
|
$stmt->execute([$id]);
|
||||||
|
$frag = $stmt->fetch();
|
||||||
|
if (!$frag) {
|
||||||
|
Response::error('碎片不存在');
|
||||||
|
}
|
||||||
|
if (in_array($frag['status'], ['已转换', '已废弃'], true)) {
|
||||||
|
Response::error('该碎片已处理(' . $frag['status'] . ')');
|
||||||
|
}
|
||||||
|
|
||||||
|
$sourceType = trim($_POST['source_type'] ?? ($frag['source_type'] ?? ''));
|
||||||
|
$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'] ?? '');
|
||||||
|
if (!in_array($sourceType, ['company', 'person', 'product', 'need', 'mixed'], true)) {
|
||||||
|
Response::error('碎片类型不合法', 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 硬数据标准校验:企业/产品/需求 → 企业名称必填;人员 → 手机、邮箱至少一项
|
||||||
|
if (in_array($sourceType, ['company', 'product', 'need'], true) && $companyName === '') {
|
||||||
|
Response::error('企业类型硬数据:企业名称为必填项', 400);
|
||||||
|
}
|
||||||
|
if ($sourceType === 'person' && $phone === '' && $email === '') {
|
||||||
|
Response::error('人员类型硬数据:手机号码、邮箱至少填写一项', 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 格式校验
|
||||||
|
checkFieldFormat('phone', $phone);
|
||||||
|
checkFieldFormat('email', $email);
|
||||||
|
checkFieldFormat('id_number', $idNumber);
|
||||||
|
if ($profileUrl !== '' && !preg_match('/^https?:\/\//i', $profileUrl)) {
|
||||||
|
Response::error('主页链接格式不正确(应以 http:// 或 https:// 开头)', 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 唯一性校验(收集全部重复)
|
||||||
|
$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);
|
||||||
|
|
||||||
|
// extra_info 合并更新(身份证/平台/账号ID/主页链接)
|
||||||
|
$extra = json_decode($frag['extra_info'] ?? 'null', true);
|
||||||
|
if (!is_array($extra)) {
|
||||||
|
$extra = [];
|
||||||
|
}
|
||||||
|
if ($idNumber !== '') $extra['id_number'] = $idNumber; else unset($extra['id_number']);
|
||||||
|
if ($platform !== '') $extra['platform'] = $platform; else unset($extra['platform']);
|
||||||
|
if ($accountId !== '') $extra['account_id'] = $accountId; else unset($extra['account_id']);
|
||||||
|
if ($profileUrl !== '') $extra['profile_url'] = $profileUrl; else unset($extra['profile_url']);
|
||||||
|
|
||||||
|
$pdo->prepare(
|
||||||
|
"UPDATE preliminary_data
|
||||||
|
SET source_type = ?, company_name = ?, person_name = ?, contact_phone = ?, contact_email = ?, extra_info = ?
|
||||||
|
WHERE id = ?"
|
||||||
|
)->execute([
|
||||||
|
$sourceType,
|
||||||
|
$companyName !== '' ? $companyName : null,
|
||||||
|
$personName !== '' ? $personName : null,
|
||||||
|
$phone !== '' ? $phone : null,
|
||||||
|
$email !== '' ? $email : null,
|
||||||
|
$extra ? json_encode($extra, JSON_UNESCAPED_UNICODE) : null,
|
||||||
|
$id,
|
||||||
|
]);
|
||||||
|
|
||||||
|
logCurrent('update', 'fragment', 'preliminary_data', $id, [
|
||||||
|
'source_type' => $sourceType,
|
||||||
|
'company_name' => $companyName,
|
||||||
|
'person_name' => $personName,
|
||||||
|
'phone' => $phone,
|
||||||
|
'email' => $email,
|
||||||
|
'extra' => $extra,
|
||||||
|
]);
|
||||||
|
|
||||||
|
Response::success(null, '已保存,点击「转软碎片」完成转换');
|
||||||
+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.43';
|
var APP_VERSION = 'v1.0.44';
|
||||||
|
|
||||||
/** 页脚版权/备案信息(在 config.js 中修改) */
|
/** 页脚版权/备案信息(在 config.js 中修改) */
|
||||||
var FOOTER_TEXT = '© 2026 SuperLink 管理系统 版权所有 | 备案号:请替换为真实备案号';
|
var FOOTER_TEXT = '© 2026 SuperLink 管理系统 版权所有 | 备案号:请替换为真实备案号';
|
||||||
|
|||||||
+27
-22
@@ -298,44 +298,47 @@ $(function () {
|
|||||||
/* ================= 硬碎片:补全录入(转换) ================= */
|
/* ================= 硬碎片:补全录入(转换) ================= */
|
||||||
window.convertHard = function (id) {
|
window.convertHard = function (id) {
|
||||||
httpGet('fragment/hard_detail.php', { id: id }).then(function (d) {
|
httpGet('fragment/hard_detail.php', { id: id }).then(function (d) {
|
||||||
|
var ex = d.extra_info || {};
|
||||||
|
if (typeof ex === 'string') { try { ex = JSON.parse(ex); } catch (e) { ex = {}; } }
|
||||||
var typeSel = '<select id="cv-type">' +
|
var typeSel = '<select id="cv-type">' +
|
||||||
'<option value="company"' + (d.source_type === 'company' || d.source_type === 'mixed' ? ' selected' : '') + '>企业</option>' +
|
'<option value="company"' + (d.source_type === 'company' ? ' selected' : '') + '>企业</option>' +
|
||||||
'<option value="person"' + (d.source_type === 'person' ? ' selected' : '') + '>人员</option>' +
|
'<option value="person"' + (d.source_type === 'person' ? ' selected' : '') + '>人员</option>' +
|
||||||
'<option value="product"' + (d.source_type === 'product' ? ' selected' : '') + '>产品</option>' +
|
'<option value="product"' + (d.source_type === 'product' ? ' selected' : '') + '>产品</option>' +
|
||||||
'<option value="need"' + (d.source_type === 'need' ? ' selected' : '') + '>需求</option></select>';
|
'<option value="need"' + (d.source_type === 'need' ? ' selected' : '') + '>需求</option>' +
|
||||||
|
'<option value="mixed"' + (d.source_type === 'mixed' ? ' selected' : '') + '>混合</option></select>';
|
||||||
var content =
|
var content =
|
||||||
'<form id="cv-form" style="padding:16px 22px 4px;">' +
|
'<form id="cv-form" style="padding:16px 22px 4px;">' +
|
||||||
'<div class="form-grid">' +
|
'<div class="form-grid">' +
|
||||||
' <div class="form-item"><label>转换目标<span class="req">*</span></label>' + typeSel + '</div>' +
|
' <div class="form-item"><label>碎片类型<span class="req">*</span></label>' + typeSel + '</div>' +
|
||||||
' <div class="form-item" id="cv-f-company_name"><label>公司名称</label><input type="text" name="company_name" value="' + escHtml(d.company_name || '') + '"></div>' +
|
' <div class="form-item" id="cv-f-company_name"><label>公司名称</label><input type="text" name="company_name" value="' + escHtml(d.company_name || '') + '"></div>' +
|
||||||
' <div class="form-item" id="cv-f-person_name"><label>联系人</label><input type="text" name="person_name" value="' + escHtml(d.person_name || '') + '"></div>' +
|
' <div class="form-item" id="cv-f-person_name"><label>联系人</label><input type="text" name="person_name" value="' + escHtml(d.person_name || '') + '"></div>' +
|
||||||
' <div class="form-item"><label>联系电话</label><input type="text" name="contact_phone" value="' + escHtml(d.contact_phone || '') + '"></div>' +
|
' <div class="form-item"><label>联系电话</label><input type="text" name="contact_phone" value="' + escHtml(d.contact_phone || '') + '"></div>' +
|
||||||
' <div class="form-item"><label>联系邮箱</label><input type="text" name="contact_email" value="' + escHtml(d.contact_email || '') + '"></div>' +
|
' <div class="form-item"><label>联系邮箱</label><input type="text" name="contact_email" value="' + escHtml(d.contact_email || '') + '"></div>' +
|
||||||
' <div class="form-item" id="cv-f-category"><label>产品品类</label><input type="text" name="target_product_category" value="' + escHtml(d.target_product_category || '') + '"></div>' +
|
' <div class="form-item"><label>身份证号码</label><input type="text" name="id_number" value="' + escHtml(ex.id_number || '') + '" placeholder="15 或 18 位"></div>' +
|
||||||
' <div class="form-item" id="cv-f-industry"><label>行业</label><input type="text" name="industry" placeholder="转换为企业时填写"></div>' +
|
' <div class="form-item"><label>社媒平台</label><select name="platform" id="cv-platform"><option value="">-</option>' +
|
||||||
' <div class="form-item" id="cv-f-needcat"><label>需求类别</label><input type="text" name="need_category" placeholder="如:设备采购"></div>' +
|
' <option value="wechat">微信</option><option value="weibo">微博</option><option value="douyin">抖音</option>' +
|
||||||
' <div class="form-item" id="cv-f-scenario"><label>应用场景</label><input type="text" name="application_scenario" placeholder="如:码垛"></div>' +
|
' <option value="bilibili">B站</option><option value="linkedin">领英</option><option value="xiaohongshu">小红书</option>' +
|
||||||
' <div class="form-item full"><label>描述/补充说明</label><textarea name="description" style="height:60px;">' + escHtml(d.description || '') + '</textarea></div>' +
|
' <option value="other">其他</option></select></div>' +
|
||||||
|
' <div class="form-item"><label>账号ID</label><input type="text" name="account_id" value="' + escHtml(ex.account_id || '') + '" placeholder="社媒账号ID"></div>' +
|
||||||
|
' <div class="form-item"><label>主页链接</label><input type="text" name="profile_url" value="' + escHtml(ex.profile_url || '') + '" placeholder="https://..."></div>' +
|
||||||
'</div>' +
|
'</div>' +
|
||||||
|
'<div style="font-size:12px;color:#8a94a6;margin-top:4px;">补全仅保存到硬数据,点击「转软碎片」后才转换为主表数据。</div>' +
|
||||||
'<div class="dialog-footer"><button type="button" class="btn" id="cv-cancel">取消</button>' +
|
'<div class="dialog-footer"><button type="button" class="btn" id="cv-cancel">取消</button>' +
|
||||||
'<button type="submit" class="btn btn-primary">确认转换</button></div>' +
|
'<button type="submit" class="btn btn-primary">保存补全</button></div>' +
|
||||||
'</form>';
|
'</form>';
|
||||||
|
|
||||||
var idx = Dialog.open({
|
var idx = Dialog.open({
|
||||||
title: '硬碎片补全录入',
|
title: '硬数据继续补全',
|
||||||
area: ['640px', 'auto'],
|
area: ['640px', 'auto'],
|
||||||
content: content,
|
content: content,
|
||||||
btn: false
|
btn: false
|
||||||
});
|
});
|
||||||
|
$('#cv-platform').val(ex.platform || '');
|
||||||
|
|
||||||
function syncFields() {
|
function syncFields() {
|
||||||
var t = $('#cv-type').val();
|
var t = $('#cv-type').val();
|
||||||
$('#cv-f-company_name').toggle(t !== 'person');
|
$('#cv-f-company_name').toggle(t !== 'person');
|
||||||
$('#cv-f-person_name').toggle(t === 'person');
|
$('#cv-f-person_name').toggle(t === 'person' || t === 'mixed');
|
||||||
$('#cv-f-category').toggle(t === 'product' || t === 'need');
|
|
||||||
$('#cv-f-industry').toggle(t === 'company');
|
|
||||||
$('#cv-f-needcat').toggle(t === 'need');
|
|
||||||
$('#cv-f-scenario').toggle(t === 'need');
|
|
||||||
}
|
}
|
||||||
$('#cv-type').on('change', syncFields);
|
$('#cv-type').on('change', syncFields);
|
||||||
syncFields();
|
syncFields();
|
||||||
@@ -344,21 +347,23 @@ $(function () {
|
|||||||
$('#cv-form').on('submit', function (e) {
|
$('#cv-form').on('submit', function (e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
var fd = {};
|
var fd = {};
|
||||||
$(this).find('input, select, textarea').each(function () {
|
$(this).find('input, select').each(function () {
|
||||||
var name = $(this).attr('name');
|
var name = $(this).attr('name');
|
||||||
if (name) fd[name] = $(this).val();
|
if (name) fd[name] = $(this).val();
|
||||||
});
|
});
|
||||||
fd.id = id;
|
fd.id = id;
|
||||||
fd.target_type = $('#cv-type').val();
|
// 类型标准校验
|
||||||
// 格式校验:手机 / 邮箱
|
if (fd.source_type !== 'person' && fd.source_type !== 'mixed' && !$.trim(fd.company_name)) { Dialog.error('企业类型硬数据:企业名称为必填项'); return; }
|
||||||
|
if (fd.source_type === 'person' && !$.trim(fd.contact_phone) && !$.trim(fd.contact_email)) { Dialog.error('人员类型硬数据:手机号码、邮箱至少填写一项'); return; }
|
||||||
|
// 格式校验:手机 / 邮箱 / 身份证 / 主页链接
|
||||||
if (fd.contact_phone && !validatePhoneCN(fd.contact_phone)) { Dialog.error('手机号码格式不正确(应为国内 11 位手机号,如 13812345678)'); return; }
|
if (fd.contact_phone && !validatePhoneCN(fd.contact_phone)) { Dialog.error('手机号码格式不正确(应为国内 11 位手机号,如 13812345678)'); return; }
|
||||||
if (fd.contact_email && !validateEmail(fd.contact_email)) { Dialog.error('邮箱格式不正确'); return; }
|
if (fd.contact_email && !validateEmail(fd.contact_email)) { Dialog.error('邮箱格式不正确'); return; }
|
||||||
httpPost('fragment/hard_convert.php', fd).then(function () {
|
if (fd.id_number && !validateIdCard(fd.id_number)) { Dialog.error('身份证号码格式不正确(应为 15 或 18 位)'); return; }
|
||||||
Dialog.success('转换成功', function () {
|
if (fd.profile_url && !/^https?:\/\//.test(fd.profile_url)) { Dialog.error('主页链接格式不正确(应以 http:// 或 https:// 开头)'); return; }
|
||||||
|
httpPost('fragment/hard_update.php', fd).then(function () {
|
||||||
|
Dialog.success('已保存,点击「转软碎片」完成转换', function () {
|
||||||
Dialog.close(idx);
|
Dialog.close(idx);
|
||||||
loadStats();
|
|
||||||
loadHardList(hardPage);
|
loadHardList(hardPage);
|
||||||
loadSoftList(1);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user