configFile)) { $this->initConfig(); } // 初始化操作日志文件(如果不存在) if (!FileManager::fileExists($this->operationFile)) { $this->initOperationLog(); } // 初始化基金名称文件(如果不存在) if (!FileManager::fileExists($this->fundNamesFile)) { $this->initFundNames(); } // 初始化推荐基金文件(如果不存在) if (!FileManager::fileExists($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" ] ]; FileManager::saveJsonFile($this->configFile, $defaultConfig); } /** * 初始化操作日志 */ private function initOperationLog() { $initialLog = [ 'operations' => [] ]; FileManager::saveJsonFile($this->operationFile, $initialLog); } /** * 初始化推荐基金文件 */ private function initRecommendedFunds() { $initialData = []; FileManager::saveJsonFile($this->recommendedFundsFile, $initialData); } /** * 初始化基金名称文件 */ 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); FileManager::saveJsonFile($this->fundNamesFile, $fundNames); } /** * 加载基金名称映射 */ private function loadFundNames() { if (!FileManager::fileExists($this->fundNamesFile)) { $this->initFundNames(); } return FileManager::loadJsonFile($this->fundNamesFile, []); } /** * 保存基金名称映射 */ private function saveFundNames($fundNames) { $result = FileManager::saveJsonFile($this->fundNamesFile, $fundNames); 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); } // 记录操作日志 FileManager::saveJsonFile($this->operationFile, $log); return $operation; } /** * 加载操作日志 */ private function loadOperationLog() { return FileManager::loadJsonFile($this->operationFile, ['operations' => []], [$this, 'initOperationLog']); } /** * 获取基金名称(优先从文件读取,不存在时从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() { return FileManager::loadJsonFile($this->configFile, [], [$this, 'initConfig']); } /** * 保存基金配置 */ private function saveConfig($config) { $result = FileManager::saveJsonFile($this->configFile, $config); 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 = FileManager::loadJsonFile($this->recommendedFundsFile, []); // 按时间倒序排序 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 = FileManager::loadJsonFile($this->recommendedFundsFile, []); // 查找并更新基金状态 $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; } // 保存更新后的数据 FileManager::saveJsonFile($this->recommendedFundsFile, $recommendedFunds); // 添加到基金配置中 $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 = FileManager::loadJsonFile($this->recommendedFundsFile, []); // 过滤掉要删除的基金 $filteredFunds = array_filter($recommendedFunds, function($fund) use ($fundCode) { return $fund['fund_code'] != $fundCode; }); // 保存过滤后的数据 FileManager::saveJsonFile($this->recommendedFundsFile, array_values($filteredFunds)); // 记录操作日志 $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 = FileManager::loadFile($apiUrl); 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() { // 处理OPTIONS请求 $this->handleOptionsRequest(); // 解析请求参数 $request = $this->parseRequest(); $method = $request['method']; $action = $request['action']; $params = $request['params']; 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; case 'get_email_config': $this->getEmailConfig(); break; default: http_response_code(404); echo json_encode(['success' => false, 'message' => 'Endpoint not found']); } } elseif ($method === 'POST') { // 如果之前没有成功解析POST数据,则再次尝试 if (!isset($postData) || json_last_error() !== JSON_ERROR_NONE) { // 获取POST数据 $input = file_get_contents('php://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; } } 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; case 'update_email_config': $this->updateEmailConfig($postData); break; case 'add_email_recipient': $this->addEmailRecipient($postData); break; case 'delete_email_recipient': $this->deleteEmailRecipient($postData); 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']); } } /** * 获取邮箱配置 */ private function getEmailConfig() { $emailConfigFile = 'data/email_config.json'; $emailConfig = FileManager::loadJsonFile($emailConfigFile, null); if ($emailConfig !== null) { echo json_encode([ 'success' => true, 'data' => $emailConfig ]); } else { echo json_encode([ 'success' => false, 'message' => '邮箱配置文件不存在' ]); } } /** * 更新邮箱配置 */ private function updateEmailConfig($postData) { $emailConfigFile = 'data/email_config.json'; // 验证必要参数 $requiredParams = ['smtp_server', 'smtp_port', 'smtp_secure', 'username', 'password', 'from_email', 'from_name']; foreach ($requiredParams as $param) { if (!isset($postData[$param]) || empty(trim($postData[$param]))) { echo json_encode([ 'success' => false, 'message' => "参数 {$param} 不能为空" ]); return; } } // 构建新的配置 $newConfig = [ 'smtp_server' => trim($postData['smtp_server']), 'smtp_port' => intval($postData['smtp_port']), 'smtp_secure' => trim($postData['smtp_secure']), 'username' => trim($postData['username']), 'password' => trim($postData['password']), 'from_email' => trim($postData['from_email']), 'from_name' => trim($postData['from_name']), 'to_emails' => isset($postData['to_emails']) && is_array($postData['to_emails']) ? $postData['to_emails'] : [] ]; // 保存配置 if (FileManager::saveJsonFile($emailConfigFile, $newConfig)) { // 记录操作日志 $this->logOperation('更新邮箱配置', ['smtp_server' => $newConfig['smtp_server'], 'username' => $newConfig['username']]); echo json_encode([ 'success' => true, 'message' => '邮箱配置更新成功' ]); } else { echo json_encode([ 'success' => false, 'message' => '邮箱配置更新失败' ]); } } /** * 添加邮箱收件人 */ private function addEmailRecipient($postData) { $emailConfigFile = 'data/email_config.json'; if (!isset($postData['email']) || empty(trim($postData['email']))) { echo json_encode([ 'success' => false, 'message' => '邮箱地址不能为空' ]); return; } $newEmail = trim($postData['email']); // 验证邮箱格式 if (!filter_var($newEmail, FILTER_VALIDATE_EMAIL)) { echo json_encode([ 'success' => false, 'message' => '邮箱地址格式无效' ]); return; } $emailConfig = FileManager::loadJsonFile($emailConfigFile, null); if ($emailConfig !== null) { // 检查邮箱是否已经存在 if (in_array($newEmail, $emailConfig['to_emails'])) { echo json_encode([ 'success' => false, 'message' => '邮箱地址已经存在' ]); return; } // 添加新邮箱 $emailConfig['to_emails'][] = $newEmail; // 保存配置 if (FileManager::saveJsonFile($emailConfigFile, $emailConfig)) { // 记录操作日志 $this->logOperation('添加邮箱收件人', ['email' => $newEmail]); echo json_encode([ 'success' => true, 'message' => '邮箱收件人添加成功', 'data' => $emailConfig['to_emails'] ]); } else { echo json_encode([ 'success' => false, 'message' => '邮箱收件人添加失败' ]); } } else { echo json_encode([ 'success' => false, 'message' => '邮箱配置文件不存在' ]); } } /** * 删除邮箱收件人 */ private function deleteEmailRecipient($postData) { $emailConfigFile = 'data/email_config.json'; if (!isset($postData['email']) || empty(trim($postData['email']))) { echo json_encode([ 'success' => false, 'message' => '邮箱地址不能为空' ]); return; } $deleteEmail = trim($postData['email']); $emailConfig = FileManager::loadJsonFile($emailConfigFile, null); if ($emailConfig !== null) { // 查找邮箱在数组中的位置 $index = array_search($deleteEmail, $emailConfig['to_emails']); if ($index === false) { echo json_encode([ 'success' => false, 'message' => '未找到该邮箱地址' ]); return; } // 删除邮箱 unset($emailConfig['to_emails'][$index]); // 重新索引数组 $emailConfig['to_emails'] = array_values($emailConfig['to_emails']); // 保存配置 if (FileManager::saveJsonFile($emailConfigFile, $emailConfig)) { // 记录操作日志 $this->logOperation('删除邮箱收件人', ['email' => $deleteEmail]); echo json_encode([ 'success' => true, 'message' => '邮箱收件人删除成功', 'data' => $emailConfig['to_emails'] ]); } else { echo json_encode([ 'success' => false, 'message' => '邮箱收件人删除失败' ]); } } else { echo json_encode([ 'success' => false, 'message' => '邮箱配置文件不存在' ]); } } } // 错误处理 try { $api = new FundAdminAPI(); $api->handleRequest(); } catch (Exception $e) { http_response_code(500); echo json_encode([ 'success' => false, 'message' => '服务器内部错误: ' . $e->getMessage() ]); } ?>