参考文献作者堆叠
参考文献相关性检测 作者ai写作辅助检测工作
This commit is contained in:
299
application/api/controller/AITools.php
Normal file
299
application/api/controller/AITools.php
Normal file
@@ -0,0 +1,299 @@
|
||||
<?php
|
||||
/**
|
||||
* AI 工具接口 — 编辑辅助:AI 辅助写作风险分析
|
||||
*/
|
||||
|
||||
namespace app\api\controller;
|
||||
|
||||
use app\common\ArticleParserService;
|
||||
use app\common\mq\AiWritingRiskMqPublisher;
|
||||
use app\common\PlagiarismService;
|
||||
use app\common\service\AiWritingRiskTaskService;
|
||||
use think\Validate;
|
||||
|
||||
class Aitools extends Base
|
||||
{
|
||||
public function __construct(\think\Request $request = null)
|
||||
{
|
||||
parent::__construct($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* @title 写作风险检测可视化页面(前后端分离入口)
|
||||
* @description 仅渲染 HTML;检测与进度走 JSON 接口
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
return $this->renderRiskPage();
|
||||
}
|
||||
|
||||
/**
|
||||
* @title 开始 AI 辅助写作风险检测(异步入队)
|
||||
* @description 立即返回 task_id;后台 RabbitMQ 消费执行检测
|
||||
* @param url 稿件链接地址(.docx)
|
||||
* @param p_article_id 可选,业务稿件ID
|
||||
*/
|
||||
public function realtimeWritingRiskDetect()
|
||||
{
|
||||
$data = $this->request->param();
|
||||
$rule = new Validate([
|
||||
'url' => 'require',
|
||||
]);
|
||||
if (!$rule->check($data)) {
|
||||
return $this->detectJsonError($rule->getError());
|
||||
}
|
||||
$url = trim((string) $data['url']);
|
||||
$pArticleId = intval($data['p_article_id'] ?? 0);
|
||||
try {
|
||||
$taskService = new AiWritingRiskTaskService();
|
||||
$created = $taskService->createTask($url, $pArticleId);
|
||||
// 新任务,或复用但仍在排队(可能丢消息)时重新投递
|
||||
if (empty($created['reused']) || intval($created['status']) === AiWritingRiskTaskService::STATUS_PENDING) {
|
||||
(new AiWritingRiskMqPublisher())->publishTaskStart(intval($created['task_id']), 'enqueue');
|
||||
}
|
||||
|
||||
return json([
|
||||
'code' => 200,
|
||||
'msg' => !empty($created['reused']) ? '已有进行中的检测任务' : '已开始检测',
|
||||
'data' => [
|
||||
'task_id' => $created['task_id'],
|
||||
'task_no' => $created['task_no'],
|
||||
'status' => $created['status'],
|
||||
'status_text' => intval($created['status']) === 1 ? 'running' : 'queued',
|
||||
'progress_percent' => 0,
|
||||
'message' => $created['message'],
|
||||
'reused' => !empty($created['reused']),
|
||||
],
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
return $this->detectJsonError($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @title 刷新 AI 辅助写作风险检测进度
|
||||
* @description
|
||||
* - 浏览器 GET(无 format=json)→ 可视化页面:未完成显示进度,完成后显示结果
|
||||
* - POST 或 format=json → JSON(供前端轮询)
|
||||
* @param task_id 任务ID(与 task_no 二选一)
|
||||
* @param task_no 任务号(与 task_id 二选一)
|
||||
*/
|
||||
public function realtimeWritingRiskProgress()
|
||||
{
|
||||
$data = $this->request->param();
|
||||
$taskId = intval($data['task_id'] ?? 0);
|
||||
$taskNo = trim((string) ($data['task_no'] ?? ''));
|
||||
if ($taskId <= 0 && $taskNo === '') {
|
||||
if ($this->wantsProgressJson()) {
|
||||
return $this->detectJsonError('task_id 或 task_no 必填');
|
||||
}
|
||||
return $this->renderRiskPage();
|
||||
}
|
||||
|
||||
try {
|
||||
$progress = (new AiWritingRiskTaskService())->getProgress($taskNo, $taskId);
|
||||
} catch (\Exception $e) {
|
||||
if ($this->wantsProgressJson()) {
|
||||
return $this->detectJsonError($e->getMessage());
|
||||
}
|
||||
return $this->renderRiskPage([
|
||||
'page_error' => $e->getMessage(),
|
||||
'task_id' => $taskId,
|
||||
'task_no' => $taskNo,
|
||||
]);
|
||||
}
|
||||
|
||||
if ($this->wantsProgressJson()) {
|
||||
return json([
|
||||
'code' => 200,
|
||||
'msg' => 'success',
|
||||
'data' => $progress,
|
||||
]);
|
||||
}
|
||||
|
||||
return $this->renderRiskPage([
|
||||
'task_id' => intval($progress['task_id'] ?? $taskId),
|
||||
'task_no' => (string) ($progress['task_no'] ?? $taskNo),
|
||||
'initial_data' => $progress,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否返回 JSON:POST、format=json、XHR/fetch 显式要 JSON
|
||||
*/
|
||||
private function wantsProgressJson(): bool
|
||||
{
|
||||
if (strtolower((string) $this->request->param('format', '')) === 'json') {
|
||||
return true;
|
||||
}
|
||||
if ($this->request->isAjax()) {
|
||||
return true;
|
||||
}
|
||||
$accept = strtolower((string) $this->request->header('accept', ''));
|
||||
if (strpos($accept, 'application/json') !== false) {
|
||||
return true;
|
||||
}
|
||||
// 页面内 fetch 使用 POST
|
||||
if ($this->request->isPost()) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 渲染可视化页
|
||||
* @param array $opts task_id/task_no/initial_data/page_error
|
||||
*/
|
||||
private function renderRiskPage(array $opts = [])
|
||||
{
|
||||
$apiStart = (string) url('api/aitools/realtimeWritingRiskDetect');
|
||||
$apiProgress = (string) url('api/aitools/realtimeWritingRiskProgress');
|
||||
$initial = isset($opts['initial_data']) && is_array($opts['initial_data'])
|
||||
? $opts['initial_data']
|
||||
: null;
|
||||
|
||||
$this->assign([
|
||||
'api_start_js' => json_encode($apiStart, JSON_UNESCAPED_SLASHES),
|
||||
'api_progress_js' => json_encode($apiProgress, JSON_UNESCAPED_SLASHES),
|
||||
'init_task_id_js' => json_encode(intval($opts['task_id'] ?? 0)),
|
||||
'init_task_no_js' => json_encode((string) ($opts['task_no'] ?? ''), JSON_UNESCAPED_UNICODE),
|
||||
'init_data_js' => json_encode($initial, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES),
|
||||
'page_error_js' => json_encode((string) ($opts['page_error'] ?? ''), JSON_UNESCAPED_UNICODE),
|
||||
]);
|
||||
return $this->fetch('aiTools/index');
|
||||
}
|
||||
|
||||
/**
|
||||
* @title AI 辅助写作风险分析(编辑辅助)
|
||||
* @description 解析稿件结构,并从模版句、重复短语、句长特征等维度输出风险报告
|
||||
* @param url 稿件链接地址(.docx)
|
||||
*/
|
||||
public function writingRiskAnalysis()
|
||||
{
|
||||
$data = $this->request->param();
|
||||
$rule = new Validate([
|
||||
'url' => 'require',
|
||||
]);
|
||||
if (!$rule->check($data)) {
|
||||
return jsonError($rule->getError());
|
||||
}
|
||||
|
||||
try {
|
||||
$filePath = $this->resolveManuscriptUrl($data['url']);
|
||||
} catch (\Exception $e) {
|
||||
return jsonError($e->getMessage());
|
||||
}
|
||||
|
||||
$result = ArticleParserService::analyzeWritingRisk($filePath);
|
||||
if (empty($result['status']) || intval($result['status']) !== 1) {
|
||||
return jsonError(empty($result['msg']) ? '稿件分析失败' : $result['msg']);
|
||||
}
|
||||
|
||||
return jsonSuccess([
|
||||
'manuscript' => $this->pickManuscriptFields($result['data']),
|
||||
'risk_analysis' => $result['data']['risk_analysis'] ?? [],
|
||||
'metrics' => $this->pickMetricFields($result['data']),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @title 根据稿件链接解析稿件结构
|
||||
* @description 从稿件地址解析标题、摘要、关键词及 IMRaD 正文分节(不含风险综述)
|
||||
* @param url 稿件链接地址(.docx)
|
||||
*/
|
||||
public function parseManuscript()
|
||||
{
|
||||
$data = $this->request->param();
|
||||
$rule = new Validate([
|
||||
'url' => 'require',
|
||||
]);
|
||||
if (!$rule->check($data)) {
|
||||
return jsonError($rule->getError());
|
||||
}
|
||||
|
||||
try {
|
||||
$filePath = $this->resolveManuscriptUrl($data['url']);
|
||||
} catch (\Exception $e) {
|
||||
return jsonError($e->getMessage());
|
||||
}
|
||||
|
||||
$result = ArticleParserService::parseManuscriptStructure($filePath);
|
||||
if (empty($result['status']) || intval($result['status']) !== 1) {
|
||||
return jsonError(empty($result['msg']) ? '稿件解析失败' : $result['msg']);
|
||||
}
|
||||
|
||||
return jsonSuccess($result['data']);
|
||||
}
|
||||
|
||||
private function detectJsonError($msg)
|
||||
{
|
||||
return json(['code' => 500, 'msg' => (string) $msg, 'data' => []]);
|
||||
}
|
||||
|
||||
private function pickManuscriptFields(array $data): array
|
||||
{
|
||||
$keys = ['title', 'abstract', 'keywords', 'Introduction', 'Methods', 'Results', 'Discussion', 'Conclusion', 'References'];
|
||||
$out = [];
|
||||
foreach ($keys as $key) {
|
||||
if (array_key_exists($key, $data)) {
|
||||
$out[$key] = $data[$key];
|
||||
}
|
||||
}
|
||||
return $out;
|
||||
}
|
||||
|
||||
private function pickMetricFields(array $data): array
|
||||
{
|
||||
$keys = [
|
||||
'sentence_stats',
|
||||
'sentence_stats_by_section',
|
||||
'repeated_phrases',
|
||||
'repeated_phrases_by_section',
|
||||
'template_sentence_stats',
|
||||
];
|
||||
$out = [];
|
||||
foreach ($keys as $key) {
|
||||
if (array_key_exists($key, $data)) {
|
||||
$out[$key] = $data[$key];
|
||||
}
|
||||
}
|
||||
return $out;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将稿件链接解析为本地可读文件路径
|
||||
*/
|
||||
private function resolveManuscriptUrl($url)
|
||||
{
|
||||
$url = trim((string)$url);
|
||||
if ($url === '') {
|
||||
throw new \Exception('稿件地址不能为空');
|
||||
}
|
||||
|
||||
if (preg_match('/^([a-zA-Z]:[\\\\\/]|\/)/', $url) && is_file($url)) {
|
||||
return $url;
|
||||
}
|
||||
|
||||
if (preg_match('#^https?://#i', $url)) {
|
||||
try {
|
||||
return (new PlagiarismService())->resolveFileUrlToLocal($url);
|
||||
} catch (\Exception $e) {
|
||||
$path = parse_url($url, PHP_URL_PATH);
|
||||
if ($path) {
|
||||
$local = rtrim(ROOT_PATH, '/') . $path;
|
||||
if (is_file($local)) {
|
||||
return $local;
|
||||
}
|
||||
}
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
$local = rtrim(ROOT_PATH, '/') . '/public/' . ltrim(ltrim($url, '/'), 'public');
|
||||
if (is_file($local)) {
|
||||
return $local;
|
||||
}
|
||||
|
||||
throw new \Exception('无法解析稿件文件路径: ' . $url);
|
||||
}
|
||||
}
|
||||
@@ -272,6 +272,14 @@ class Base extends Controller
|
||||
$this->production_article_refer_obj->where('p_article_id', $refer_info['p_article_id'])->where('index', ">", $refer_info['index'])->where('state', 0)->setDec('index');
|
||||
$this->production_article_refer_obj->where('p_refer_id', $p_refer_id)->update(['state' => 1]);
|
||||
|
||||
try {
|
||||
Db::name('production_article_refer_author')
|
||||
->where('p_refer_id', intval($p_refer_id))
|
||||
->delete();
|
||||
} catch (\Exception $e) {
|
||||
\think\Log::error('delOneRefer delete refer_author p_refer_id=' . $p_refer_id . ' ' . $e->getMessage());
|
||||
}
|
||||
|
||||
// 文献集合已变更,原校对结果的 reference_no 已全部错位,整篇标记为未校对
|
||||
try {
|
||||
(new \app\common\ReferenceCheckService())
|
||||
|
||||
@@ -7,6 +7,8 @@ use think\Env;
|
||||
use think\Queue;
|
||||
use think\Validate;
|
||||
use app\common\CrossrefService;
|
||||
use app\common\ReferenceCheckService;
|
||||
use app\common\ReferenceReferAuthorService;
|
||||
use app\common\ReferenceRelevanceCheckService;
|
||||
|
||||
class Preaccept extends Base
|
||||
@@ -36,6 +38,78 @@ class Preaccept extends Base
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增参考文献后同步作者明细到 t_production_article_refer_author
|
||||
*/
|
||||
private function syncReferAuthorsAfterInsert($pReferId, $pArticleId, array $refer)
|
||||
{
|
||||
$pReferId = intval($pReferId);
|
||||
$pArticleId = intval($pArticleId);
|
||||
if ($pReferId <= 0 || $pArticleId <= 0) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
(new ReferenceReferAuthorService())->syncOneRefer($pReferId, $pArticleId, $refer);
|
||||
} catch (\Exception $e) {
|
||||
\think\Log::error(
|
||||
'syncReferAuthorsAfterInsert p_refer_id=' . $pReferId
|
||||
. ' p_article_id=' . $pArticleId . ' ' . $e->getMessage()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 编辑参考文献后同步作者明细;refer_doi 变化时先删后重新抓取入库
|
||||
*/
|
||||
private function syncReferAuthorsAfterUpdate($pReferId, $pArticleId, array $oldRefer, array $updata)
|
||||
{
|
||||
$pReferId = intval($pReferId);
|
||||
$pArticleId = intval($pArticleId);
|
||||
if ($pReferId <= 0 || $pArticleId <= 0 || empty($oldRefer)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$newRefer = array_merge($oldRefer, $updata);
|
||||
$refUtil = new ReferenceCheckService();
|
||||
$oldDoi = $this->normalizeReferDoi($refUtil->extractDoiFromRefer($oldRefer));
|
||||
$newDoi = $this->normalizeReferDoi($refUtil->extractDoiFromRefer($newRefer));
|
||||
$doiChanged = $oldDoi !== $newDoi;
|
||||
|
||||
$authorChanged = false;
|
||||
if (array_key_exists('author', $updata)) {
|
||||
$authorChanged = trim((string)($oldRefer['author'] ?? '')) !== trim((string)$updata['author']);
|
||||
}
|
||||
|
||||
if (!$doiChanged && !$authorChanged) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
$svc = new ReferenceReferAuthorService();
|
||||
if ($doiChanged) {
|
||||
Db::name('production_article_refer_author')->where('p_refer_id', $pReferId)->delete();
|
||||
$svc->syncOneRefer($pReferId, $pArticleId, $newRefer);
|
||||
} elseif ($authorChanged) {
|
||||
$svc->syncFromReferAuthorField(
|
||||
$pReferId,
|
||||
$pArticleId,
|
||||
(string)($newRefer['author'] ?? '')
|
||||
);
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
\think\Log::error(
|
||||
'syncReferAuthorsAfterUpdate p_refer_id=' . $pReferId
|
||||
. ' p_article_id=' . $pArticleId . ' ' . $e->getMessage()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private function normalizeReferDoi($doi)
|
||||
{
|
||||
$doi = preg_replace('#^https?://(dx\.)?doi\.org/#i', '', trim((string)$doi));
|
||||
return strtolower(trim($doi, " \t\n\r\0\x0B/"));
|
||||
}
|
||||
|
||||
|
||||
/**获取文章参考文献列表
|
||||
* @return \think\response\Json
|
||||
@@ -165,6 +239,11 @@ class Preaccept extends Base
|
||||
$adId= $this->production_article_refer_obj->insertGetId($insert);
|
||||
$this->production_article_refer_obj->where('p_article_id', $p_info['p_article_id'])->where("p_refer_id", "<>", $adId)->where("index", ">", $pre_refer['index'])->where('state', 0)->setInc('index');
|
||||
$this->resetArticleChecksOnReferChange(intval($p_info['p_article_id']), 'addRefer');
|
||||
$this->syncReferAuthorsAfterInsert(
|
||||
$adId,
|
||||
intval($p_info['p_article_id']),
|
||||
array_merge($insert, ['p_refer_id' => $adId, 'p_article_id' => $p_info['p_article_id']])
|
||||
);
|
||||
return jsonSuccess([]);
|
||||
|
||||
|
||||
@@ -222,6 +301,12 @@ class Preaccept extends Base
|
||||
$adId= $this->production_article_refer_obj->insertGetId($insert);
|
||||
$this->production_article_refer_obj->where('p_article_id', $p_info['p_article_id'])->where("p_refer_id", "<>", $adId)->where("index", ">", $pre_refer['index'])->where('state', 0)->setInc('index');
|
||||
$this->resetArticleChecksOnReferChange(intval($p_info['p_article_id']), 'addReferByParticleid');
|
||||
$this->syncReferAuthorsAfterInsert(
|
||||
$adId,
|
||||
intval($p_info['p_article_id']),
|
||||
array_merge($insert, ['p_refer_id' => $adId, 'p_article_id' => $p_info['p_article_id']])
|
||||
);
|
||||
|
||||
return jsonSuccess([]);
|
||||
}
|
||||
|
||||
@@ -498,6 +583,13 @@ class Preaccept extends Base
|
||||
\think\Log::error('editRefer enqueueRecheckByPReferId p_refer_id=' . $data['p_refer_id'] . ' ' . $e->getMessage());
|
||||
}
|
||||
|
||||
$this->syncReferAuthorsAfterUpdate(
|
||||
intval($data['p_refer_id']),
|
||||
intval($old_refer_info['p_article_id']),
|
||||
$old_refer_info,
|
||||
$updata
|
||||
);
|
||||
|
||||
return jsonSuccess([]);
|
||||
}
|
||||
|
||||
|
||||
@@ -13,6 +13,8 @@ use think\Env;
|
||||
use think\Queue;
|
||||
use app\common\ReferenceCheckService;
|
||||
use app\common\ReferenceRelevanceCheckService;
|
||||
use app\common\ReferenceStackingStatsService;
|
||||
use app\common\ReferenceReferAuthorService;
|
||||
use app\common\DbReconnectHelper;
|
||||
/**
|
||||
* @title 参考文献
|
||||
@@ -1818,5 +1820,117 @@ class References extends Base
|
||||
return $frag;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 参考文献引用堆叠统计(同作者>15%、同刊>20%、自引>10%,实时计算)
|
||||
*
|
||||
* POST/GET: p_article_id(必填)
|
||||
*/
|
||||
public function referenceStackingStats()
|
||||
{
|
||||
$aParam = $this->request->post();
|
||||
if (empty($aParam)) {
|
||||
$aParam = $this->request->param();
|
||||
}
|
||||
|
||||
$iPArticleId = empty($aParam['p_article_id']) ? 0 : intval($aParam['p_article_id']);
|
||||
if ($iPArticleId <= 0) {
|
||||
return json_encode(array('status' => 2, 'msg' => 'Please select an article'));
|
||||
}
|
||||
|
||||
try {
|
||||
$svc = new ReferenceStackingStatsService();
|
||||
$result = $svc->getThresholdStackingByPArticleId($iPArticleId);
|
||||
return jsonSuccess($result);
|
||||
} catch (\Exception $e) {
|
||||
return jsonError($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试:同步参考文献作者明细到 t_production_article_refer_author
|
||||
* POST/GET: p_article_id(必填), p_refer_id(可选,仅同步单条), include_authors(可选,默认 0;1 返回作者明细), sleep_ms(可选,默认 120)
|
||||
*/
|
||||
public function referenceReferAuthorSyncTest()
|
||||
{
|
||||
$aParam = $this->request->post();
|
||||
if (empty($aParam)) {
|
||||
$aParam = $this->request->param();
|
||||
}
|
||||
|
||||
$iPArticleId = empty($aParam['p_article_id']) ? 0 : intval($aParam['p_article_id']);
|
||||
if ($iPArticleId <= 0) {
|
||||
return json_encode(array('status' => 2, 'msg' => 'Please select an article'));
|
||||
}
|
||||
|
||||
$iPReferId = empty($aParam['p_refer_id']) ? 0 : intval($aParam['p_refer_id']);
|
||||
$includeAuthors = !empty($aParam['include_authors']) && intval($aParam['include_authors']) === 1;
|
||||
$sleepMs = isset($aParam['sleep_ms']) ? max(0, intval($aParam['sleep_ms'])) : 120;
|
||||
|
||||
try {
|
||||
$svc = new ReferenceReferAuthorService();
|
||||
|
||||
if ($iPReferId > 0) {
|
||||
$refer = Db::name('production_article_refer')
|
||||
->where('p_refer_id', $iPReferId)
|
||||
->where('p_article_id', $iPArticleId)
|
||||
->where('state', 0)
|
||||
->find();
|
||||
if (empty($refer)) {
|
||||
return jsonError('Reference not found');
|
||||
}
|
||||
|
||||
$refUtil = new ReferenceCheckService();
|
||||
$doi = $refUtil->extractDoiFromRefer($refer);
|
||||
$count = 0;
|
||||
if ($doi !== '') {
|
||||
$crossref = new CrossrefService([
|
||||
'mailto' => trim((string)Env::get('crossref_mailto', '')),
|
||||
]);
|
||||
$summary = $crossref->fetchWorkSummary($doi);
|
||||
if ($summary === null || empty($summary['doi'])) {
|
||||
$summary = ['doi' => $doi, 'raw' => []];
|
||||
}
|
||||
$count = $svc->syncFromWorkSummary($iPReferId, $iPArticleId, $doi, $summary);
|
||||
}
|
||||
if ($count <= 0) {
|
||||
$count = $svc->syncFromReferAuthorField($iPReferId, $iPArticleId, (string)($refer['author'] ?? ''));
|
||||
}
|
||||
|
||||
$result = [
|
||||
'p_article_id' => $iPArticleId,
|
||||
'p_refer_id' => $iPReferId,
|
||||
'reference_no' => intval($refer['index']) + 1,
|
||||
'doi' => $doi,
|
||||
'authors' => $count,
|
||||
'synced' => $count > 0 ? 1 : 0,
|
||||
'failed' => $count > 0 ? 0 : 1,
|
||||
];
|
||||
} else {
|
||||
$result = $svc->syncByPArticleId($iPArticleId, ['sleep_ms' => $sleepMs]);
|
||||
}
|
||||
|
||||
if ($includeAuthors) {
|
||||
$query = Db::name('production_article_refer_author')
|
||||
->alias('a')
|
||||
->join('production_article_refer r', 'r.p_refer_id = a.p_refer_id', 'LEFT')
|
||||
->field('a.p_refer_id,r.index,a.author_seq,a.display_name,a.citation_name,a.orcid,a.openalex_id,a.identity_source')
|
||||
->where('a.p_article_id', $iPArticleId);
|
||||
if ($iPReferId > 0) {
|
||||
$query->where('a.p_refer_id', $iPReferId);
|
||||
}
|
||||
$rows = $query->order('r.index asc, a.author_seq asc')->select();
|
||||
foreach ($rows as &$row) {
|
||||
$row['reference_no'] = intval($row['index']) + 1;
|
||||
unset($row['index']);
|
||||
}
|
||||
unset($row);
|
||||
$result['author_rows'] = $rows;
|
||||
}
|
||||
|
||||
return jsonSuccess($result);
|
||||
} catch (\Exception $e) {
|
||||
return jsonError($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user