Files
JiJin/admin_api.php
2025-11-18 14:25:00 +08:00

917 lines
31 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
header('Content-Type: application/json');
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: GET, POST, OPTIONS');
header('Access-Control-Allow-Headers: Content-Type');
class FundAdminAPI {
private $configFile = 'data/fund_config.json';
private $operationFile = 'data/operation_log.json';
private $fundNamesFile = 'data/fund_names.json';
private $recommendedFundsFile = 'data/recommended_funds.json';
public function __construct() {
// 确保数据目录存在
if (!is_dir('data')) {
mkdir('data', 0755, true);
}
// 初始化配置文件(如果不存在)
if (!file_exists($this->configFile)) {
$this->initConfig();
}
// 初始化操作日志文件(如果不存在)
if (!file_exists($this->operationFile)) {
$this->initOperationLog();
}
// 初始化基金名称文件(如果不存在)
if (!file_exists($this->fundNamesFile)) {
$this->initFundNames();
}
// 初始化推荐基金文件(如果不存在)
if (!file_exists($this->recommendedFundsFile)) {
$this->initRecommendedFunds();
}
}
/**
* 初始化配置文件
*/
private function initConfig() {
$defaultConfig = [
[
"channel" => "0",
"fund_code" => "003766",
"investment" => 1000,
"name" => "汇添富医疗服务混合"
],
[
"channel" => "0",
"fund_code" => "004206",
"investment" => 1000,
"name" => "基金004206"
],
[
"channel" => "0",
"fund_code" => "019432",
"investment" => 1000,
"name" => "基金019432"
],
[
"channel" => "1",
"fund_code" => "003766",
"investment" => 1000,
"name" => "汇添富医疗服务混合"
],
[
"channel" => "1",
"fund_code" => "008327",
"investment" => 1000,
"name" => "基金008327"
],
[
"channel" => "2",
"fund_code" => "017811",
"investment" => 1000,
"name" => "基金017811"
]
];
file_put_contents($this->configFile, json_encode($defaultConfig, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));
}
/**
* 初始化操作日志
*/
private function initOperationLog() {
$initialLog = [
'operations' => []
];
file_put_contents($this->operationFile, json_encode($initialLog, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));
}
/**
* 初始化推荐基金文件
*/
private function initRecommendedFunds() {
$initialData = [];
file_put_contents($this->recommendedFundsFile, json_encode($initialData, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));
}
/**
* 初始化基金名称文件
*/
private function initFundNames() {
// 从配置中提取已有的基金名称作为初始数据
$defaultFundNames = [];
$config = $this->loadConfig();
foreach ($config as $fund) {
if (isset($fund['fund_code']) && isset($fund['name']) && $fund['name'] !== "基金{$fund['fund_code']}") {
$defaultFundNames[$fund['fund_code']] = $fund['name'];
}
}
// 添加一些常用基金名称
$commonFunds = [
'003766' => '汇添富医疗服务混合',
'110022' => '易方达消费行业股票',
'001714' => '工银文体产业股票',
'000241' => '中欧时代先锋股票',
'001475' => '易方达国防军工混合',
'161725' => '招商中证白酒指数',
'000689' => '前海开源新经济混合',
'001102' => '前海开源国家比较优势混合',
'001593' => '天弘创业板ETF联接',
'000971' => '高升沪深300指数增强'
];
// 合并默认基金名称
$fundNames = array_merge($defaultFundNames, $commonFunds);
file_put_contents($this->fundNamesFile, json_encode($fundNames, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));
}
/**
* 加载基金名称映射
*/
private function loadFundNames() {
if (!file_exists($this->fundNamesFile)) {
$this->initFundNames();
}
$data = file_get_contents($this->fundNamesFile);
if (!$data) {
return [];
}
$fundNames = json_decode($data, true);
return is_array($fundNames) ? $fundNames : [];
}
/**
* 保存基金名称映射
*/
private function saveFundNames($fundNames) {
$result = file_put_contents($this->fundNamesFile, json_encode($fundNames, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));
if ($result === false) {
error_log('无法写入基金名称文件,请检查目录权限');
return false;
}
return true;
}
/**
* 记录操作日志
*/
private function logOperation($type, $fundCode, $channel = null, $investment = null, $details = '') {
$log = $this->loadOperationLog();
$operation = [
'id' => uniqid(),
'type' => $type, // 'add', 'update', 'delete'
'fund_code' => $fundCode,
'channel' => $channel,
'investment' => $investment,
'details' => $details,
'timestamp' => time(),
'date' => date('Y-m-d H:i:s')
];
$log['operations'][] = $operation;
// 只保留最近100条操作记录
if (count($log['operations']) > 100) {
$log['operations'] = array_slice($log['operations'], -100);
}
file_put_contents($this->operationFile, json_encode($log, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));
return $operation;
}
/**
* 加载操作日志
*/
private function loadOperationLog() {
if (!file_exists($this->operationFile)) {
$this->initOperationLog();
}
$data = file_get_contents($this->operationFile);
if (!$data) {
return ['operations' => []];
}
$log = json_decode($data, true);
return is_array($log) ? $log : ['operations' => []];
}
/**
* 获取基金名称优先从文件读取不存在时从API获取并保存
*/
public function getFundName($fundCode) {
try {
// 优先从文件中查找
$fundNames = $this->loadFundNames();
if (isset($fundNames[$fundCode])) {
return [
'success' => true,
'name' => $fundNames[$fundCode]
];
} else {
// 文件中没有尝试从API获取
$fundName = $this->fetchFundNameFromAPI($fundCode);
if ($fundName) {
// 将新获取的基金名称保存到文件中
$fundNames[$fundCode] = $fundName;
$this->saveFundNames($fundNames);
return [
'success' => true,
'name' => $fundName
];
} else {
// 如果API也获取失败尝试从本地数据查找
$config = $this->loadConfig();
$fund = array_filter($config, function($item) use ($fundCode) {
return $item['fund_code'] == $fundCode;
});
$fund = reset($fund);
if ($fund && isset($fund['name'])) {
// 保存到文件中供以后使用
$fundNames[$fundCode] = $fund['name'];
$this->saveFundNames($fundNames);
return [
'success' => true,
'name' => $fund['name']
];
} else {
// 如果都没有,返回默认名称
return [
'success' => true,
'name' => "基金$fundCode"
];
}
}
}
} catch (Exception $e) {
return [
'success' => false,
'message' => '获取基金名称失败: ' . $e->getMessage()
];
}
}
/**
* 获取操作日志
*/
public function getOperationLog($limit = 20) {
try {
$log = $this->loadOperationLog();
$operations = $log['operations'];
// 按时间倒序排列
usort($operations, function($a, $b) {
return $b['timestamp'] - $a['timestamp'];
});
// 限制返回数量
$operations = array_slice($operations, 0, $limit);
return [
'success' => true,
'data' => $operations
];
} catch (Exception $e) {
return [
'success' => false,
'message' => '获取操作日志失败: ' . $e->getMessage()
];
}
}
/**
* 加载基金配置
*/
private function loadConfig() {
if (!file_exists($this->configFile)) {
$this->initConfig();
}
$data = file_get_contents($this->configFile);
if (!$data) {
return [];
}
$config = json_decode($data, true);
return is_array($config) ? $config : [];
}
/**
* 保存基金配置
*/
private function saveConfig($config) {
$result = file_put_contents($this->configFile, json_encode($config, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));
if ($result === false) {
throw new Exception('无法写入配置文件,请检查目录权限');
}
return true;
}
/**
* 获取所有基金配置
*/
public function getFunds() {
try {
$config = $this->loadConfig();
return [
'success' => true,
'data' => $config
];
} catch (Exception $e) {
return [
'success' => false,
'message' => '获取基金配置失败: ' . $e->getMessage()
];
}
}
/**
* 添加基金
*/
public function addFund($fundData) {
try {
// 调试:记录接收到的数据
error_log("addFund received: " . print_r($fundData, true));
$config = $this->loadConfig();
// 验证数据
if (!isset($fundData['fund_code']) || trim($fundData['fund_code']) === '') {
return [
'success' => false,
'message' => '基金代码不能为空'
];
}
if (!isset($fundData['channel']) || trim($fundData['channel']) === '') {
return [
'success' => false,
'message' => '渠道不能为空'
];
}
$fundCode = trim($fundData['fund_code']);
$channel = trim($fundData['channel']);
// 验证基金代码格式
if (!preg_match('/^\d{6}$/', $fundCode)) {
return [
'success' => false,
'message' => '基金代码必须是6位数字'
];
}
// 检查是否已存在
foreach ($config as $item) {
if ($item['fund_code'] === $fundCode && $item['channel'] === $channel) {
return [
'success' => false,
'message' => '该渠道下已存在相同的基金代码'
];
}
}
// 添加新基金
// 获取基金名称
$fundName = "基金{$fundCode}";
$fundNames = [
'003766' => '汇添富医疗服务混合',
'110022' => '易方达消费行业股票'
];
if (isset($fundNames[$fundCode])) {
$fundName = $fundNames[$fundCode];
}
$newFund = [
'channel' => $channel,
'fund_code' => $fundCode,
'investment' => isset($fundData['investment']) ? floatval($fundData['investment']) : 1000,
'name' => $fundName
];
$config[] = $newFund;
if ($this->saveConfig($config)) {
// 记录操作日志
$this->logOperation('add', $fundCode, $channel, $newFund['investment'], '添加基金');
return [
'success' => true,
'message' => '基金添加成功'
];
} else {
return [
'success' => false,
'message' => '保存配置失败'
];
}
} catch (Exception $e) {
return [
'success' => false,
'message' => '添加基金失败: ' . $e->getMessage()
];
}
}
/**
* 更新基金
*/
public function updateFund($fundData) {
try {
// 调试:记录接收到的数据
error_log("updateFund received: " . print_r($fundData, true));
$config = $this->loadConfig();
$index = isset($fundData['index']) ? intval($fundData['index']) : -1;
// 验证索引
if ($index < 0 || !isset($config[$index])) {
return [
'success' => false,
'message' => '基金不存在'
];
}
// 验证数据
if (!isset($fundData['fund_code']) || trim($fundData['fund_code']) === '') {
return [
'success' => false,
'message' => '基金代码不能为空'
];
}
if (!isset($fundData['channel']) || trim($fundData['channel']) === '') {
return [
'success' => false,
'message' => '渠道不能为空'
];
}
$fundCode = trim($fundData['fund_code']);
$channel = trim($fundData['channel']);
// 获取旧数据用于日志
$oldFund = $config[$index];
// 检查重复(排除自身)
foreach ($config as $i => $item) {
if ($i !== $index && $item['fund_code'] === $fundCode && $item['channel'] === $channel) {
return [
'success' => false,
'message' => '该渠道下已存在相同的基金代码'
];
}
}
// 更新基金信息
$config[$index] = [
'channel' => $channel,
'fund_code' => $fundCode,
'investment' => isset($fundData['investment']) ? floatval($fundData['investment']) : 1000,
'name' => $oldFund['name'] ?? "基金{$fundCode}"
];
if ($this->saveConfig($config)) {
// 记录操作日志
$details = sprintf('更新基金: 投资金额 %s -> %s',
$oldFund['investment'],
$config[$index]['investment']
);
$this->logOperation('update', $fundCode, $channel, $config[$index]['investment'], $details);
return [
'success' => true,
'message' => '基金更新成功'
];
} else {
return [
'success' => false,
'message' => '保存配置失败'
];
}
} catch (Exception $e) {
return [
'success' => false,
'message' => '更新基金失败: ' . $e->getMessage()
];
}
}
/**
* 删除基金
*/
public function deleteFund($index) {
try {
$config = $this->loadConfig();
$index = intval($index);
// 验证索引
if (!isset($config[$index])) {
return [
'success' => false,
'message' => '基金不存在'
];
}
// 获取要删除的基金信息用于日志
$deletedFund = $config[$index];
// 删除基金
array_splice($config, $index, 1);
if ($this->saveConfig($config)) {
// 记录操作日志
$this->logOperation('delete', $deletedFund['fund_code'], $deletedFund['channel'], $deletedFund['investment'], '删除基金');
return [
'success' => true,
'message' => '基金删除成功'
];
} else {
return [
'success' => false,
'message' => '保存配置失败'
];
}
} catch (Exception $e) {
return [
'success' => false,
'message' => '删除基金失败: ' . $e->getMessage()
];
}
}
/**
* 获取基金名称列表优先从文件读取不存在时从API获取并保存
*/
public function getFundNames() {
$fundCodes = $_GET['fund_codes'] ?? '';
if (empty($fundCodes)) {
return [
'success' => false,
'message' => '基金代码不能为空'
];
}
$codes = explode(',', $fundCodes);
$results = [];
$fundNames = $this->loadFundNames();
foreach ($codes as $code) {
$code = trim($code);
if (!empty($code)) {
// 先从文件中查找
if (isset($fundNames[$code])) {
$results[$code] = $fundNames[$code];
} else {
// 文件中没有则从API获取
$fundName = $this->fetchFundNameFromAPI($code);
if ($fundName) {
$results[$code] = $fundName;
// 将新获取的基金名称保存到文件中
$fundNames[$code] = $fundName;
$this->saveFundNames($fundNames);
} else {
// 如果API也获取失败使用默认名称
$results[$code] = "基金$code";
}
}
}
}
return [
'success' => true,
'data' => $results,
'message' => '获取基金名称成功'
];
}
/**
* 获取推荐基金列表
*/
private function getRecommendedFunds() {
try {
$recommendedFunds = [];
if (file_exists($this->recommendedFundsFile)) {
$data = file_get_contents($this->recommendedFundsFile);
$recommendedFunds = json_decode($data, true);
if (!is_array($recommendedFunds)) {
$recommendedFunds = [];
}
}
// 按时间倒序排序
usort($recommendedFunds, function($a, $b) {
return strtotime($b['timestamp']) - strtotime($a['timestamp']);
});
echo json_encode(['success' => true, 'data' => $recommendedFunds], JSON_UNESCAPED_UNICODE);
} catch (Exception $e) {
echo json_encode(['success' => false, 'message' => '获取推荐基金失败: ' . $e->getMessage()], JSON_UNESCAPED_UNICODE);
}
}
/**
* 批准推荐基金
*/
private function approveRecommendedFund() {
try {
$fundCode = isset($_GET['fund_code']) ? $_GET['fund_code'] : '';
if (empty($fundCode)) {
echo json_encode(['success' => false, 'message' => '基金代码不能为空'], JSON_UNESCAPED_UNICODE);
return;
}
// 加载推荐基金数据
$recommendedFunds = [];
if (file_exists($this->recommendedFundsFile)) {
$data = file_get_contents($this->recommendedFundsFile);
$recommendedFunds = json_decode($data, true);
if (!is_array($recommendedFunds)) {
$recommendedFunds = [];
}
}
// 查找并更新基金状态
$found = false;
foreach ($recommendedFunds as &$fund) {
if ($fund['fund_code'] == $fundCode) {
$fund['status'] = 'approved';
$found = true;
break;
}
}
if (!$found) {
echo json_encode(['success' => false, 'message' => '未找到该推荐基金'], JSON_UNESCAPED_UNICODE);
return;
}
// 保存更新后的数据
file_put_contents($this->recommendedFundsFile, json_encode($recommendedFunds, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));
// 添加到基金配置中
$config = $this->loadConfig();
$fundExists = false;
foreach ($config as $existingFund) {
if ($existingFund['fund_code'] == $fundCode && $existingFund['channel'] == '0') {
$fundExists = true;
break;
}
}
if (!$fundExists) {
$newFund = [
'channel' => '2', // 2表示支付宝渠道
'fund_code' => $fundCode,
'investment' => 1000,
'name' => '基金' . $fundCode
];
$config[] = $newFund;
$this->saveConfig($config);
}
// 记录操作日志
$this->logOperation('approve_recommended_fund', ['fund_code' => $fundCode]);
echo json_encode(['success' => true, 'message' => '基金批准成功'], JSON_UNESCAPED_UNICODE);
} catch (Exception $e) {
echo json_encode(['success' => false, 'message' => '批准基金失败: ' . $e->getMessage()], JSON_UNESCAPED_UNICODE);
}
}
/**
* 删除推荐基金
*/
private function deleteRecommendedFund() {
try {
$fundCode = isset($_GET['fund_code']) ? $_GET['fund_code'] : '';
if (empty($fundCode)) {
echo json_encode(['success' => false, 'message' => '基金代码不能为空'], JSON_UNESCAPED_UNICODE);
return;
}
// 加载推荐基金数据
$recommendedFunds = [];
if (file_exists($this->recommendedFundsFile)) {
$data = file_get_contents($this->recommendedFundsFile);
$recommendedFunds = json_decode($data, true);
if (!is_array($recommendedFunds)) {
$recommendedFunds = [];
}
}
// 过滤掉要删除的基金
$filteredFunds = array_filter($recommendedFunds, function($fund) use ($fundCode) {
return $fund['fund_code'] != $fundCode;
});
// 保存过滤后的数据
file_put_contents($this->recommendedFundsFile, json_encode(array_values($filteredFunds), JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));
// 记录操作日志
$this->logOperation('delete_recommended_fund', ['fund_code' => $fundCode]);
echo json_encode(['success' => true, 'message' => '基金删除成功'], JSON_UNESCAPED_UNICODE);
} catch (Exception $e) {
echo json_encode(['success' => false, 'message' => '删除基金失败: ' . $e->getMessage()], JSON_UNESCAPED_UNICODE);
}
}
/**
* 从API获取基金名称只在文件中不存在时调用
*/
private function fetchFundNameFromAPI($fundCode) {
// 尝试多个API源
$apiSources = [
"https://fundgz.1234567.com.cn/js/{$fundCode}.js?rt=" . time(),
"https://api.doctorxiong.club/v1/fund/detail?code={$fundCode}"
];
foreach ($apiSources as $apiUrl) {
try {
// 设置超时时间
$context = stream_context_create([
'http' => [
'timeout' => 5,
'header' => 'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'
]
]);
$response = file_get_contents($apiUrl, false, $context);
if ($response) {
// 处理不同API的响应格式
if (strpos($apiUrl, 'fundgz.1234567.com.cn') !== false) {
// 处理JSONP格式
if (preg_match('/jsonpgz\((.*)\)/', $response, $matches)) {
$data = json_decode($matches[1], true);
if (isset($data['name']) && !empty($data['name'])) {
return $data['name'];
}
}
} else if (strpos($apiUrl, 'doctorxiong.club') !== false) {
// 处理JSON格式
$data = json_decode($response, true);
if (isset($data['data']['name']) && !empty($data['data']['name'])) {
return $data['data']['name'];
}
}
}
} catch (Exception $e) {
// 记录错误但继续尝试下一个API源
error_log("获取基金{$fundCode}名称失败: " . $e->getMessage());
continue;
}
}
// 所有API都失败返回false
return false;
}
/**
* 处理API请求
*/
public function handleRequest() {
$method = $_SERVER['REQUEST_METHOD'];
if ($method === 'OPTIONS') {
exit(0);
}
// 解析请求参数
$queryString = $_SERVER['QUERY_STRING'] ?? '';
parse_str($queryString, $params);
$action = $params['action'] ?? '';
if ($method === 'GET') {
switch ($action) {
case 'get_funds':
$result = $this->getFunds();
echo json_encode($result, JSON_UNESCAPED_UNICODE);
break;
case 'get_operation_log':
$limit = isset($params['limit']) ? intval($params['limit']) : 20;
$result = $this->getOperationLog($limit);
echo json_encode($result, JSON_UNESCAPED_UNICODE);
break;
case 'get_fund_name':
$fundCode = $params['fund_code'] ?? '';
if ($fundCode) {
$result = $this->getFundName($fundCode);
echo json_encode($result, JSON_UNESCAPED_UNICODE);
} else {
echo json_encode(['success' => false, 'message' => '基金代码不能为空']);
}
break;
case 'get_fund_names':
$result = $this->getFundNames();
echo json_encode($result, JSON_UNESCAPED_UNICODE);
break;
case 'get_recommended_funds':
$this->getRecommendedFunds();
break;
case 'approve_recommended_fund':
$this->approveRecommendedFund();
break;
case 'delete_recommended_fund':
$this->deleteRecommendedFund();
break;
default:
http_response_code(404);
echo json_encode(['success' => false, 'message' => 'Endpoint not found']);
}
} elseif ($method === 'POST') {
// 获取POST数据
$input = file_get_contents('php://input');
// 调试:记录原始输入
error_log("Raw input: " . $input);
$postData = json_decode($input, true);
if (json_last_error() !== JSON_ERROR_NONE) {
http_response_code(400);
echo json_encode([
'success' => false,
'message' => '无效的JSON数据: ' . json_last_error_msg(),
'debug_input' => $input
]);
return;
}
// 调试:记录解析后的数据
error_log("Parsed data: " . print_r($postData, true));
switch ($action) {
case 'add_fund':
$result = $this->addFund($postData);
echo json_encode($result, JSON_UNESCAPED_UNICODE);
break;
case 'update_fund':
$result = $this->updateFund($postData);
echo json_encode($result, JSON_UNESCAPED_UNICODE);
break;
case 'delete_fund':
$result = $this->deleteFund($postData['index']);
echo json_encode($result, JSON_UNESCAPED_UNICODE);
break;
default:
http_response_code(404);
echo json_encode(['success' => false, 'message' => 'Endpoint not found']);
}
} else {
http_response_code(405);
echo json_encode(['success' => false, 'message' => 'Method not allowed']);
}
}
}
// 错误处理
try {
$api = new FundAdminAPI();
$api->handleRequest();
} catch (Exception $e) {
http_response_code(500);
echo json_encode([
'success' => false,
'message' => '服务器内部错误: ' . $e->getMessage()
]);
}
?>