bib文件生成
This commit is contained in:
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user