diff --git a/application/api/controller/Author.php b/application/api/controller/Author.php index f024a1f2..fa55016e 100644 --- a/application/api/controller/Author.php +++ b/application/api/controller/Author.php @@ -302,7 +302,8 @@ class Author extends Controller private function resolveFormAction() { - return rtrim($this->request->root(), '/') . '/api/author/index'; + // 生产环境未配置伪静态时需带 index.php,如 /public/index.php/api/author/index + return rtrim($this->request->baseFile(), '/') . '/api/author/index'; } private function renderReportPage(array $params, $formAction) diff --git a/application/api/controller/EmailClient.php b/application/api/controller/EmailClient.php index 8fda8ff3..b243f43b 100644 --- a/application/api/controller/EmailClient.php +++ b/application/api/controller/EmailClient.php @@ -2141,9 +2141,12 @@ class EmailClient extends Base $where['l.state'] = intval($state); } if ($keyword !== '') { - $where['e.email|e.name'] = ['like',"%".trim($keyword)."%"]; + $where['e.email|e.name'] = ['like', '%' . $keyword . '%']; } - $total = Db::name('promotion_email_log')->alias('l')->where($where)->count(); + $total = Db::name('promotion_email_log')->alias('l') + ->join('t_expert e', 'l.expert_id = e.expert_id', 'LEFT') + ->where($where) + ->count('l.log_id'); $list = Db::name('promotion_email_log')->alias('l') ->join('t_expert e', 'l.expert_id = e.expert_id', 'LEFT') ->where($where) diff --git a/application/api/controller/ExpertManage.php b/application/api/controller/ExpertManage.php index bd3a0fea..b8c473d9 100644 --- a/application/api/controller/ExpertManage.php +++ b/application/api/controller/ExpertManage.php @@ -39,6 +39,7 @@ class ExpertManage extends Base $field = trim(isset($data['field']) ? $data['field'] : ''); $state = isset($data['state']) ? $data['state'] : '-1'; $source = trim(isset($data['source']) ? $data['source'] : ''); + $country = trim(isset($data['country']) ? $data['country'] : ''); $page = max(1, intval(isset($data['pageIndex']) ? $data['pageIndex'] : 1)); $pageSize = max(1, intval(isset($data['pageSize']) ? $data['pageSize'] : 20)); @@ -74,6 +75,10 @@ class ExpertManage extends Base $query->where('e.source', $source); $countQuery->where('e.source', $source); } + if ($country !== '') { + $query->where('e.country', $country); + $countQuery->where('e.country', $country); + } // $countQuery = clone $query; // $total = $countQuery->distinct('e.expert_id')->count(); diff --git a/application/api/controller/User.php b/application/api/controller/User.php index 27a9a750..1b8edef4 100644 --- a/application/api/controller/User.php +++ b/application/api/controller/User.php @@ -3311,4 +3311,63 @@ class User extends Base return jsonSuccess([]); } + /** + * 根据 user_id 查 user_cv 简历,调用大模型解析用户基本信息。 + * + * POST/GET user_id=用户ID(也支持 userId) + * 简历地址:https://submission.tmrjournals.com/public/reviewer/{cv} + * 同机部署时优先读 public/reviewer/ 本地文件。 + */ + public function getUserInfoByFile() + { + @set_time_limit(180); + + $userId = intval($this->request->param('user_id', $this->request->param('userId', 0))); + if ($userId <= 0) { + return jsonError('请提供 user_id'); + } + + try { + $service = new \app\common\UserInfoFromFileService(); + $result = $service->parseFromUserId($userId); + + \think\Log::info('[getUserInfoByFile] user_id=' . $userId . ' ' . json_encode($result['user_info'], JSON_UNESCAPED_UNICODE)); + + return jsonSuccess([ + 'message' => '解析完成', + 'user_id' => $result['user_id'], + 'cv' => $result['cv'], + 'cv_url' => $result['cv_url'], + 'file' => $result['file'], + 'text_length' => $result['text_length'], + 'text_preview' => $result['text_preview'], + 'user_info' => $result['user_info'], + 'print' => $this->formatUserInfoForPrint($result['user_info']), + ]); + } catch (\Throwable $e) { + return jsonError($e->getMessage()); + } + } + + private function formatUserInfoForPrint(array $info) + { + $labels = [ + 'realname' => '姓名', + 'email' => '邮箱', + 'phone' => '电话', + 'orcid' => 'ORCID', + 'technical' => '职称', + 'field' => '研究领域', + 'company' => 'Institution', + 'department' => '科室', + 'country' => '国家', + 'introduction' => '简介', + ]; + $lines = []; + foreach ($labels as $key => $label) { + $val = trim((string) ($info[$key] ?? '')); + $lines[] = $label . ':' . ($val !== '' ? $val : '(未识别)'); + } + return implode("\n", $lines); + } } diff --git a/application/common/service/LLMService.php b/application/common/service/LLMService.php index fdd1fb42..20e25fc1 100644 --- a/application/common/service/LLMService.php +++ b/application/common/service/LLMService.php @@ -120,6 +120,35 @@ class LLMService ]; } + /** + * 通用对话请求(与参考文献校对共用 postChat / [promotion] 配置) + * + * @param array $messages OpenAI messages + * @param float $temperature + * @return string|null 助手回复正文 + */ + public function requestChat(array $messages, $temperature = 0) + { + if ($this->url === '' || $this->model === '') { + \think\Log::warning('LLM requestChat: url or model not configured'); + return null; + } + $payload = [ + 'model' => $this->model, + 'temperature' => $temperature, + 'messages' => $messages, + ]; + return $this->postChat($payload); + } + + /** + * 解析模型返回的 JSON 对象(去除 markdown 代码块等) + */ + public function parseJsonResponse($raw) + { + return $this->parseJson($raw); + } + /** * 解析 can_support;兼容 is_match 字段 */