This commit is contained in:
nanguaboss
2026-08-03 00:07:01 +08:00
commit 71c6e8d9c1
112 changed files with 7815 additions and 0 deletions
+30
View File
@@ -0,0 +1,30 @@
<?php
/**
* 企业新增接口 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';
checkAjax();
checkPermission('company');
$data = extractFields(COMPANY_FIELDS);
// 显示名称不再单独录入:优先取中文名称,其次英文名称(数据库 display_name 为 NOT NULL)
if (empty($data['display_name'])) {
$data['display_name'] = $data['name_zh'] ?? ($data['name_en'] ?? '');
}
if (empty($data['display_name'])) {
Response::error('企业名称(中文名称或英文名称)为必填项', 400);
}
$pdo = DB::getInstance()->getPdo();
[$sql, $params] = buildInsert($data);
$pdo->prepare("INSERT INTO companies $sql")->execute($params);
$newId = (int)$pdo->lastInsertId();
logCurrent('add', 'company', 'companies', $newId, $data);
Response::success(['id' => $newId], '新增成功');
+40
View File
@@ -0,0 +1,40 @@
<?php
/**
* 企业删除接口(软删除) 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';
checkAjax();
checkPermission('company');
$id = (int)($_POST['id'] ?? 0);
$ids = trim($_POST['ids'] ?? '');
$idList = [];
if ($id > 0) {
$idList[] = $id;
}
if ($ids !== '') {
foreach (explode(',', $ids) as $v) {
$v = (int)trim($v);
if ($v > 0) {
$idList[] = $v;
}
}
}
$idList = array_unique($idList);
if (!$idList) {
Response::error('参数错误', 400);
}
$pdo = DB::getInstance()->getPdo();
$in = implode(',', array_fill(0, count($idList), '?'));
$stmt = $pdo->prepare("UPDATE companies SET is_active = 0 WHERE id IN ($in)");
$stmt->execute($idList);
$affected = $stmt->rowCount();
logCurrent('delete', 'company', 'companies', null, ['ids' => $idList]);
Response::success(['affected' => $affected], "已删除 $affected 条记录");
+74
View File
@@ -0,0 +1,74 @@
<?php
/**
* 企业详情接口 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';
checkPermission('company');
$id = (int)($_REQUEST['id'] ?? 0);
if ($id <= 0) {
Response::error('参数错误', 400);
}
$pdo = DB::getInstance()->getPdo();
$stmt = $pdo->prepare("SELECT * FROM companies WHERE id = ?");
$stmt->execute([$id]);
$company = $stmt->fetch();
if (!$company) {
Response::error('企业不存在');
}
$products = $pdo->prepare("SELECT id, category_name, category_description, is_core, is_active FROM company_products WHERE company_id = ? AND is_active = 1");
$products->execute([$id]);
$needs = $pdo->prepare("SELECT id, contact_person, need_category, target_product_category, application_scenario, description, is_valid, created_at FROM company_needs WHERE company_id = ? ORDER BY id DESC");
$needs->execute([$id]);
$docs = $pdo->prepare(
"SELECT cd.id, cd.doc_name, cd.storage_path, cd.file_type, cd.publish_date, cd.is_active
FROM company_documents cd
INNER JOIN document_links dl ON dl.document_id = cd.id
WHERE dl.owner_type = 'company' AND dl.owner_id = ? AND cd.is_active = 1"
);
$docs->execute([$id]);
$financials = $pdo->prepare("SELECT * FROM company_financials WHERE company_id = ? ORDER BY fiscal_year DESC");
$financials->execute([$id]);
$relations = $pdo->prepare(
"SELECT cr.id, cr.relation_type, cr.is_direct, cr.ownership_percentage, cr.established_date, c.display_name AS child_name
FROM company_relations cr
LEFT JOIN companies c ON c.id = cr.child_company_id
WHERE cr.parent_company_id = ?"
);
$relations->execute([$id]);
// 关联联系人(工作经历 + 联系方式)
$contacts = $pdo->prepare(
"SELECT p.id, p.full_name,
(SELECT GROUP_CONCAT(CONCAT_WS(' ', pwe2.position, pwe2.department) SEPARATOR ' / ')
FROM person_work_experiences pwe2
WHERE pwe2.company_id = ? AND pwe2.person_id = p.id AND pwe2.is_active = 1) AS position,
(SELECT GROUP_CONCAT(CONCAT(sa.platform, ':', sa.account_id) SEPARATOR ' | ')
FROM social_accounts sa WHERE sa.owner_type = 'person' AND sa.owner_id = p.id AND sa.is_active = 1) AS contacts
FROM person_work_experiences pwe
LEFT JOIN persons p ON p.id = pwe.person_id
WHERE pwe.company_id = ? AND pwe.is_active = 1
GROUP BY p.id, p.full_name"
);
$contacts->execute([$id, $id]);
Response::success([
'company' => $company,
'products' => $products->fetchAll(),
'needs' => $needs->fetchAll(),
'documents' => $docs->fetchAll(),
'financials' => $financials->fetchAll(),
'relations' => $relations->fetchAll(),
'contacts' => $contacts->fetchAll(),
]);
+81
View File
@@ -0,0 +1,81 @@
<?php
/**
* 企业员工列表接口 GET /api/company/employees.php
* 入参: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';
checkPermission('company');
$companyId = (int)($_REQUEST['company_id'] ?? 0);
if ($companyId <= 0) {
Response::error('参数错误', 400);
}
[$page, $limit] = pageParams(10);
$keyword = trim($_REQUEST['keyword'] ?? '');
$jobLevel = trim($_REQUEST['job_level'] ?? '');
$pdo = DB::getInstance()->getPdo();
// 公司存在性校验
$chk = $pdo->prepare("SELECT COUNT(*) FROM companies WHERE id = ?");
$chk->execute([$companyId]);
if ((int)$chk->fetchColumn() === 0) {
Response::error('企业不存在');
}
$where = ['pwe.company_id = ?', 'pwe.is_active = 1'];
$params = [$companyId];
if ($keyword !== '') {
$where[] = 'p.full_name LIKE ?';
$params[] = "%$keyword%";
}
if ($jobLevel !== '') {
$where[] = 'pwe.job_level = ?';
$params[] = $jobLevel;
}
$whereSql = implode(' AND ', $where);
// 总数
$stmt = $pdo->prepare(
"SELECT COUNT(*) FROM person_work_experiences pwe
INNER JOIN persons p ON p.id = pwe.person_id
WHERE $whereSql"
);
$stmt->execute($params);
$total = (int)$stmt->fetchColumn();
// 列表
$offset = ($page - 1) * $limit;
$stmt = $pdo->prepare(
"SELECT p.id AS person_id, p.full_name, pwe.id AS work_id, pwe.position, pwe.department,
pwe.job_level, pwe.start_date, pwe.end_date, pwe.is_current,
(SELECT sa.account_id FROM social_accounts sa
WHERE sa.owner_type = 'person' AND sa.owner_id = p.id AND sa.platform = 'phone' AND sa.is_active = 1
ORDER BY sa.is_primary DESC LIMIT 1) AS phone,
(SELECT sa.account_id FROM social_accounts sa
WHERE sa.owner_type = 'person' AND sa.owner_id = p.id AND sa.platform = 'email' AND sa.is_active = 1
ORDER BY sa.is_primary DESC LIMIT 1) AS email
FROM person_work_experiences pwe
INNER JOIN persons p ON p.id = pwe.person_id
WHERE $whereSql
ORDER BY pwe.is_current DESC, pwe.start_date DESC
LIMIT $limit OFFSET $offset"
);
$stmt->execute($params);
$list = $stmt->fetchAll();
// 职级字典(该公司范围内,供筛选下拉)
$lv = $pdo->prepare(
"SELECT DISTINCT job_level FROM person_work_experiences
WHERE company_id = ? AND is_active = 1 AND job_level IS NOT NULL AND job_level <> ''
ORDER BY job_level"
);
$lv->execute([$companyId]);
$levels = $lv->fetchAll(PDO::FETCH_COLUMN);
Response::success(['list' => $list, 'total' => $total, 'page' => $page, 'limit' => $limit, 'levels' => $levels]);
+58
View File
@@ -0,0 +1,58 @@
<?php
/**
* 企业导出接口(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';
checkPermission('company');
// 仅导出勾选的企业
$idsRaw = trim($_REQUEST['ids'] ?? '');
$idList = [];
if ($idsRaw !== '') {
foreach (explode(',', $idsRaw) as $v) {
$v = (int)trim($v);
if ($v > 0) {
$idList[] = $v;
}
}
}
$idList = array_unique($idList);
if (!$idList) {
Response::error('请先选择需要导出的企业数据', 1);
}
$pdo = DB::getInstance()->getPdo();
$in = implode(',', array_fill(0, count($idList), '?'));
$stmt = $pdo->prepare(
"SELECT id, name_zh, name_en, business_role, country, registration_number,
address, legal_representative, industry, industry_subdivision, website,
registered_capital, established_date, latest_employee_count, latest_annual_revenue,
is_listed, stock_code, created_at
FROM companies WHERE id IN ($in) AND is_active = 1 ORDER BY id DESC"
);
$stmt->execute($idList);
$list = $stmt->fetchAll();
logCurrent('export', 'company', 'companies', null, ['count' => count($list), 'ids' => $idList]);
header('Content-Type: text/csv; charset=utf-8');
header('Content-Disposition: attachment; filename="companies_' . date('Ymd_His') . '.csv"');
echo "\xEF\xBB\xBF"; // UTF-8 BOM,便于 Excel 直接打开
$out = fopen('php://output', 'w');
fputcsv($out, ['ID', '公司名称', '英文名称', '业务角色', '国家/地区', '注册号', '地址', '法人', '行业', '行业细分', '官网', '注册资本', '成立日期', '员工数', '年营收', '是否上市', '股票代码', '创建时间']);
foreach ($list as $r) {
fputcsv($out, [
$r['id'], $r['name_zh'], $r['name_en'], $r['business_role'],
$r['country'], $r['registration_number'], $r['address'], $r['legal_representative'],
$r['industry'], $r['industry_subdivision'], $r['website'], $r['registered_capital'],
$r['established_date'], $r['latest_employee_count'], $r['latest_annual_revenue'],
$r['is_listed'], $r['stock_code'], date('Y-m-d', strtotime($r['created_at'])),
]);
}
fclose($out);
exit;
+84
View File
@@ -0,0 +1,84 @@
<?php
/**
* 企业 CSV 导入接口 POST /api/company/import.php (multipart/form-data, 字段名 file)
* CSV 表头:name_zh,name_en,display_name,business_role,country,registration_number,address,
* legal_form,legal_representative,industry,industry_subdivision,website,
* 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';
checkAjax();
checkPermission('company');
if (empty($_FILES['file']) || $_FILES['file']['error'] !== UPLOAD_ERR_OK) {
Response::error('请选择要上传的CSV文件', 400);
}
$tmp = $_FILES['file']['tmp_name'];
$handle = fopen($tmp, 'r');
if (!$handle) {
Response::error('无法读取文件', 400);
}
// 去掉 UTF-8 BOM
$first = fgets($handle);
$first = preg_replace('/^\xEF\xBB\xBF/', '', $first);
$header = str_getcsv(trim($first));
$map = [
'name_zh' => 'name_zh', 'name_en' => 'name_en', 'display_name' => 'display_name',
'business_role' => 'business_role', 'country' => 'country', 'registration_number' => 'registration_number',
'address' => 'address', 'legal_form' => 'legal_form', 'legal_representative' => 'legal_representative',
'industry' => 'industry', 'industry_subdivision' => 'industry_subdivision', 'website' => 'website',
'registered_capital' => 'registered_capital', 'established_date' => 'established_date',
'is_listed' => 'is_listed', 'stock_code' => 'stock_code',
];
$pdo = DB::getInstance()->getPdo();
$inserted = 0;
$failed = 0;
$stmt = $pdo->prepare(
"INSERT INTO companies
(name_zh, name_en, display_name, business_role, country, registration_number,
address, legal_form, legal_representative, industry, industry_subdivision, website,
registered_capital, established_date, is_listed, stock_code)
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)"
);
while (($row = fgetcsv($handle)) !== false) {
$row = array_map('trim', $row);
$rec = [];
foreach ($header as $idx => $col) {
$col = trim($col);
if (isset($map[$col]) && isset($row[$idx])) {
$rec[$map[$col]] = $row[$idx];
}
}
if (empty($rec['display_name'])) {
$failed++;
continue;
}
$rec['established_date'] = ($rec['established_date'] ?? '') !== '' ? date('Y-m-d', strtotime($rec['established_date'])) : null;
try {
$stmt->execute([
$rec['name_zh'] ?? null, $rec['name_en'] ?? null, $rec['display_name'],
$rec['business_role'] ?? null, $rec['country'] ?? null, $rec['registration_number'] ?? null,
$rec['address'] ?? null, $rec['legal_form'] ?? null, $rec['legal_representative'] ?? null,
$rec['industry'] ?? null, $rec['industry_subdivision'] ?? null, $rec['website'] ?? null,
$rec['registered_capital'] ?? null, $rec['established_date'],
($rec['is_listed'] ?? 0) ? 1 : 0, $rec['stock_code'] ?? null,
]);
$inserted++;
} catch (Exception $e) {
$failed++;
}
}
fclose($handle);
logCurrent('import', 'company', 'companies', null, ['inserted' => $inserted, 'failed' => $failed]);
Response::success(['inserted' => $inserted, 'failed' => $failed], "导入完成:成功 $inserted 条,失败 $failed 条");
+67
View File
@@ -0,0 +1,67 @@
<?php
/**
* 企业列表接口 GET/POST /api/company/list.php
* 参数:page / limit / keyword(公司名/注册号)/ industry / business_role / country / is_active
* 返回:{ list, total, page, limit }
*/
require_once __DIR__ . '/../common/db.php';
require_once __DIR__ . '/../common/response.php';
require_once __DIR__ . '/../common/auth.php';
checkPermission('company');
[$page, $limit] = pageParams();
$keyword = trim($_REQUEST['keyword'] ?? '');
$industry = trim($_REQUEST['industry'] ?? '');
$role = trim($_REQUEST['business_role'] ?? '');
$country = trim($_REQUEST['country'] ?? '');
$active = isset($_REQUEST['is_active']) && $_REQUEST['is_active'] !== '' ? (int)$_REQUEST['is_active'] : null;
$where = ['is_active = 1'];
$params = [];
if ($active !== null) {
$where = [];
$params = [];
$where[] = 'is_active = ?';
$params[] = $active;
}
if ($keyword !== '') {
$where[] = '(display_name LIKE ? OR name_zh LIKE ? OR name_en LIKE ? OR registration_number LIKE ?)';
$like = "%$keyword%";
array_push($params, $like, $like, $like, $like);
}
if ($industry !== '') {
$where[] = 'industry = ?';
$params[] = $industry;
}
if ($role !== '') {
$where[] = 'business_role = ?';
$params[] = $role;
}
if ($country !== '') {
$where[] = 'country = ?';
$params[] = $country;
}
$whereSql = implode(' AND ', $where);
$pdo = DB::getInstance()->getPdo();
$stmt = $pdo->prepare("SELECT COUNT(*) FROM companies WHERE $whereSql");
$stmt->execute($params);
$total = (int)$stmt->fetchColumn();
$offset = ($page - 1) * $limit;
$stmt = $pdo->prepare(
"SELECT id, name_zh, name_en, display_name, business_role, country, registration_number,
address, legal_representative, industry, industry_subdivision, website,
latest_employee_count, latest_annual_revenue, is_listed, stock_code,
is_active, created_at, updated_at
FROM companies
WHERE $whereSql
ORDER BY id DESC
LIMIT $limit OFFSET $offset"
);
$stmt->execute($params);
$list = $stmt->fetchAll();
Response::success(['list' => $list, 'total' => $total, 'page' => $page, 'limit' => $limit]);
+40
View File
@@ -0,0 +1,40 @@
<?php
/**
* 企业编辑接口 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';
checkAjax();
checkPermission('company');
$id = (int)($_POST['id'] ?? 0);
if ($id <= 0) {
Response::error('参数错误', 400);
}
$data = extractFields(COMPANY_FIELDS);
unset($data['display_name']); // 显示名称不允许通过编辑接口置空/改名,如需改名请走完整字段
if (empty($data)) {
Response::error('没有需要更新的字段', 400);
}
$pdo = DB::getInstance()->getPdo();
$check = $pdo->prepare("SELECT id, display_name FROM companies WHERE id = ?");
$check->execute([$id]);
$old = $check->fetch();
if (!$old) {
Response::error('企业不存在');
}
[$sets, $params] = buildUpdate($data);
$params[] = $id;
$pdo->prepare("UPDATE companies SET $sets WHERE id = ?")->execute($params);
logCurrent('update', 'company', 'companies', $id, ['before' => $old, 'after' => $data]);
Response::success(null, '更新成功');