From 987197ea3cd83acfe6f23c110df07c143ac6421f Mon Sep 17 00:00:00 2001 From: wangjinlei <751475802@qq.com> Date: Wed, 17 Dec 2025 15:54:45 +0800 Subject: [PATCH] =?UTF-8?q?bib=E6=96=87=E4=BB=B6=E7=94=9F=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- application/api/controller/Production.php | 391 ++++++++++++++++++++++ 1 file changed, 391 insertions(+) diff --git a/application/api/controller/Production.php b/application/api/controller/Production.php index 87e34f4..679955e 100644 --- a/application/api/controller/Production.php +++ b/application/api/controller/Production.php @@ -2015,6 +2015,397 @@ class Production extends Base } + + public function mytestbib(){ + $data = $this->request->post(); + $rule = new Validate([ + "article_id"=>"require" + ]); + if(!$rule->check($data)){ + return jsonError($rule->getError()); + } + $file = $this->generateBibFile($data['article_id']); + return jsonSuccess(['file'=>$file]); + } + + + /** + * 生成指定文章的 BibTeX 文件 + * @param int $p_article_id 生产文章ID + * @param string $outputPath 输出文件路径(可选) + * @return string 生成的文件路径 + */ + public function generateBibFile($article_id, $outputPath = null) + { + $production_article_obj = $this->production_article_obj->where("article_id",$article_id)->find(); + if($production_article_obj==null){ + $this->addProductionEx($article_id); + $production_article_obj = $this->production_article_obj->where("article_id",$article_id)->find(); + } + if (!$outputPath) { + $outputPath = ROOT_PATH . 'public' . DS . 'latex' . DS . 'tex_'.$article_id . DS .'references_' . $article_id . '.bib'; + } + $dir = dirname($outputPath); + if (!is_dir($dir)) { + mkdir($dir, 0755, true); + } + $bibContent = $this->generateBibContent($production_article_obj['p_article_id']); + file_put_contents($outputPath, $bibContent); + + return $outputPath; + } + + /** + * 获取参考文献数据 + * @param int $p_article_id 生产文章ID + * @return array 参考文献数组 + */ + private function getReferences($p_article_id) + { + return $this->production_article_refer_obj->where("p_article_id",$p_article_id)->where("state",0)->select(); +// $sql = "SELECT * FROM t_production_article_refer +// WHERE p_article_id = :p_article_id +// AND state = 0 +// ORDER BY `index`"; +// +// $stmt = $this->db->prepare($sql); +// $stmt->bindParam(':p_article_id', $p_article_id, PDO::PARAM_INT); +// $stmt->execute(); +// +// return $stmt->fetchAll(PDO::FETCH_ASSOC); + } + + /** + * 生成 BibTeX 内容 + * @param array $references 参考文献数据 + * @param int $p_article_id 文章ID + * @return string BibTeX 内容 + */ + private function generateBibContent( $p_article_id) + { + $content = "% BibTeX file generated for article ID: {$p_article_id}\n"; + $content .= "% Generated on " . date('Y-m-d H:i:s') . "\n\n"; + $references = $this->production_article_refer_obj->where("p_article_id",$p_article_id)->where("state",0)->select(); + foreach ($references as $ref) { + $entry = $this->generateBibEntry($ref); + if ($entry) { + $content .= $entry . "\n\n"; + } + } + + return $content; + } + + /** + * 生成单个 BibTeX 条目 + * @param array $ref 参考文献数据 + * @return string|null BibTeX 条目或 null + */ + private function generateBibEntry($ref) + { + // 使用 DOI 或索引作为 citation key + $citationKey = $this->generateCitationKey($ref); + + // 如果有解析后的作者和标题信息,优先使用 + if (!empty($ref['author']) && !empty($ref['title'])) { + return $this->generateFromParsedData($ref, $citationKey); + } + // 否则尝试从原始内容解析 + else { + return ; +// return $this->generateFromRawContent($ref, $citationKey); + } + } + + /** + * 生成 citation key + * @param array $ref 参考文献数据 + * @return string citation key + */ + private function generateCitationKey($ref) + { + if (!empty($ref['refer_doi'])) { + // 使用 DOI 的部分作为 key + $parts = explode('/', $ref['refer_doi']); + if (count($parts) > 1) { + return str_replace('.', '_', end($parts)); + } + } + + // 默认使用索引 + return 'ref_' . $ref['p_refer_id']; + } + private function parsePublicationInfo($referenceText) + { + $result = [ + 'year' => '', + 'volume' => '', + 'number' => '', + 'pages' => '' + ]; + + // 匹配模式:year;volume(number):pages + $pattern = '/(\d{4})\s*;\s*(\d+)(?:\(([^)]+)\))?(?:\s*:\s*([a-zA-Z0-9\u2013\u2014\-]+(?:\s*[\u2013\u2014\-]\s*[a-zA-Z0-9]+)?))?/'; + + + if (preg_match($pattern, $referenceText, $matches)) { + $result['year'] = isset($matches[1]) ? $matches[1] : ''; + $result['volume'] = isset($matches[2]) ? $matches[2] : ''; + + // 期号可能是可选的 + if (isset($matches[3]) && $matches[3] !== '') { + $result['number'] = $matches[3]; + } + + // 页码也是可选的 + if (isset($matches[4]) && $matches[4] !== '') { + $result['pages'] = $matches[4]; + } + } else { + // 如果标准模式不匹配,尝试更宽松的匹配方式 + $result = self::parseLooseFormat($referenceText); + } + + return $result; + } + + private function parseLooseFormat($referenceText) + { + $result = [ + 'year' => '', + 'volume' => '', + 'number' => '', + 'pages' => '' + ]; + + // 提取年份(4位数字) + if (preg_match('/\b(\d{4})\b/', $referenceText, $yearMatches)) { + $result['year'] = $yearMatches[1]; + } + + // 提取卷号和期号(格式如 54(4) 或 54 (4)) + if (preg_match('/(\d+)\s*\(?(\d*)\)?\s*:?\s*([a-zA-Z0-9\-]*)/', $referenceText, $volMatches)) { + if (isset($volMatches[1])) { + $result['volume'] = $volMatches[1]; + } + + if (isset($volMatches[2]) && $volMatches[2] !== '') { + $result['number'] = $volMatches[2]; + } + + if (isset($volMatches[3]) && $volMatches[3] !== '') { + $result['pages'] = $volMatches[3]; + } + } + + return $result; + } + + + + /** + * 从解析后的数据生成 BibTeX 条目 + * @param array $ref 参考文献数据 + * @param string $citationKey citation key + * @return string BibTeX 条目 + */ + private function generateFromParsedData($ref, $citationKey) + { + $entry = "@article{{$citationKey},\n"; + + // 添加已解析的字段 + if (!empty($ref['author'])) { + $entry .= " author={" . $this->escapeBibTeX($this->formatBibTeXAuthors($ref['author'])) . "},\n"; + } + + if (!empty($ref['title'])) { + $entry .= " title={" . $this->escapeBibTeX($ref['title']) . "},\n"; + } + + if (!empty($ref['joura'])) { + $entry .= " journal={" . $this->escapeBibTeX($ref['joura']) . "},\n"; + } + + // 解析并添加出版信息 + if (!empty($ref['dateno'])) { + $pubInfo = $this->parsePublicationInfo($ref['dateno']); + + if (!empty($pubInfo['year'])) { + $entry .= " year={" . $this->escapeBibTeX($pubInfo['year']) . "},\n"; + } + + if (!empty($pubInfo['volume'])) { + $entry .= " volume={" . $this->escapeBibTeX($pubInfo['volume']) . "},\n"; + } + + if (!empty($pubInfo['number'])) { + $entry .= " number={" . $this->escapeBibTeX($pubInfo['number']) . "},\n"; + } + + if (!empty($pubInfo['pages'])) { + $entry .= " pages={" . $this->escapeBibTeX($pubInfo['pages']) . "},\n"; + } + } + + if (!empty($ref['refer_doi'])) { + $entry .= " doi={" . $this->escapeBibTeX($ref['refer_doi']) . "},\n"; + } + + // 移除最后一个逗号并关闭条目 + $entry = rtrim($entry, ",\n") . "\n}"; + + return $entry; + } + + + /** + * 格式化作者信息为BibTeX格式 + * @param string $authors 作者字符串,如 "Li Y, Yang C, Zhao H, Qu S, Li X, Li Y" + * @return string 格式化后的作者字符串,如 "Li, Y. and Yang, C. and Zhao, H. and Qu, S. and Li, X. and Li, Y." + */ + private function formatBibTeXAuthors($authors) { + $authors = rtrim($authors, ". \t\n\r\0\x0B"); + // 如果包含"et al",则原样保留 + if (stripos($authors, 'et al') !== false) { + return $authors; + } + + // 按逗号分割各个作者 + $authorArray = array_map('trim', explode(',', $authors)); + $formattedAuthors = []; + + foreach ($authorArray as $author) { + $author = trim($author); + if (empty($author)) { + continue; + } + + // 按空格分割作者的各部分 + $authorParts = explode(' ', $author); + if (count($authorParts) >= 2) { + // 最后一个是姓氏,前面的是名字首字母 + $lastName = array_pop($authorParts); + $firstName = implode(' ', $authorParts); + $formattedAuthors[] = $firstName.", ".$lastName; +// $firstNameInitials = implode('. ', $authorParts) . '.'; +// $formattedAuthors[] = $lastName . ', ' . $firstNameInitials; + } else { + // 如果只有一个部分,当作姓氏处理 + $formattedAuthors[] = $author; + } + } + + // 用 " and " 连接所有作者 + return implode(' and ', $formattedAuthors); + } + + /** + * 从原始内容生成 BibTeX 条目(简单处理) + * @param array $ref 参考文献数据 + * @param string $citationKey citation key + * @return string BibTeX 条目 + */ + private function generateFromRawContent($ref, $citationKey) + { + $entry = "@misc{{$citationKey},\n"; + + // 将整个参考文献内容作为 note 字段 + if (!empty($ref['refer_content'])) { + $entry .= " note={" . $this->escapeBibTeX($ref['refer_content']) . "},\n"; + } + + if (!empty($ref['refer_doi'])) { + $entry .= " doi={" . $this->escapeBibTeX($ref['refer_doi']) . "},\n"; + } + + // 移除最后一个逗号并关闭条目 + $entry = rtrim($entry, ",\n") . "\n}"; + + return $entry; + } + + /** + * 转义 BibTeX 特殊字符 + * @param string $text 文本内容 + * @return string 转义后的内容 + */ + private function escapeBibTeX($text) + { + // 转义大括号 + $text = str_replace(['{', '}'], ['\\{', '\\}'], $text); + // 转义反斜杠 + $text = str_replace('\\', '\\\\', $text); + return $text; + } + + +// 在 Production 控制器中添加以下方法: + +// 在 Production.php 中添加以下方法: + +/** + * 生成 BibTeX 文件 + */ +public function generateBibtex() +{ + $data = $this->request->post(); + $rule = new Validate([ + 'p_article_id' => 'require|number' + ]); + + if (!$rule->check($data)) { + return jsonError($rule->getError()); + } + + try { + // 创建数据库连接 + $db = Db::connect(); + $generator = new ReferenceBibGenerator($db); + + // 生成 BibTeX 文件 + $filePath = $generator->generateBibFile($data['p_article_id']); + + // 返回相对路径供前端使用 + $relativePath = str_replace(ROOT_PATH . 'public' . DS, '', $filePath); + + return jsonSuccess([ + 'file_path' => $relativePath, + 'full_path' => $filePath + ]); + + } catch (Exception $e) { + return jsonError('生成 BibTeX 文件失败: ' . $e->getMessage()); + } +} + +/** + * 下载 BibTeX 文件 + */ +public function downloadBibtex() +{ + $p_article_id = $this->request->param('p_article_id', 0); + + if (!$p_article_id) { + return jsonError('缺少参数'); + } + + $filePath = ROOT_PATH . 'public' . DS . 'bibtex' . DS . 'references_' . $p_article_id . '.bib'; + + if (!file_exists($filePath)) { + return jsonError('文件不存在'); + } + + // 设置下载头信息 + header('Content-Type: application/x-bibtex'); + header('Content-Disposition: attachment; filename="references_' . $p_article_id . '.bib"'); + header('Content-Length: ' . filesize($filePath)); + + // 输出文件内容 + readfile($filePath); + exit; +} + + public function getFragBF() { $data = $this->request->post();