v1.0.51: 批次1架构筑基 - 1)版本迁移(common/migrate.php+schema_versions+natsort迁移文件);2)登录安全(bcrypt平滑迁移login/user_add/user_update旧sha1命中自动重写+system_login_attempts限流+session加固httponly/samesite/secure);3)CSRF Token(服务端checkCsrf+auth/csrf.php登录页取token+前端common.js/login.js统一携带X-CSRF-Token);4)统一基座common/Api.php并存量强制迁移全部70个endpoint(Api::boot按public/super/module/permissions分流,写接口强制checkAjax+checkCsrf,全局异常处理);5)前端收敛(common.js新增esc转义别名);6)REV-4硬数据来源渠道可编辑(hard_update+补全弹窗下拉);7)prepared卫生(soft_update.php修复+analysis.php表名白名单REV-9)
Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
/**
|
||||
* 获取 CSRF Token GET /api/auth/csrf.php
|
||||
* 供登录页等未登录场景在发起 POST 前先取会话绑定的 Token。
|
||||
* 登录后 session.php 会随用户信息一并返回 csrf_token(common.js 自动缓存)。
|
||||
*/
|
||||
require_once __DIR__ . '/../common/Api.php';
|
||||
$pdo = Api::boot(['public' => true]);
|
||||
|
||||
Response::success(['csrf_token' => csrfToken()]);
|
||||
+3
-6
@@ -3,14 +3,11 @@
|
||||
* 找回密码接口 POST /api/auth/forgot.php
|
||||
* 入参:username / contact_email
|
||||
* 说明:占位实现 —— 校验账号存在后返回成功(后续可接入 SMTP 发送重置邮件/短信验证码)。
|
||||
* 批次1:经 Api::boot(['public' => true]) 免登录,但写请求仍校验 Ajax + CSRF。
|
||||
*/
|
||||
require_once __DIR__ . '/../common/db.php';
|
||||
require_once __DIR__ . '/../common/response.php';
|
||||
require_once __DIR__ . '/../common/logger.php';
|
||||
require_once __DIR__ . '/../common/Api.php';
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||
Response::error('请求方式错误', 400);
|
||||
}
|
||||
Api::boot(['public' => true]);
|
||||
|
||||
$username = trim($_POST['username'] ?? '');
|
||||
$contactEmail = trim($_POST['contact_email'] ?? '');
|
||||
|
||||
+32
-13
@@ -3,18 +3,21 @@
|
||||
* 登录接口 POST /api/auth/login.php
|
||||
* 入参:username / password / slider_token(滑块验证通过后的token)
|
||||
* 出参(成功):{ code:0, msg:'登录成功', data:{ real_name, role_name, permissions } }
|
||||
*
|
||||
* 批次1 安全加固(REV-1/REV-8):
|
||||
* - 密码 bcrypt(password_hash)优先校验;存量 sha1 用户登录命中后平滑升级
|
||||
* - 登录限流(15分钟内同 IP+账号失败≥5 次锁定)
|
||||
* - 登录成功后 session_regenerate_id 防会话固定
|
||||
* - 经 Api::boot 统一走 CSRF + 安全 Session
|
||||
*/
|
||||
require_once __DIR__ . '/../common/db.php';
|
||||
require_once __DIR__ . '/../common/response.php';
|
||||
require_once __DIR__ . '/../common/logger.php';
|
||||
require_once __DIR__ . '/../common/Api.php';
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||
Response::error('请求方式错误', 400);
|
||||
}
|
||||
$pdo = Api::boot(['public' => true]);
|
||||
|
||||
$username = trim($_POST['username'] ?? '');
|
||||
$password = (string)($_POST['password'] ?? '');
|
||||
$sliderToken = trim($_POST['slider_token'] ?? '');
|
||||
$ip = $_SERVER['REMOTE_ADDR'] ?? '0.0.0.0';
|
||||
|
||||
// 滑块验证:前端 slider.js 拖动通过后生成的 token,服务端校验非空且格式合法
|
||||
if ($sliderToken === '' || strlen($sliderToken) < 10) {
|
||||
@@ -24,7 +27,9 @@ if ($username === '' || $password === '') {
|
||||
Response::error('请输入账号和密码');
|
||||
}
|
||||
|
||||
$pdo = DB::getInstance()->getPdo();
|
||||
// 登录限流(fail-open)
|
||||
checkLoginThrottle($username, $ip);
|
||||
|
||||
$stmt = $pdo->prepare(
|
||||
"SELECT u.*, r.role_name, r.permissions
|
||||
FROM system_users u
|
||||
@@ -34,22 +39,35 @@ $stmt = $pdo->prepare(
|
||||
$stmt->execute([$username]);
|
||||
$user = $stmt->fetch();
|
||||
|
||||
if (!$user || !hash_equals($user['password'], sha1($password))) {
|
||||
// 密码校验:bcrypt 优先;旧版无盐 sha1 校验通过后即时平滑升级为 bcrypt
|
||||
$authOk = false;
|
||||
if ($user) {
|
||||
if (password_verify($password, (string)$user['password'])) {
|
||||
$authOk = true;
|
||||
} elseif (hash_equals((string)$user['password'], sha1($password))) {
|
||||
$authOk = true;
|
||||
$pdo->prepare("UPDATE system_users SET password = ? WHERE id = ?")
|
||||
->execute([password_hash($password, PASSWORD_DEFAULT), (int)$user['id']]);
|
||||
}
|
||||
}
|
||||
if (!$authOk) {
|
||||
recordLoginAttempt($username, $ip, false);
|
||||
Response::error('账号或密码错误');
|
||||
}
|
||||
if ((int)$user['is_active'] !== 1) {
|
||||
recordLoginAttempt($username, $ip, false);
|
||||
Response::error('账号已被禁用,请联系管理员');
|
||||
}
|
||||
|
||||
recordLoginAttempt($username, $ip, true);
|
||||
|
||||
// 更新最后登录时间/IP
|
||||
$ip = $_SERVER['REMOTE_ADDR'] ?? '0.0.0.0';
|
||||
$upd = $pdo->prepare("UPDATE system_users SET last_login_time = NOW(), last_login_ip = ? WHERE id = ?");
|
||||
$upd->execute([$ip, $user['id']]);
|
||||
|
||||
// 写入 Session
|
||||
if (session_status() === PHP_SESSION_NONE) {
|
||||
session_start();
|
||||
}
|
||||
// 会话固定防护:登录成功后重新生成 session id(保留已有 CSRF Token)
|
||||
session_regenerate_id(true);
|
||||
|
||||
$_SESSION['user_id'] = (int)$user['id'];
|
||||
$_SESSION['username'] = $user['username'];
|
||||
$_SESSION['real_name'] = $user['real_name'] ?? $user['username'];
|
||||
@@ -64,4 +82,5 @@ Response::success([
|
||||
'real_name' => $_SESSION['real_name'],
|
||||
'role_name' => $_SESSION['role_name'],
|
||||
'permissions' => $_SESSION['permissions'],
|
||||
'csrf_token' => csrfToken(),
|
||||
], '登录成功');
|
||||
|
||||
+3
-7
@@ -1,15 +1,11 @@
|
||||
<?php
|
||||
/**
|
||||
* 登出接口 POST /api/auth/logout.php
|
||||
* 批次1 起经 Api::boot 统一校验登录态 + Ajax + CSRF。
|
||||
*/
|
||||
require_once __DIR__ . '/../common/db.php';
|
||||
require_once __DIR__ . '/../common/response.php';
|
||||
require_once __DIR__ . '/../common/logger.php';
|
||||
require_once __DIR__ . '/../common/auth.php';
|
||||
require_once __DIR__ . '/../common/Api.php';
|
||||
|
||||
if (session_status() === PHP_SESSION_NONE) {
|
||||
session_start();
|
||||
}
|
||||
Api::boot();
|
||||
|
||||
if (!empty($_SESSION['user_id'])) {
|
||||
logAction((int)$_SESSION['user_id'], $_SESSION['username'] ?? '', 'logout', 'auth', 'system_users', (int)$_SESSION['user_id']);
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
<?php
|
||||
/**
|
||||
* 会话校验接口 GET /api/auth/session.php
|
||||
* 前端页面加载时调用:未登录返回 401(前端跳转登录页),已登录返回当前用户信息。
|
||||
* 前端页面加载时调用:未登录返回 401(前端跳转登录页),已登录返回当前用户信息 + CSRF Token。
|
||||
*/
|
||||
require_once __DIR__ . '/../common/response.php';
|
||||
require_once __DIR__ . '/../common/auth.php';
|
||||
require_once __DIR__ . '/../common/Api.php';
|
||||
|
||||
requireLogin();
|
||||
Api::boot(); // 仅要求登录
|
||||
|
||||
$pdo = DB::getInstance()->getPdo();
|
||||
$stmt = $pdo->prepare("SELECT last_login_time, last_login_ip FROM system_users WHERE id = ?");
|
||||
@@ -19,6 +18,7 @@ Response::success([
|
||||
'real_name' => $_SESSION['real_name'],
|
||||
'role_name' => $_SESSION['role_name'],
|
||||
'permissions' => $_SESSION['permissions'],
|
||||
'csrf_token' => csrfToken(),
|
||||
'last_login_time' => $loginInfo['last_login_time'] ?? null,
|
||||
'last_login_ip' => $loginInfo['last_login_ip'] ?? null,
|
||||
]);
|
||||
|
||||
@@ -7,11 +7,9 @@
|
||||
* - year 为空 = 全部年份;否则按创建年份过滤
|
||||
* 返回:{ years, company: [{channel, count, percent, details:[{source,count}]}], person: [...] }
|
||||
*/
|
||||
require_once __DIR__ . '/../common/db.php';
|
||||
require_once __DIR__ . '/../common/response.php';
|
||||
require_once __DIR__ . '/../common/auth.php';
|
||||
require_once __DIR__ . '/../common/Api.php';
|
||||
|
||||
checkPermission('channel');
|
||||
Api::boot(['module' => 'channel']);
|
||||
|
||||
$year = trim($_REQUEST['year'] ?? '');
|
||||
if ($year !== '' && (!ctype_digit($year) || (int)$year < 2000 || (int)$year > 2100)) {
|
||||
@@ -32,6 +30,10 @@ $years = $pdo->query(
|
||||
/** 单维度渠道分析 */
|
||||
function channelAnalysis($pdo, $table, $year)
|
||||
{
|
||||
// REV-9:表名白名单,杜绝 SQL 插值
|
||||
if (!in_array($table, ['companies', 'persons'], true)) {
|
||||
Response::error('参数错误', 400);
|
||||
}
|
||||
$where = 'is_active = 1 AND source_channel IS NOT NULL AND source_channel <> \'\'';
|
||||
$params = [];
|
||||
if ($year !== '') {
|
||||
|
||||
@@ -3,13 +3,9 @@
|
||||
* 渠道新增计划删除接口 POST /api/channel/plan_delete.php
|
||||
* 入参:id(必填);软删除(is_active = 0)
|
||||
*/
|
||||
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/Api.php';
|
||||
$pdo = Api::boot(['module' => 'channel']);
|
||||
|
||||
checkAjax();
|
||||
checkPermission('channel');
|
||||
|
||||
$id = (int)($_POST['id'] ?? 0);
|
||||
if ($id <= 0) {
|
||||
|
||||
@@ -5,11 +5,9 @@
|
||||
* 参数:page / limit / channel_type / status / keyword
|
||||
* 返回:{ list, total, page, limit }(list 含 remaining_days 剩余天数)
|
||||
*/
|
||||
require_once __DIR__ . '/../common/db.php';
|
||||
require_once __DIR__ . '/../common/response.php';
|
||||
require_once __DIR__ . '/../common/auth.php';
|
||||
require_once __DIR__ . '/../common/Api.php';
|
||||
$pdo = Api::boot(['module' => 'channel']);
|
||||
|
||||
checkPermission('channel');
|
||||
|
||||
[$page, $limit] = pageParams();
|
||||
$channelType = trim($_REQUEST['channel_type'] ?? '');
|
||||
|
||||
@@ -5,14 +5,9 @@
|
||||
* source_detail / occurrence_address(发生地址)/ industry / start_date / end_date / remark / status(待启动|已执行|错过)/ cost(费用(元))
|
||||
* 说明:编辑(传 id)时仅更新提交的字段,便于列表行内实时改状态/费用。
|
||||
*/
|
||||
require_once __DIR__ . '/../common/db.php';
|
||||
require_once __DIR__ . '/../common/response.php';
|
||||
require_once __DIR__ . '/../common/auth.php';
|
||||
require_once __DIR__ . '/../common/helpers.php';
|
||||
require_once __DIR__ . '/../common/logger.php';
|
||||
require_once __DIR__ . '/../common/Api.php';
|
||||
$pdo = Api::boot(['module' => 'channel']);
|
||||
|
||||
checkAjax();
|
||||
checkPermission('channel');
|
||||
|
||||
$id = (int)($_POST['id'] ?? 0);
|
||||
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
<?php
|
||||
/**
|
||||
* 统一接口基座 v1(批次1「架构筑基」REV-ARCH-2)
|
||||
*
|
||||
* 所有业务接口统一走 Api::boot(),收敛「require 一串公共文件 + checkAjax +
|
||||
* checkPermission + DB 连接」的重复样板,并提供统一的异常→JSON 兜底。
|
||||
*
|
||||
* 用法(endpoint 顶部):
|
||||
* require_once __DIR__ . '/../common/Api.php';
|
||||
* $pdo = Api::boot(['module' => 'preliminary']); // 登录 + 模块权限(写接口另含 Ajax/CSRF)
|
||||
* $pdo = Api::boot(); // 仅要求登录(如 session.php)
|
||||
* $pdo = Api::boot(['super' => true]); // 仅超级管理员
|
||||
* $pdo = Api::boot(['permissions' => [...]]); // 任一权限
|
||||
* $pdo = Api::boot(['public' => true]); // 免登录(仅 login/forgot/csrf)
|
||||
*
|
||||
* boot() 统一执行:
|
||||
* - 安全 Session(httponly/samesite)
|
||||
* - POST/PUT/PATCH/DELETE:checkAjax(X-Requested-With)+ checkCsrf(Token 头)
|
||||
* - 登录态 + 权限
|
||||
* - 返回 PDO(后续代码可直接用 $pdo;原 `$pdo = DB::getInstance()->getPdo();`
|
||||
* 属冗余无害,可保留)
|
||||
*
|
||||
* 本文件另注册全局异常处理器:未捕获异常统一转 JSON 500 + 技术日志。
|
||||
*/
|
||||
require_once __DIR__ . '/db.php';
|
||||
require_once __DIR__ . '/response.php';
|
||||
require_once __DIR__ . '/logger.php';
|
||||
require_once __DIR__ . '/security.php';
|
||||
require_once __DIR__ . '/auth.php';
|
||||
require_once __DIR__ . '/helpers.php';
|
||||
require_once __DIR__ . '/validate.php';
|
||||
require_once __DIR__ . '/duplicate_check.php';
|
||||
require_once __DIR__ . '/completeness.php';
|
||||
|
||||
/** 统一异常处理:未捕获异常 → JSON 500 + 技术日志(REV-ARCH-3) */
|
||||
set_exception_handler(function (\Throwable $e) {
|
||||
logError('未捕获异常', [
|
||||
'msg' => $e->getMessage(),
|
||||
'file' => $e->getFile(),
|
||||
'line' => $e->getLine(),
|
||||
'uri' => $_SERVER['REQUEST_URI'] ?? '',
|
||||
]);
|
||||
if (!headers_sent()) {
|
||||
http_response_code(500);
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
}
|
||||
echo json_encode(['code' => 500, 'msg' => '服务器内部错误,请稍后重试'], JSON_UNESCAPED_UNICODE);
|
||||
exit;
|
||||
});
|
||||
|
||||
class Api
|
||||
{
|
||||
/**
|
||||
* 统一引导
|
||||
* @param array $opts 见文件头注释
|
||||
* @return PDO
|
||||
*/
|
||||
public static function boot(array $opts = [])
|
||||
{
|
||||
startSessionSecure();
|
||||
|
||||
$isWrite = in_array($_SERVER['REQUEST_METHOD'] ?? 'GET', ['POST', 'PUT', 'PATCH', 'DELETE'], true);
|
||||
if ($isWrite) {
|
||||
checkAjax();
|
||||
}
|
||||
|
||||
if (empty($opts['public'])) {
|
||||
if (!empty($opts['super'])) {
|
||||
requireSuperAdmin();
|
||||
} elseif (!empty($opts['module'])) {
|
||||
checkPermission($opts['module']);
|
||||
} elseif (!empty($opts['permissions'])) {
|
||||
checkAnyPermission($opts['permissions']);
|
||||
} else {
|
||||
requireLogin();
|
||||
}
|
||||
}
|
||||
|
||||
if ($isWrite) {
|
||||
checkCsrf();
|
||||
}
|
||||
|
||||
return DB::getInstance()->getPdo();
|
||||
}
|
||||
}
|
||||
+6
-4
@@ -6,13 +6,15 @@
|
||||
*/
|
||||
require_once __DIR__ . '/db.php';
|
||||
require_once __DIR__ . '/response.php';
|
||||
require_once __DIR__ . '/security.php';
|
||||
|
||||
/** 启动 Session */
|
||||
/**
|
||||
* 启动 Session(批次1 起经 security.php 加固:httponly + samesite=Lax + HTTPS secure)
|
||||
* 兼容既有调用方式,行为不变,仅 cookie 属性增强(REV-SEC-4/REV-8)
|
||||
*/
|
||||
function startSession()
|
||||
{
|
||||
if (session_status() === PHP_SESSION_NONE) {
|
||||
session_start();
|
||||
}
|
||||
startSessionSecure();
|
||||
}
|
||||
|
||||
/** 校验写类接口的 Ajax 头 */
|
||||
|
||||
@@ -5,11 +5,9 @@
|
||||
* 返回各字典表启用中的数据,供前端下拉/联动使用。
|
||||
* 数据来源:官方 Excel 生成的种子数据(见 sql/dict_seed.sql),不依赖任何第三方接口。
|
||||
*/
|
||||
require_once __DIR__ . '/db.php';
|
||||
require_once __DIR__ . '/response.php';
|
||||
require_once __DIR__ . '/auth.php';
|
||||
require_once __DIR__ . '/Api.php';
|
||||
$pdo = Api::boot();
|
||||
|
||||
requireLogin();
|
||||
|
||||
$type = trim($_REQUEST['type'] ?? 'all');
|
||||
$pdo = DB::getInstance()->getPdo();
|
||||
|
||||
@@ -55,3 +55,21 @@ function logCurrent($action, $module, $targetTable = null, $targetId = null, $co
|
||||
$content
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 技术异常日志(批次1 REV-ARCH-3):写入 runtime/logs/error-YYYYMM.log
|
||||
* 业务审计走 system_logs(logAction/logCurrent),技术异常走此文件。
|
||||
* @param string $message
|
||||
* @param mixed $context 上下文数组,自动 JSON 编码
|
||||
*/
|
||||
function logError($message, $context = null)
|
||||
{
|
||||
$dir = __DIR__ . '/../../runtime/logs';
|
||||
if (!is_dir($dir)) {
|
||||
@mkdir($dir, 0775, true);
|
||||
}
|
||||
$line = '[' . date('Y-m-d H:i:s') . '] ' . $message
|
||||
. ($context !== null ? ' ' . json_encode($context, JSON_UNESCAPED_UNICODE) : '')
|
||||
. PHP_EOL;
|
||||
@file_put_contents($dir . '/error-' . date('Ym') . '.log', $line, FILE_APPEND);
|
||||
}
|
||||
|
||||
@@ -11,15 +11,13 @@
|
||||
* 返回:{ country, province, city, label, found }
|
||||
* label 示例:"中国-广东省-深圳市" / "中国香港" / "美国"
|
||||
*/
|
||||
require_once __DIR__ . '/db.php';
|
||||
require_once __DIR__ . '/response.php';
|
||||
require_once __DIR__ . '/auth.php';
|
||||
require_once __DIR__ . '/Api.php';
|
||||
$pdo = Api::boot();
|
||||
|
||||
/**
|
||||
* 接口入口:仅当作为 HTTP 接口直接访问时执行(便于函数被 require 复用/测试)
|
||||
*/
|
||||
if (isset($_SERVER['SCRIPT_FILENAME']) && realpath($_SERVER['SCRIPT_FILENAME']) === __FILE__) {
|
||||
requireLogin();
|
||||
|
||||
$phone = trim($_REQUEST['phone'] ?? '');
|
||||
if ($phone === '') {
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
<?php
|
||||
/**
|
||||
* 安全中间件(批次1):Session 加固 + CSRF Token + 登录限流
|
||||
* 依赖:db.php / response.php / logger.php(均无回环依赖)
|
||||
*/
|
||||
require_once __DIR__ . '/db.php';
|
||||
require_once __DIR__ . '/response.php';
|
||||
require_once __DIR__ . '/logger.php';
|
||||
|
||||
/** 安全启动 Session:httponly + samesite=Lax + HTTPS 下 secure */
|
||||
function startSessionSecure()
|
||||
{
|
||||
if (session_status() === PHP_SESSION_NONE) {
|
||||
$secure = !empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off';
|
||||
session_set_cookie_params([
|
||||
'lifetime' => 0,
|
||||
'path' => '/',
|
||||
'domain' => '',
|
||||
'secure' => $secure,
|
||||
'httponly' => true,
|
||||
'samesite' => 'Lax',
|
||||
]);
|
||||
session_start();
|
||||
}
|
||||
}
|
||||
|
||||
/** 获取(或创建)当前会话绑定的 CSRF Token */
|
||||
function csrfToken()
|
||||
{
|
||||
startSessionSecure();
|
||||
if (empty($_SESSION['csrf_token']) || strlen($_SESSION['csrf_token']) !== 64) {
|
||||
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
|
||||
}
|
||||
return $_SESSION['csrf_token'];
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验写接口的 CSRF Token(要求 X-CSRF-Token 请求头与会话一致)
|
||||
* 配合前端 common.js httpPost 统一附带;防跨站请求伪造(REV-2/REV-SEC-2)
|
||||
*/
|
||||
function checkCsrf()
|
||||
{
|
||||
startSessionSecure();
|
||||
$token = $_SERVER['HTTP_X_CSRF_TOKEN'] ?? '';
|
||||
if ($token === '' || empty($_SESSION['csrf_token'])
|
||||
|| !hash_equals($_SESSION['csrf_token'], $token)) {
|
||||
Response::error('安全校验失败(CSRF),请刷新页面后重试', 403);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 登录限流:同一 IP+账号 15 分钟内失败 ≥5 次则锁定(429)
|
||||
* fail-open:DB 异常时跳过限流,保证登录可用(REV-8)
|
||||
*/
|
||||
function checkLoginThrottle($username, $ip)
|
||||
{
|
||||
try {
|
||||
$pdo = DB::getInstance()->getPdo();
|
||||
$pdo->prepare("DELETE FROM system_login_attempts WHERE attempt_time < NOW() - INTERVAL 30 MINUTE")->execute();
|
||||
$stmt = $pdo->prepare(
|
||||
"SELECT COUNT(*) FROM system_login_attempts
|
||||
WHERE ip = ? AND username = ? AND success = 0 AND attempt_time > NOW() - INTERVAL 15 MINUTE"
|
||||
);
|
||||
$stmt->execute([$ip, $username]);
|
||||
if ((int)$stmt->fetchColumn() >= 5) {
|
||||
Response::error('尝试次数过多,请15分钟后再试', 429);
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
logError('登录限流检查失败(已跳过限流)', ['msg' => $e->getMessage()]);
|
||||
}
|
||||
}
|
||||
|
||||
/** 记录一次登录尝试结果(供限流统计) */
|
||||
function recordLoginAttempt($username, $ip, $success)
|
||||
{
|
||||
try {
|
||||
$pdo = DB::getInstance()->getPdo();
|
||||
$pdo->prepare(
|
||||
"INSERT INTO system_login_attempts (username, ip, success, attempt_time) VALUES (?, ?, ?, NOW())"
|
||||
)->execute([$username, $ip, $success ? 1 : 0]);
|
||||
} catch (Exception $e) {
|
||||
logError('登录尝试记录失败', ['msg' => $e->getMessage()]);
|
||||
}
|
||||
}
|
||||
+2
-9
@@ -3,16 +3,9 @@
|
||||
* 企业新增接口 POST /api/company/add.php
|
||||
* 必填:display_name;其余字段见 COMPANY_FIELDS 白名单
|
||||
*/
|
||||
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/helpers.php';
|
||||
require_once __DIR__ . '/../common/completeness.php';
|
||||
require_once __DIR__ . '/../common/duplicate_check.php';
|
||||
require_once __DIR__ . '/../common/Api.php';
|
||||
$pdo = Api::boot(['module' => 'company']);
|
||||
|
||||
checkAjax();
|
||||
checkPermission('company');
|
||||
|
||||
$data = extractFields(COMPANY_FIELDS);
|
||||
// 显示名称不再单独录入:取中文名称(数据库 display_name 为 NOT NULL)
|
||||
|
||||
@@ -4,11 +4,9 @@
|
||||
* 返回启用中的认证类型(如:瞪羚企业、国家高新技术企业等),供编辑弹窗下拉使用
|
||||
* 数据源:data_dict_certificate(原 certification_types,v1.0.16 改名,v1.0.18 再改名 data_dict_certificate)
|
||||
*/
|
||||
require_once __DIR__ . '/../common/db.php';
|
||||
require_once __DIR__ . '/../common/response.php';
|
||||
require_once __DIR__ . '/../common/auth.php';
|
||||
require_once __DIR__ . '/../common/Api.php';
|
||||
$pdo = Api::boot(['module' => 'company']);
|
||||
|
||||
checkPermission('company');
|
||||
|
||||
$pdo = DB::getInstance()->getPdo();
|
||||
$types = $pdo->query("SELECT id, name FROM data_dict_certificate WHERE is_active = 1 ORDER BY sort_order, id")->fetchAll();
|
||||
|
||||
@@ -3,13 +3,9 @@
|
||||
* 企业删除接口(软删除) POST /api/company/delete.php
|
||||
* 入参:id(或 ids 逗号分隔批量)
|
||||
*/
|
||||
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/Api.php';
|
||||
$pdo = Api::boot(['module' => 'company']);
|
||||
|
||||
checkAjax();
|
||||
checkPermission('company');
|
||||
|
||||
$id = (int)($_POST['id'] ?? 0);
|
||||
$ids = trim($_POST['ids'] ?? '');
|
||||
|
||||
@@ -3,11 +3,9 @@
|
||||
* 企业详情接口 GET /api/company/detail.php?id=1
|
||||
* 返回企业主表信息 + 关联数据(产品品类/文档/财务/资质/联系方式)
|
||||
*/
|
||||
require_once __DIR__ . '/../common/db.php';
|
||||
require_once __DIR__ . '/../common/response.php';
|
||||
require_once __DIR__ . '/../common/auth.php';
|
||||
require_once __DIR__ . '/../common/Api.php';
|
||||
$pdo = Api::boot(['module' => 'company']);
|
||||
|
||||
checkPermission('company');
|
||||
|
||||
$id = (int)($_REQUEST['id'] ?? 0);
|
||||
if ($id <= 0) {
|
||||
|
||||
@@ -4,11 +4,9 @@
|
||||
* 入参:company_id(必填)/ page / limit / keyword(姓名)/ job_level(职级)
|
||||
* 数据源:persons JOIN person_work_experiences(按公司),联系方式取 social_accounts
|
||||
*/
|
||||
require_once __DIR__ . '/../common/db.php';
|
||||
require_once __DIR__ . '/../common/response.php';
|
||||
require_once __DIR__ . '/../common/auth.php';
|
||||
require_once __DIR__ . '/../common/Api.php';
|
||||
$pdo = Api::boot(['module' => 'company']);
|
||||
|
||||
checkPermission('company');
|
||||
|
||||
$companyId = (int)($_REQUEST['company_id'] ?? 0);
|
||||
if ($companyId <= 0) {
|
||||
|
||||
@@ -3,12 +3,9 @@
|
||||
* 企业导出接口(CSV) GET/POST /api/company/export.php
|
||||
* 入参:ids(必填,勾选的企业ID,逗号分隔)——只导出勾选的企业
|
||||
*/
|
||||
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/Api.php';
|
||||
$pdo = Api::boot(['module' => 'company']);
|
||||
|
||||
checkPermission('company');
|
||||
|
||||
// 仅导出勾选的企业
|
||||
$idsRaw = trim($_REQUEST['ids'] ?? '');
|
||||
|
||||
@@ -6,14 +6,9 @@
|
||||
* registered_capital,established_date,is_listed,stock_code
|
||||
* 仅 display_name 为必填。
|
||||
*/
|
||||
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/completeness.php';
|
||||
require_once __DIR__ . '/../common/Api.php';
|
||||
$pdo = Api::boot(['module' => 'company']);
|
||||
|
||||
checkAjax();
|
||||
checkPermission('company');
|
||||
|
||||
if (empty($_FILES['file']) || $_FILES['file']['error'] !== UPLOAD_ERR_OK) {
|
||||
Response::error('请选择要上传的CSV文件', 400);
|
||||
|
||||
@@ -8,11 +8,9 @@
|
||||
* 参数:page / limit / keyword / field / industry / company_type / is_active
|
||||
* 返回:{ list, total, page, limit }
|
||||
*/
|
||||
require_once __DIR__ . '/../common/db.php';
|
||||
require_once __DIR__ . '/../common/response.php';
|
||||
require_once __DIR__ . '/../common/auth.php';
|
||||
require_once __DIR__ . '/../common/Api.php';
|
||||
$pdo = Api::boot(['module' => 'company']);
|
||||
|
||||
checkPermission('company');
|
||||
|
||||
[$page, $limit] = pageParams();
|
||||
$keyword = trim($_REQUEST['keyword'] ?? '');
|
||||
|
||||
@@ -5,16 +5,9 @@
|
||||
* 入参:company_id(必填)/ platform(平台)/ account_id(账号名称)/ profile_url(主页链接)/
|
||||
* remark / is_active(1启用 0停用)/ is_defult(1常用)
|
||||
*/
|
||||
require_once __DIR__ . '/../common/db.php';
|
||||
require_once __DIR__ . '/../common/response.php';
|
||||
require_once __DIR__ . '/../common/auth.php';
|
||||
require_once __DIR__ . '/../common/helpers.php';
|
||||
require_once __DIR__ . '/../common/logger.php';
|
||||
require_once __DIR__ . '/../common/completeness.php';
|
||||
require_once __DIR__ . '/../common/duplicate_check.php';
|
||||
require_once __DIR__ . '/../common/Api.php';
|
||||
$pdo = Api::boot(['module' => 'company']);
|
||||
|
||||
checkAjax();
|
||||
checkPermission('company');
|
||||
|
||||
$companyId = (int)($_POST['company_id'] ?? 0);
|
||||
$platform = trim($_POST['platform'] ?? '');
|
||||
|
||||
@@ -5,11 +5,9 @@
|
||||
* 字段:平台/账号名称/ID/主页链接/状态/更新日期
|
||||
* 入参:company_id(必填)/ page / limit
|
||||
*/
|
||||
require_once __DIR__ . '/../common/db.php';
|
||||
require_once __DIR__ . '/../common/response.php';
|
||||
require_once __DIR__ . '/../common/auth.php';
|
||||
require_once __DIR__ . '/../common/Api.php';
|
||||
$pdo = Api::boot(['module' => 'company']);
|
||||
|
||||
checkPermission('company');
|
||||
|
||||
$companyId = (int)($_REQUEST['company_id'] ?? 0);
|
||||
if ($companyId <= 0) {
|
||||
|
||||
@@ -5,14 +5,9 @@
|
||||
* is_active(1在产 0停产)/ attrs(可选 JSON:[{attr_id, value}],EAV 参数值)
|
||||
* 说明:attr_id 来自 company_products_attr(按企业所属行业定义的参数属性)
|
||||
*/
|
||||
require_once __DIR__ . '/../common/db.php';
|
||||
require_once __DIR__ . '/../common/response.php';
|
||||
require_once __DIR__ . '/../common/auth.php';
|
||||
require_once __DIR__ . '/../common/helpers.php';
|
||||
require_once __DIR__ . '/../common/logger.php';
|
||||
require_once __DIR__ . '/../common/Api.php';
|
||||
$pdo = Api::boot(['module' => 'company']);
|
||||
|
||||
checkAjax();
|
||||
checkPermission('company');
|
||||
|
||||
$companyId = (int)($_POST['company_id'] ?? 0);
|
||||
$categoryName = trim($_POST['category_name'] ?? '');
|
||||
|
||||
@@ -3,11 +3,9 @@
|
||||
* 产品参数属性定义接口 GET /api/company/product_attrs.php?industry=汽车制造
|
||||
* 返回指定行业的参数属性定义(EAV),供新增产品弹窗动态生成参数输入项
|
||||
*/
|
||||
require_once __DIR__ . '/../common/db.php';
|
||||
require_once __DIR__ . '/../common/response.php';
|
||||
require_once __DIR__ . '/../common/auth.php';
|
||||
require_once __DIR__ . '/../common/Api.php';
|
||||
$pdo = Api::boot(['module' => 'company']);
|
||||
|
||||
checkPermission('company');
|
||||
|
||||
$industry = trim($_REQUEST['industry'] ?? '');
|
||||
if ($industry === '') {
|
||||
|
||||
@@ -4,11 +4,9 @@
|
||||
* v1.0.18:返回产品基础字段(产品品类/基本定位/包含系列/状态/更新日期)+ EAV 参数值(JSON)
|
||||
* 入参:company_id(必填)/ page / limit
|
||||
*/
|
||||
require_once __DIR__ . '/../common/db.php';
|
||||
require_once __DIR__ . '/../common/response.php';
|
||||
require_once __DIR__ . '/../common/auth.php';
|
||||
require_once __DIR__ . '/../common/Api.php';
|
||||
$pdo = Api::boot(['module' => 'company']);
|
||||
|
||||
checkPermission('company');
|
||||
|
||||
$companyId = (int)($_REQUEST['company_id'] ?? 0);
|
||||
if ($companyId <= 0) {
|
||||
|
||||
@@ -3,16 +3,9 @@
|
||||
* 企业编辑接口 POST /api/company/update.php
|
||||
* 入参:id + 需要更新的白名单字段
|
||||
*/
|
||||
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/helpers.php';
|
||||
require_once __DIR__ . '/../common/completeness.php';
|
||||
require_once __DIR__ . '/../common/duplicate_check.php';
|
||||
require_once __DIR__ . '/../common/Api.php';
|
||||
$pdo = Api::boot(['module' => 'company']);
|
||||
|
||||
checkAjax();
|
||||
checkPermission('company');
|
||||
|
||||
$id = (int)($_POST['id'] ?? 0);
|
||||
if ($id <= 0) {
|
||||
|
||||
@@ -4,11 +4,9 @@
|
||||
* 返回:8 个指标卡 + 区域分布(国外=世界地图按国家 / 国内=中国地图按省份,企业/人员双维度)
|
||||
* + 行业分布(企业/人员)+ 需求关键词(简化)
|
||||
*/
|
||||
require_once __DIR__ . '/../common/db.php';
|
||||
require_once __DIR__ . '/../common/response.php';
|
||||
require_once __DIR__ . '/../common/auth.php';
|
||||
require_once __DIR__ . '/../common/Api.php';
|
||||
$pdo = Api::boot(['module' => 'dashboard']);
|
||||
|
||||
checkPermission('dashboard');
|
||||
|
||||
$pdo = DB::getInstance()->getPdo();
|
||||
|
||||
|
||||
@@ -3,13 +3,9 @@
|
||||
* 文件删除接口(同时删除物理文件) POST /api/document/delete.php
|
||||
* 入参:id(或 ids 逗号分隔批量)
|
||||
*/
|
||||
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/Api.php';
|
||||
$pdo = Api::boot(['permissions' => ['document', 'competitor_data']]);
|
||||
|
||||
checkAjax();
|
||||
checkAnyPermission(['document', 'competitor_data']);
|
||||
|
||||
$id = (int)($_POST['id'] ?? 0);
|
||||
$ids = trim($_POST['ids'] ?? '');
|
||||
|
||||
@@ -4,11 +4,9 @@
|
||||
* 参数:page / limit / keyword / file_type / is_active
|
||||
* 数据源:company_documents 表
|
||||
*/
|
||||
require_once __DIR__ . '/../common/db.php';
|
||||
require_once __DIR__ . '/../common/response.php';
|
||||
require_once __DIR__ . '/../common/auth.php';
|
||||
require_once __DIR__ . '/../common/Api.php';
|
||||
$pdo = Api::boot(['permissions' => ['document', 'competitor_data']]);
|
||||
|
||||
checkAnyPermission(['document', 'competitor_data']);
|
||||
|
||||
[$page, $limit] = pageParams();
|
||||
$keyword = trim($_REQUEST['keyword'] ?? '');
|
||||
|
||||
@@ -5,13 +5,9 @@
|
||||
* 存储:static/uploads/documents/年/月/随机文件名
|
||||
* 返回:相对路径 storage_path
|
||||
*/
|
||||
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/Api.php';
|
||||
$pdo = Api::boot(['permissions' => ['document', 'competitor_data']]);
|
||||
|
||||
checkAjax();
|
||||
checkAnyPermission(['document', 'competitor_data']);
|
||||
|
||||
if (empty($_FILES['file']) || $_FILES['file']['error'] !== UPLOAD_ERR_OK) {
|
||||
Response::error('请选择要上传的文件', 400);
|
||||
|
||||
@@ -6,15 +6,9 @@
|
||||
* target_product_category / description / source_channel
|
||||
* 写入 preliminary_data,recorded_by=当前登录用户(v1.0.50 起无 status 字段)
|
||||
*/
|
||||
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';
|
||||
require_once __DIR__ . '/../common/Api.php';
|
||||
$pdo = Api::boot(['module' => 'preliminary']);
|
||||
|
||||
checkAjax();
|
||||
checkPermission('preliminary');
|
||||
|
||||
$sourceType = trim($_POST['source_type'] ?? '');
|
||||
if (!in_array($sourceType, ['company', 'person', 'product', 'need', 'mixed'], true)) {
|
||||
|
||||
@@ -6,16 +6,9 @@
|
||||
* description, industry, need_category, application_scenario, contact_person
|
||||
* 逻辑:按目标类型写入主表(product/need 缺公司时自动建公司)→ 标记碎片已转换(converted_type/id, processed_at)
|
||||
*/
|
||||
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/completeness.php';
|
||||
require_once __DIR__ . '/../common/duplicate_check.php';
|
||||
require_once __DIR__ . '/../common/validate.php';
|
||||
require_once __DIR__ . '/../common/Api.php';
|
||||
$pdo = Api::boot(['module' => 'preliminary']);
|
||||
|
||||
checkAjax();
|
||||
checkPermission('preliminary');
|
||||
|
||||
$id = (int)($_POST['id'] ?? 0);
|
||||
$targetType = trim($_POST['target_type'] ?? '');
|
||||
|
||||
@@ -2,12 +2,10 @@
|
||||
/**
|
||||
* 硬碎片详情接口 GET /api/fragment/hard_detail.php?id=1
|
||||
*/
|
||||
require_once __DIR__ . '/../common/db.php';
|
||||
require_once __DIR__ . '/../common/response.php';
|
||||
require_once __DIR__ . '/../common/auth.php';
|
||||
require_once __DIR__ . '/../common/Api.php';
|
||||
$pdo = Api::boot(['module' => 'preliminary']);
|
||||
require_once __DIR__ . '/hard_common.php';
|
||||
|
||||
checkPermission('preliminary');
|
||||
|
||||
$id = (int)($_REQUEST['id'] ?? 0);
|
||||
if ($id <= 0) {
|
||||
|
||||
@@ -4,13 +4,9 @@
|
||||
* 入参:id
|
||||
* v1.0.50:废弃改为从 preliminary_data 彻底硬删除(不再保留记录)
|
||||
*/
|
||||
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/Api.php';
|
||||
$pdo = Api::boot(['module' => 'preliminary']);
|
||||
|
||||
checkAjax();
|
||||
checkPermission('preliminary');
|
||||
|
||||
$id = (int)($_POST['id'] ?? 0);
|
||||
if ($id <= 0) {
|
||||
|
||||
@@ -5,12 +5,10 @@
|
||||
* 返回:{ list, total, page, limit },每行含完整度 score、缺失层级、内容摘要
|
||||
* 说明:v1.0.50 起移除 status 字段(查看弹窗/数据表均不再展示状态),列表仅返回未转换(converted_id IS NULL)记录
|
||||
*/
|
||||
require_once __DIR__ . '/../common/db.php';
|
||||
require_once __DIR__ . '/../common/response.php';
|
||||
require_once __DIR__ . '/../common/auth.php';
|
||||
require_once __DIR__ . '/../common/Api.php';
|
||||
$pdo = Api::boot(['module' => 'preliminary']);
|
||||
require_once __DIR__ . '/hard_common.php';
|
||||
|
||||
checkPermission('preliminary');
|
||||
|
||||
[$page, $limit] = pageParams();
|
||||
$keyword = trim($_REQUEST['keyword'] ?? '');
|
||||
|
||||
@@ -4,22 +4,14 @@
|
||||
* 仅更新硬碎片字段(企业名/联系人/手机/邮箱/身份证/社媒账号/主页链接),不执行转换。
|
||||
* 转换请走 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';
|
||||
require_once __DIR__ . '/../common/Api.php';
|
||||
|
||||
checkAjax();
|
||||
checkPermission('preliminary');
|
||||
$pdo = Api::boot(['module' => '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();
|
||||
@@ -45,6 +37,7 @@ $idNumber = trim($_POST['id_number'] ?? '');
|
||||
$platform = trim($_POST['platform'] ?? '');
|
||||
$accountId = trim($_POST['account_id'] ?? '');
|
||||
$profileUrl = trim($_POST['profile_url'] ?? '');
|
||||
$sourceChannel = trim($_POST['source_channel'] ?? ($frag['source_channel'] ?? '')); // REV-4 来源渠道可编辑
|
||||
if (!in_array($sourceType, ['company', 'person', 'product', 'need', 'mixed'], true)) {
|
||||
Response::error('碎片类型不合法', 400);
|
||||
}
|
||||
@@ -89,7 +82,8 @@ if (!empty($dups)) Response::duplicates($dups);
|
||||
$pdo->prepare(
|
||||
"UPDATE preliminary_data
|
||||
SET source_type = ?, company_name = ?, industry = ?, registration_number = ?, business_role = ?, country = ?, address = ?,
|
||||
person_name = ?, contact_phone = ?, contact_email = ?, work_location = ?, id_number = ?, platform = ?, account_id = ?, profile_url = ?
|
||||
person_name = ?, contact_phone = ?, contact_email = ?, work_location = ?, id_number = ?, platform = ?, account_id = ?, profile_url = ?,
|
||||
source_channel = ?
|
||||
WHERE id = ?"
|
||||
)->execute([
|
||||
$sourceType,
|
||||
@@ -107,6 +101,7 @@ $pdo->prepare(
|
||||
$platform !== '' ? $platform : null,
|
||||
$accountId !== '' ? $accountId : null,
|
||||
$profileUrl !== '' ? $profileUrl : null,
|
||||
$sourceChannel !== '' ? $sourceChannel : null,
|
||||
$id,
|
||||
]);
|
||||
|
||||
@@ -117,6 +112,7 @@ logCurrent('update', 'fragment', 'preliminary_data', $id, [
|
||||
'phone' => $phone,
|
||||
'email' => $email,
|
||||
'id_number' => $idNumber,
|
||||
'source_channel' => $sourceChannel,
|
||||
]);
|
||||
|
||||
Response::success(null, '已保存,点击「转软碎片」完成转换');
|
||||
|
||||
@@ -4,12 +4,9 @@
|
||||
* 返回:{ 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';
|
||||
require_once __DIR__ . '/../common/auth.php';
|
||||
require_once __DIR__ . '/../common/completeness.php';
|
||||
require_once __DIR__ . '/../common/Api.php';
|
||||
$pdo = Api::boot(['module' => 'preliminary']);
|
||||
|
||||
checkPermission('preliminary');
|
||||
|
||||
$table = trim($_REQUEST['table'] ?? '');
|
||||
$id = (int)($_REQUEST['id'] ?? 0);
|
||||
|
||||
@@ -6,12 +6,9 @@
|
||||
* 每行:{ 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';
|
||||
require_once __DIR__ . '/../common/auth.php';
|
||||
require_once __DIR__ . '/../common/completeness.php';
|
||||
require_once __DIR__ . '/../common/Api.php';
|
||||
$pdo = Api::boot(['module' => 'preliminary']);
|
||||
|
||||
checkPermission('preliminary');
|
||||
|
||||
[$page, $limit] = pageParams();
|
||||
$table = trim($_REQUEST['table'] ?? '');
|
||||
|
||||
@@ -5,17 +5,9 @@
|
||||
* 逻辑:更新主表字段 → 重新计算完整度 → 达标则 is_incomplete=0
|
||||
* 特殊:persons 的 phone/email 写 social_accounts(platform=phone/email)
|
||||
*/
|
||||
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/helpers.php';
|
||||
require_once __DIR__ . '/../common/completeness.php';
|
||||
require_once __DIR__ . '/../common/duplicate_check.php';
|
||||
require_once __DIR__ . '/../common/validate.php';
|
||||
require_once __DIR__ . '/../common/Api.php';
|
||||
|
||||
checkAjax();
|
||||
checkPermission('preliminary');
|
||||
$pdo = Api::boot(['module' => 'preliminary']);
|
||||
|
||||
$table = trim($_POST['table'] ?? '');
|
||||
$id = (int)($_POST['id'] ?? 0);
|
||||
@@ -23,8 +15,6 @@ if (!in_array($table, ['persons', 'companies', 'social_accounts', 'media'], true
|
||||
Response::error('参数错误', 400);
|
||||
}
|
||||
|
||||
$pdo = DB::getInstance()->getPdo();
|
||||
|
||||
/** 按白名单收集 POST 字段 */
|
||||
$fields = [];
|
||||
$collect = function ($whitelist) use (&$fields) {
|
||||
@@ -74,7 +64,9 @@ switch ($table) {
|
||||
$dup = findDuplicate($pdo, 'id_number', $fields['id_number'], [$id]);
|
||||
if ($dup) $dups[] = $dup;
|
||||
}
|
||||
$ownAccountIds = $pdo->query("SELECT id FROM social_accounts WHERE owner_type = 'person' AND owner_id = $id")->fetchAll(PDO::FETCH_COLUMN);
|
||||
$ownStmt = $pdo->prepare("SELECT id FROM social_accounts WHERE owner_type = 'person' AND owner_id = ?");
|
||||
$ownStmt->execute([$id]);
|
||||
$ownAccountIds = $ownStmt->fetchAll(PDO::FETCH_COLUMN);
|
||||
if (isset($_POST['phone'])) {
|
||||
$dups = array_merge($dups, checkSingleAccountDuplicate($pdo, $_POST['phone'] !== '' ? 'phone' : '', $_POST['phone'], '', $ownAccountIds));
|
||||
}
|
||||
|
||||
@@ -10,11 +10,9 @@
|
||||
* - pending 待处理碎片(硬待处理 + 软待处理)
|
||||
* 说明:v1.0.50 起 preliminary_data 无 status 字段,已转换以 converted_id 非空标识
|
||||
*/
|
||||
require_once __DIR__ . '/../common/db.php';
|
||||
require_once __DIR__ . '/../common/response.php';
|
||||
require_once __DIR__ . '/../common/auth.php';
|
||||
require_once __DIR__ . '/../common/Api.php';
|
||||
$pdo = Api::boot(['module' => 'preliminary']);
|
||||
|
||||
checkPermission('preliminary');
|
||||
|
||||
$pdo = DB::getInstance()->getPdo();
|
||||
$monthStart = "DATE_FORMAT(CURDATE(), '%Y-%m-01')";
|
||||
|
||||
@@ -3,16 +3,12 @@
|
||||
* 数据定制接口(占位) GET/POST /api/marketing/data_custom.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/Api.php';
|
||||
$pdo = Api::boot(['module' => 'marketing']);
|
||||
|
||||
checkPermission('marketing');
|
||||
|
||||
$action = $_REQUEST['action'] ?? 'info';
|
||||
if ($action === 'submit' && $_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
checkAjax();
|
||||
$content = [
|
||||
'contact_name' => trim($_POST['contact_name'] ?? ''),
|
||||
'contact_phone' => trim($_POST['contact_phone'] ?? ''),
|
||||
|
||||
@@ -7,11 +7,9 @@
|
||||
* filter 按 行业 + 区域 + 关键词 筛选邮箱列表(persons.email + companies 关联联系人邮箱)
|
||||
* 入参(filter):industry / province / city / keyword / page / limit
|
||||
*/
|
||||
require_once __DIR__ . '/../common/db.php';
|
||||
require_once __DIR__ . '/../common/response.php';
|
||||
require_once __DIR__ . '/../common/auth.php';
|
||||
require_once __DIR__ . '/../common/Api.php';
|
||||
$pdo = Api::boot(['module' => 'marketing']);
|
||||
|
||||
checkPermission('marketing');
|
||||
|
||||
$action = $_REQUEST['action'] ?? 'filter';
|
||||
$pdo = DB::getInstance()->getPdo();
|
||||
|
||||
@@ -3,12 +3,9 @@
|
||||
* EDM 邮箱列表导出接口(CSV) GET/POST /api/marketing/edm_export.php
|
||||
* 入参同 edm.php filter:industry / province / city / keyword
|
||||
*/
|
||||
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/Api.php';
|
||||
$pdo = Api::boot(['module' => 'marketing']);
|
||||
|
||||
checkPermission('marketing');
|
||||
|
||||
// 仅导出勾选的邮箱(emails 逗号分隔)
|
||||
$emailsRaw = trim($_REQUEST['emails'] ?? '');
|
||||
|
||||
@@ -3,16 +3,12 @@
|
||||
* 媒体批发接口(占位) GET/POST /api/marketing/media_wholesale.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/Api.php';
|
||||
$pdo = Api::boot(['module' => 'marketing']);
|
||||
|
||||
checkPermission('marketing');
|
||||
|
||||
$action = $_REQUEST['action'] ?? 'info';
|
||||
if ($action === 'inquiry' && $_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
checkAjax();
|
||||
$content = [
|
||||
'media_ids' => trim($_POST['media_ids'] ?? ''),
|
||||
'remark' => trim($_POST['remark'] ?? ''),
|
||||
|
||||
+2
-9
@@ -4,16 +4,9 @@
|
||||
* 必填:owner_type / owner_id / platform / account_id
|
||||
* 可选:profile_url / remark / is_primary + 商业属性(account_level/certification_type/follower_count 等)
|
||||
*/
|
||||
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/helpers.php';
|
||||
require_once __DIR__ . '/../common/completeness.php';
|
||||
require_once __DIR__ . '/../common/duplicate_check.php';
|
||||
require_once __DIR__ . '/../common/Api.php';
|
||||
$pdo = Api::boot(['module' => 'media']);
|
||||
|
||||
checkAjax();
|
||||
checkPermission('media');
|
||||
|
||||
$data = extractFields(MEDIA_FIELDS);
|
||||
if (!in_array($data['owner_type'] ?? '', ['person', 'company'], true)) {
|
||||
|
||||
@@ -3,13 +3,9 @@
|
||||
* 媒体数据删除接口(软删除) POST /api/media/delete.php
|
||||
* 入参:id(或 ids 逗号分隔批量)
|
||||
*/
|
||||
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/Api.php';
|
||||
$pdo = Api::boot(['module' => 'media']);
|
||||
|
||||
checkAjax();
|
||||
checkPermission('media');
|
||||
|
||||
$id = (int)($_POST['id'] ?? 0);
|
||||
$ids = trim($_POST['ids'] ?? '');
|
||||
|
||||
@@ -2,11 +2,9 @@
|
||||
/**
|
||||
* 媒体数据详情接口 GET /api/media/detail.php?id=1
|
||||
*/
|
||||
require_once __DIR__ . '/../common/db.php';
|
||||
require_once __DIR__ . '/../common/response.php';
|
||||
require_once __DIR__ . '/../common/auth.php';
|
||||
require_once __DIR__ . '/../common/Api.php';
|
||||
$pdo = Api::boot(['module' => 'media']);
|
||||
|
||||
checkPermission('media');
|
||||
|
||||
$id = (int)($_REQUEST['id'] ?? 0);
|
||||
if ($id <= 0) {
|
||||
|
||||
@@ -3,12 +3,9 @@
|
||||
* 媒体数据导出接口(CSV) GET/POST /api/media/export.php
|
||||
* 参数同 list.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/Api.php';
|
||||
$pdo = Api::boot(['module' => 'media']);
|
||||
|
||||
checkPermission('media');
|
||||
|
||||
// 仅导出勾选的媒体账号
|
||||
$idsRaw = trim($_REQUEST['ids'] ?? '');
|
||||
|
||||
@@ -5,14 +5,9 @@
|
||||
* account_level,content_categories,follower_count,avg_read_count,certification_type
|
||||
* owner_type 为 person/company 时 owner_id 也可填对应名称(display_name/full_name),自动解析为ID。
|
||||
*/
|
||||
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/completeness.php';
|
||||
require_once __DIR__ . '/../common/Api.php';
|
||||
$pdo = Api::boot(['module' => 'media']);
|
||||
|
||||
checkAjax();
|
||||
checkPermission('media');
|
||||
|
||||
if (empty($_FILES['file']) || $_FILES['file']['error'] !== UPLOAD_ERR_OK) {
|
||||
Response::error('请选择要上传的CSV文件', 400);
|
||||
|
||||
+2
-4
@@ -4,11 +4,9 @@
|
||||
* 参数:page / limit / keyword / platform / certification_type / account_level / owner_type / is_active
|
||||
* 数据源:social_accounts JOIN media_commercial_attributes
|
||||
*/
|
||||
require_once __DIR__ . '/../common/db.php';
|
||||
require_once __DIR__ . '/../common/response.php';
|
||||
require_once __DIR__ . '/../common/auth.php';
|
||||
require_once __DIR__ . '/../common/Api.php';
|
||||
$pdo = Api::boot(['module' => 'media']);
|
||||
|
||||
checkPermission('media');
|
||||
|
||||
[$page, $limit] = pageParams();
|
||||
$keyword = trim($_REQUEST['keyword'] ?? '');
|
||||
|
||||
@@ -3,16 +3,9 @@
|
||||
* 媒体数据编辑接口 POST /api/media/update.php
|
||||
* 入参:id + 白名单字段(含商业属性)
|
||||
*/
|
||||
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/helpers.php';
|
||||
require_once __DIR__ . '/../common/completeness.php';
|
||||
require_once __DIR__ . '/../common/duplicate_check.php';
|
||||
require_once __DIR__ . '/../common/Api.php';
|
||||
$pdo = Api::boot(['module' => 'media']);
|
||||
|
||||
checkAjax();
|
||||
checkPermission('media');
|
||||
|
||||
$id = (int)($_POST['id'] ?? 0);
|
||||
if ($id <= 0) {
|
||||
|
||||
+2
-7
@@ -3,14 +3,9 @@
|
||||
* 需求新增接口 POST /api/need/add.php
|
||||
* 必填:company_id;可选白名单字段见 NEED_FIELDS
|
||||
*/
|
||||
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/helpers.php';
|
||||
require_once __DIR__ . '/../common/Api.php';
|
||||
$pdo = Api::boot(['module' => 'need']);
|
||||
|
||||
checkAjax();
|
||||
checkPermission('need');
|
||||
|
||||
$data = extractFields(NEED_FIELDS);
|
||||
if (empty($data['company_id'])) {
|
||||
|
||||
+2
-6
@@ -3,13 +3,9 @@
|
||||
* 需求删除接口(作废) POST /api/need/delete.php
|
||||
* 入参:id(或 ids 逗号分隔批量)
|
||||
*/
|
||||
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/Api.php';
|
||||
$pdo = Api::boot(['module' => 'need']);
|
||||
|
||||
checkAjax();
|
||||
checkPermission('need');
|
||||
|
||||
$id = (int)($_POST['id'] ?? 0);
|
||||
$ids = trim($_POST['ids'] ?? '');
|
||||
|
||||
+2
-4
@@ -2,11 +2,9 @@
|
||||
/**
|
||||
* 需求详情接口 GET /api/need/detail.php?id=1
|
||||
*/
|
||||
require_once __DIR__ . '/../common/db.php';
|
||||
require_once __DIR__ . '/../common/response.php';
|
||||
require_once __DIR__ . '/../common/auth.php';
|
||||
require_once __DIR__ . '/../common/Api.php';
|
||||
$pdo = Api::boot(['module' => 'need']);
|
||||
|
||||
checkPermission('need');
|
||||
|
||||
$id = (int)($_REQUEST['id'] ?? 0);
|
||||
if ($id <= 0) {
|
||||
|
||||
+2
-4
@@ -3,11 +3,9 @@
|
||||
* 需求转盘列表接口 GET/POST /api/need/list.php
|
||||
* 参数:page / limit / keyword / start_date / end_date / company_name / industry / need_category / is_valid
|
||||
*/
|
||||
require_once __DIR__ . '/../common/db.php';
|
||||
require_once __DIR__ . '/../common/response.php';
|
||||
require_once __DIR__ . '/../common/auth.php';
|
||||
require_once __DIR__ . '/../common/Api.php';
|
||||
$pdo = Api::boot(['module' => 'need']);
|
||||
|
||||
checkPermission('need');
|
||||
|
||||
[$page, $limit] = pageParams();
|
||||
$keyword = trim($_REQUEST['keyword'] ?? '');
|
||||
|
||||
+2
-6
@@ -5,13 +5,9 @@
|
||||
* 说明:当前为占位实现,仅写日志并返回成功;
|
||||
* 后续可扩展为调用第三方API推送JSON数据 / 通过SMTP发送邮件。
|
||||
*/
|
||||
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/Api.php';
|
||||
$pdo = Api::boot(['module' => 'need']);
|
||||
|
||||
checkAjax();
|
||||
checkPermission('need');
|
||||
|
||||
$id = (int)($_POST['id'] ?? 0);
|
||||
$ids = trim($_POST['ids'] ?? '');
|
||||
|
||||
+2
-7
@@ -3,14 +3,9 @@
|
||||
* 需求编辑接口 POST /api/need/update.php
|
||||
* 入参:id + 白名单字段(可含 is_valid 作废/恢复)
|
||||
*/
|
||||
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/helpers.php';
|
||||
require_once __DIR__ . '/../common/Api.php';
|
||||
$pdo = Api::boot(['module' => 'need']);
|
||||
|
||||
checkAjax();
|
||||
checkPermission('need');
|
||||
|
||||
$id = (int)($_POST['id'] ?? 0);
|
||||
if ($id <= 0) {
|
||||
|
||||
+2
-10
@@ -4,17 +4,9 @@
|
||||
* 必填:full_name;union_id 为空时自动生成。
|
||||
* 可选:contacts(JSON 数组,如 [{"platform":"email","account_id":"a@b.com","is_primary":1}])
|
||||
*/
|
||||
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/helpers.php';
|
||||
require_once __DIR__ . '/../common/completeness.php';
|
||||
require_once __DIR__ . '/../common/duplicate_check.php';
|
||||
require_once __DIR__ . '/../common/validate.php';
|
||||
require_once __DIR__ . '/../common/Api.php';
|
||||
$pdo = Api::boot(['module' => 'person']);
|
||||
|
||||
checkAjax();
|
||||
checkPermission('person');
|
||||
|
||||
$data = extractFields(PERSON_FIELDS);
|
||||
if (empty($data['full_name'])) {
|
||||
|
||||
@@ -4,11 +4,9 @@
|
||||
* 入参:person_id(必填)/ page / limit
|
||||
* 返回:该人员任职的公司列表(公司名称/部门/岗位/职级/入职日期/是否在岗)
|
||||
*/
|
||||
require_once __DIR__ . '/../common/db.php';
|
||||
require_once __DIR__ . '/../common/response.php';
|
||||
require_once __DIR__ . '/../common/auth.php';
|
||||
require_once __DIR__ . '/../common/Api.php';
|
||||
$pdo = Api::boot(['module' => 'person']);
|
||||
|
||||
checkPermission('person');
|
||||
|
||||
$personId = (int)($_REQUEST['person_id'] ?? 0);
|
||||
if ($personId <= 0) {
|
||||
|
||||
@@ -4,11 +4,9 @@
|
||||
* 返回启用中的企业(id + 显示名称),供人员「工作履历」行内公司下拉使用
|
||||
* 入参:keyword(可选,模糊匹配企业名称)/ limit(默认 500)
|
||||
*/
|
||||
require_once __DIR__ . '/../common/db.php';
|
||||
require_once __DIR__ . '/../common/response.php';
|
||||
require_once __DIR__ . '/../common/auth.php';
|
||||
require_once __DIR__ . '/../common/Api.php';
|
||||
$pdo = Api::boot(['module' => 'person']);
|
||||
|
||||
checkPermission('person');
|
||||
|
||||
$keyword = trim($_REQUEST['keyword'] ?? '');
|
||||
$limit = min(1000, max(1, (int)($_REQUEST['limit'] ?? 500)));
|
||||
|
||||
@@ -12,11 +12,9 @@
|
||||
* 入参:person_id(必填)/ limit(默认 50)
|
||||
* 返回:{ list: [{id, full_name, phone, tags:[老乡,校友,同事], intimacy}] }
|
||||
*/
|
||||
require_once __DIR__ . '/../common/db.php';
|
||||
require_once __DIR__ . '/../common/response.php';
|
||||
require_once __DIR__ . '/../common/auth.php';
|
||||
require_once __DIR__ . '/../common/Api.php';
|
||||
$pdo = Api::boot(['module' => 'person']);
|
||||
|
||||
checkPermission('person');
|
||||
|
||||
$personId = (int)($_REQUEST['person_id'] ?? 0);
|
||||
if ($personId <= 0) {
|
||||
|
||||
@@ -3,13 +3,9 @@
|
||||
* 人员删除接口(软删除) POST /api/person/delete.php
|
||||
* 入参:id(或 ids 逗号分隔批量)
|
||||
*/
|
||||
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/Api.php';
|
||||
$pdo = Api::boot(['module' => 'person']);
|
||||
|
||||
checkAjax();
|
||||
checkPermission('person');
|
||||
|
||||
$id = (int)($_POST['id'] ?? 0);
|
||||
$ids = trim($_POST['ids'] ?? '');
|
||||
|
||||
@@ -3,11 +3,9 @@
|
||||
* 人员详情接口 GET /api/person/detail.php?id=1
|
||||
* v1.0.16:联系方式改为“手机”“邮箱”两字段(仅常用);另有全部账号列表 accounts 供编辑回显
|
||||
*/
|
||||
require_once __DIR__ . '/../common/db.php';
|
||||
require_once __DIR__ . '/../common/response.php';
|
||||
require_once __DIR__ . '/../common/auth.php';
|
||||
require_once __DIR__ . '/../common/Api.php';
|
||||
$pdo = Api::boot(['module' => 'person']);
|
||||
|
||||
checkPermission('person');
|
||||
|
||||
$id = (int)($_REQUEST['id'] ?? 0);
|
||||
if ($id <= 0) {
|
||||
|
||||
@@ -3,12 +3,9 @@
|
||||
* 人员导出接口(CSV) GET/POST /api/person/export.php
|
||||
* 参数同 list.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/Api.php';
|
||||
$pdo = Api::boot(['module' => 'person']);
|
||||
|
||||
checkPermission('person');
|
||||
|
||||
// 仅导出勾选的人员
|
||||
$idsRaw = trim($_REQUEST['ids'] ?? '');
|
||||
|
||||
@@ -5,14 +5,9 @@
|
||||
* graduated_from,hometown,work_location,phone,email,source_channel,source_detail
|
||||
* 仅 full_name 必填;union_id 留空自动生成;phone/email 写入 social_accounts(默认常用)。
|
||||
*/
|
||||
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/completeness.php';
|
||||
require_once __DIR__ . '/../common/Api.php';
|
||||
$pdo = Api::boot(['module' => 'person']);
|
||||
|
||||
checkAjax();
|
||||
checkPermission('person');
|
||||
|
||||
if (empty($_FILES['file']) || $_FILES['file']['error'] !== UPLOAD_ERR_OK) {
|
||||
Response::error('请选择要上传的CSV文件', 400);
|
||||
|
||||
+2
-4
@@ -5,11 +5,9 @@
|
||||
* 返回:{ list, total, page, limit }
|
||||
* v1.0.16:联系方式列改为“手机”“邮箱”两个字段(仅返回常用:is_defult=1 优先,其次 is_primary=1)
|
||||
*/
|
||||
require_once __DIR__ . '/../common/db.php';
|
||||
require_once __DIR__ . '/../common/response.php';
|
||||
require_once __DIR__ . '/../common/auth.php';
|
||||
require_once __DIR__ . '/../common/Api.php';
|
||||
$pdo = Api::boot(['module' => 'person']);
|
||||
|
||||
checkPermission('person');
|
||||
|
||||
[$page, $limit] = pageParams();
|
||||
$keyword = trim($_REQUEST['keyword'] ?? '');
|
||||
|
||||
@@ -4,11 +4,9 @@
|
||||
* 入参:person_id(必填)/ page / limit
|
||||
* 数据源:该人员任职公司的产品品类(去重)
|
||||
*/
|
||||
require_once __DIR__ . '/../common/db.php';
|
||||
require_once __DIR__ . '/../common/response.php';
|
||||
require_once __DIR__ . '/../common/auth.php';
|
||||
require_once __DIR__ . '/../common/Api.php';
|
||||
$pdo = Api::boot(['module' => 'person']);
|
||||
|
||||
checkPermission('person');
|
||||
|
||||
$personId = (int)($_REQUEST['person_id'] ?? 0);
|
||||
if ($personId <= 0) {
|
||||
|
||||
+2
-10
@@ -3,17 +3,9 @@
|
||||
* 人员编辑接口 POST /api/person/update.php
|
||||
* 入参:id + 白名单字段;可选 contacts(JSON 数组,整体替换联系方式)
|
||||
*/
|
||||
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/helpers.php';
|
||||
require_once __DIR__ . '/../common/completeness.php';
|
||||
require_once __DIR__ . '/../common/duplicate_check.php';
|
||||
require_once __DIR__ . '/../common/validate.php';
|
||||
require_once __DIR__ . '/../common/Api.php';
|
||||
$pdo = Api::boot(['module' => 'person']);
|
||||
|
||||
checkAjax();
|
||||
checkPermission('person');
|
||||
|
||||
$id = (int)($_POST['id'] ?? 0);
|
||||
if ($id <= 0) {
|
||||
|
||||
@@ -3,11 +3,9 @@
|
||||
* 碎片处理列表接口(待定逻辑,暂做基础列表) GET/POST /api/preliminary/list.php
|
||||
* 参数:page / limit / keyword / source_type / start_date / end_date
|
||||
*/
|
||||
require_once __DIR__ . '/../common/db.php';
|
||||
require_once __DIR__ . '/../common/response.php';
|
||||
require_once __DIR__ . '/../common/auth.php';
|
||||
require_once __DIR__ . '/../common/Api.php';
|
||||
$pdo = Api::boot(['module' => 'preliminary']);
|
||||
|
||||
checkPermission('preliminary');
|
||||
|
||||
[$page, $limit] = pageParams();
|
||||
$keyword = trim($_REQUEST['keyword'] ?? '');
|
||||
|
||||
@@ -3,11 +3,9 @@
|
||||
* 碎片处理统计接口 GET /api/preliminary/stats.php
|
||||
* 返回:总数 / 今日新增
|
||||
*/
|
||||
require_once __DIR__ . '/../common/db.php';
|
||||
require_once __DIR__ . '/../common/response.php';
|
||||
require_once __DIR__ . '/../common/auth.php';
|
||||
require_once __DIR__ . '/../common/Api.php';
|
||||
$pdo = Api::boot(['module' => 'preliminary']);
|
||||
|
||||
checkPermission('preliminary');
|
||||
|
||||
$pdo = DB::getInstance()->getPdo();
|
||||
|
||||
|
||||
@@ -4,11 +4,9 @@
|
||||
* 仅超级管理员可见,默认查询近1个月
|
||||
* 参数:page / limit / keyword / action / module / username / start_date / end_date
|
||||
*/
|
||||
require_once __DIR__ . '/../common/db.php';
|
||||
require_once __DIR__ . '/../common/response.php';
|
||||
require_once __DIR__ . '/../common/auth.php';
|
||||
require_once __DIR__ . '/../common/Api.php';
|
||||
$pdo = Api::boot(['super' => true]);
|
||||
|
||||
requireSuperAdmin();
|
||||
|
||||
[$page, $limit] = pageParams();
|
||||
$keyword = trim($_REQUEST['keyword'] ?? '');
|
||||
|
||||
@@ -3,13 +3,9 @@
|
||||
* 新增角色接口(含权限分配) POST /api/system/role_add.php
|
||||
* 入参:role_name / permissions(JSON数组,菜单标识)/ is_active
|
||||
*/
|
||||
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/Api.php';
|
||||
$pdo = Api::boot(['module' => 'system']);
|
||||
|
||||
checkAjax();
|
||||
checkPermission('system');
|
||||
|
||||
$roleName = trim($_POST['role_name'] ?? '');
|
||||
$perms = json_decode($_POST['permissions'] ?? '[]', true);
|
||||
|
||||
@@ -4,13 +4,9 @@
|
||||
* 入参:id(或 ids 逗号分隔批量)
|
||||
* 保护:不允许删除内置超级管理员角色(id=1)及仍被用户使用的角色
|
||||
*/
|
||||
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/Api.php';
|
||||
$pdo = Api::boot(['module' => 'system']);
|
||||
|
||||
checkAjax();
|
||||
checkPermission('system');
|
||||
|
||||
$id = (int)($_POST['id'] ?? 0);
|
||||
$ids = trim($_POST['ids'] ?? '');
|
||||
|
||||
@@ -3,11 +3,9 @@
|
||||
* 角色列表接口 GET/POST /api/system/role_list.php
|
||||
* 参数:page / limit / keyword / is_active
|
||||
*/
|
||||
require_once __DIR__ . '/../common/db.php';
|
||||
require_once __DIR__ . '/../common/response.php';
|
||||
require_once __DIR__ . '/../common/auth.php';
|
||||
require_once __DIR__ . '/../common/Api.php';
|
||||
$pdo = Api::boot(['module' => 'system']);
|
||||
|
||||
checkPermission('system');
|
||||
|
||||
[$page, $limit] = pageParams();
|
||||
$keyword = trim($_REQUEST['keyword'] ?? '');
|
||||
|
||||
@@ -3,13 +3,9 @@
|
||||
* 编辑角色接口(含权限分配) POST /api/system/role_update.php
|
||||
* 入参:id / role_name / permissions(JSON数组)/ is_active
|
||||
*/
|
||||
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/Api.php';
|
||||
$pdo = Api::boot(['module' => 'system']);
|
||||
|
||||
checkAjax();
|
||||
checkPermission('system');
|
||||
|
||||
$id = (int)($_POST['id'] ?? 0);
|
||||
if ($id <= 0) {
|
||||
|
||||
+5
-11
@@ -3,13 +3,9 @@
|
||||
* 新增用户接口 POST /api/system/user_add.php
|
||||
* 入参:username / password / real_name / role_id / is_active
|
||||
*/
|
||||
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/Api.php';
|
||||
|
||||
checkAjax();
|
||||
checkPermission('system');
|
||||
$pdo = Api::boot(['module' => 'system']);
|
||||
|
||||
$username = trim($_POST['username'] ?? '');
|
||||
$password = (string)($_POST['password'] ?? '');
|
||||
@@ -20,15 +16,13 @@ $isActive = isset($_POST['is_active']) ? ((int)$_POST['is_active'] ? 1 : 0) : 1;
|
||||
if ($username === '' || !preg_match('/^[a-zA-Z0-9_]{3,50}$/', $username)) {
|
||||
Response::error('账号需为3-50位字母/数字/下划线', 400);
|
||||
}
|
||||
if (strlen($password) < 6) {
|
||||
Response::error('密码长度不能少于6位', 400);
|
||||
if (strlen($password) < 8 || !preg_match('/[a-zA-Z]/', $password) || !preg_match('/\d/', $password)) {
|
||||
Response::error('密码需不少于8位,且同时包含字母和数字', 400);
|
||||
}
|
||||
if ($roleId <= 0) {
|
||||
Response::error('请选择角色', 400);
|
||||
}
|
||||
|
||||
$pdo = DB::getInstance()->getPdo();
|
||||
|
||||
$chk = $pdo->prepare("SELECT COUNT(*) FROM system_users WHERE username = ?");
|
||||
$chk->execute([$username]);
|
||||
if ((int)$chk->fetchColumn() > 0) {
|
||||
@@ -45,7 +39,7 @@ $stmt = $pdo->prepare(
|
||||
"INSERT INTO system_users (username, password, real_name, role_id, is_active)
|
||||
VALUES (?, ?, ?, ?, ?)"
|
||||
);
|
||||
$stmt->execute([$username, sha1($password), $realName !== '' ? $realName : null, $roleId, $isActive]);
|
||||
$stmt->execute([$username, password_hash($password, PASSWORD_DEFAULT), $realName !== '' ? $realName : null, $roleId, $isActive]);
|
||||
$newId = (int)$pdo->lastInsertId();
|
||||
|
||||
logCurrent('add', 'system', 'system_users', $newId, ['username' => $username, 'role_id' => $roleId, 'is_active' => $isActive]);
|
||||
|
||||
@@ -4,13 +4,9 @@
|
||||
* 入参:id(或 ids 逗号分隔批量)
|
||||
* 保护:不允许删除内置管理员(id=1)
|
||||
*/
|
||||
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/Api.php';
|
||||
$pdo = Api::boot(['module' => 'system']);
|
||||
|
||||
checkAjax();
|
||||
checkPermission('system');
|
||||
|
||||
$id = (int)($_POST['id'] ?? 0);
|
||||
$ids = trim($_POST['ids'] ?? '');
|
||||
|
||||
@@ -3,11 +3,9 @@
|
||||
* 用户列表接口 GET/POST /api/system/user_list.php
|
||||
* 参数:page / limit / keyword / role_id / is_active
|
||||
*/
|
||||
require_once __DIR__ . '/../common/db.php';
|
||||
require_once __DIR__ . '/../common/response.php';
|
||||
require_once __DIR__ . '/../common/auth.php';
|
||||
require_once __DIR__ . '/../common/Api.php';
|
||||
$pdo = Api::boot(['module' => 'system']);
|
||||
|
||||
checkPermission('system');
|
||||
|
||||
[$page, $limit] = pageParams();
|
||||
$keyword = trim($_REQUEST['keyword'] ?? '');
|
||||
|
||||
@@ -3,20 +3,14 @@
|
||||
* 编辑用户接口(含角色分配) POST /api/system/user_update.php
|
||||
* 入参:id / real_name / role_id / is_active / password(可选,留空不改密码)
|
||||
*/
|
||||
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/Api.php';
|
||||
|
||||
checkAjax();
|
||||
checkPermission('system');
|
||||
$pdo = Api::boot(['module' => 'system']);
|
||||
|
||||
$id = (int)($_POST['id'] ?? 0);
|
||||
if ($id <= 0) {
|
||||
Response::error('参数错误', 400);
|
||||
}
|
||||
|
||||
$pdo = DB::getInstance()->getPdo();
|
||||
$check = $pdo->prepare("SELECT * FROM system_users WHERE id = ?");
|
||||
$check->execute([$id]);
|
||||
$old = $check->fetch();
|
||||
@@ -52,11 +46,11 @@ if (isset($_POST['is_active'])) {
|
||||
}
|
||||
$password = (string)($_POST['password'] ?? '');
|
||||
if ($password !== '') {
|
||||
if (strlen($password) < 6) {
|
||||
Response::error('密码长度不能少于6位', 400);
|
||||
if (strlen($password) < 8 || !preg_match('/[a-zA-Z]/', $password) || !preg_match('/\d/', $password)) {
|
||||
Response::error('密码需不少于8位,且同时包含字母和数字', 400);
|
||||
}
|
||||
$sets[] = 'password = ?';
|
||||
$params[] = sha1($password);
|
||||
$params[] = password_hash($password, PASSWORD_DEFAULT);
|
||||
}
|
||||
|
||||
if (!$sets) {
|
||||
|
||||
Reference in New Issue
Block a user