初始化版本

This commit is contained in:
LL
2025-11-18 14:25:00 +08:00
commit d6640719bd
42 changed files with 14150 additions and 0 deletions

917
admin_api.php Normal file
View File

@@ -0,0 +1,917 @@
<?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()
]);
}
?>