694 lines
30 KiB
Python
694 lines
30 KiB
Python
|
|
#!/usr/bin/env python
|
|||
|
|
# -*- coding: utf-8 -*-
|
|||
|
|
"""作者管理接口"""
|
|||
|
|
|
|||
|
|
from flask import Blueprint, request, jsonify, g
|
|||
|
|
import logging
|
|||
|
|
from datetime import datetime
|
|||
|
|
from auth_utils import require_auth, require_role, AuthUtils
|
|||
|
|
from database_config import get_db_manager
|
|||
|
|
# 导入统一日志配置
|
|||
|
|
from log_config import setup_article_server_logger
|
|||
|
|
|
|||
|
|
# 使用统一的日志记录器
|
|||
|
|
logger = logging.getLogger('article_server')
|
|||
|
|
|
|||
|
|
# 创建蓝图
|
|||
|
|
author_bp = Blueprint('author', __name__, url_prefix='/api/authors')
|
|||
|
|
|
|||
|
|
@author_bp.route('', methods=['GET'])
|
|||
|
|
@require_auth
|
|||
|
|
def get_authors():
|
|||
|
|
"""获取作者列表(分页)"""
|
|||
|
|
try:
|
|||
|
|
page = int(request.args.get('page', 1))
|
|||
|
|
size = int(request.args.get('size', 10))
|
|||
|
|
offset = (page - 1) * size
|
|||
|
|
|
|||
|
|
# 搜索参数
|
|||
|
|
search = request.args.get('search', '').strip()
|
|||
|
|
department = request.args.get('department', '').strip()
|
|||
|
|
status = request.args.get('status', '').strip()
|
|||
|
|
channel = request.args.get('channel', '').strip()
|
|||
|
|
|
|||
|
|
db_manager = get_db_manager()
|
|||
|
|
|
|||
|
|
# 构建查询条件
|
|||
|
|
where_conditions = []
|
|||
|
|
params = []
|
|||
|
|
|
|||
|
|
if search:
|
|||
|
|
where_conditions.append("(author_name LIKE %s OR hospital LIKE %s OR title LIKE %s OR id = %s)")
|
|||
|
|
search_param = f"%{search}%"
|
|||
|
|
params.extend([search_param, search_param, search_param, search])
|
|||
|
|
|
|||
|
|
if department:
|
|||
|
|
where_conditions.append("department = %s")
|
|||
|
|
params.append(department)
|
|||
|
|
|
|||
|
|
if status:
|
|||
|
|
where_conditions.append("status = %s")
|
|||
|
|
params.append(status)
|
|||
|
|
|
|||
|
|
if channel:
|
|||
|
|
where_conditions.append("channel = %s")
|
|||
|
|
params.append(channel)
|
|||
|
|
|
|||
|
|
where_clause = ""
|
|||
|
|
if where_conditions:
|
|||
|
|
where_clause = "WHERE " + " AND ".join(where_conditions)
|
|||
|
|
|
|||
|
|
# 查询科室总数(用于分页)
|
|||
|
|
dept_count_sql = f"SELECT COUNT(DISTINCT department_id) as total FROM ai_authors {where_clause}"
|
|||
|
|
dept_count_result = db_manager.execute_query(dept_count_sql, params)
|
|||
|
|
total_departments = dept_count_result[0]['total']
|
|||
|
|
|
|||
|
|
# 先获取分页的科室ID列表
|
|||
|
|
dept_sql = f"""SELECT DISTINCT department_id, department_name
|
|||
|
|
FROM ai_authors {where_clause}
|
|||
|
|
ORDER BY department_id ASC
|
|||
|
|
LIMIT %s OFFSET %s"""
|
|||
|
|
dept_params = params + [size, offset]
|
|||
|
|
departments = db_manager.execute_query(dept_sql, dept_params)
|
|||
|
|
|
|||
|
|
# 如果没有科室数据,直接返回空结果
|
|||
|
|
if not departments:
|
|||
|
|
department_list = []
|
|||
|
|
else:
|
|||
|
|
# 获取这些科室的所有作者
|
|||
|
|
dept_ids = [str(dept['department_id']) for dept in departments]
|
|||
|
|
dept_id_placeholders = ','.join(['%s'] * len(dept_ids))
|
|||
|
|
|
|||
|
|
# 构建包含科室ID过滤的查询条件
|
|||
|
|
author_where_conditions = where_conditions.copy()
|
|||
|
|
author_where_conditions.append(f"department_id IN ({dept_id_placeholders})")
|
|||
|
|
author_where_clause = "WHERE " + " AND ".join(author_where_conditions)
|
|||
|
|
|
|||
|
|
author_sql = f"""SELECT id as author_id, author_name,
|
|||
|
|
department_id, department_name, toutiao_cookie, toutiao_images_cookie
|
|||
|
|
FROM ai_authors {author_where_clause}
|
|||
|
|
ORDER BY department_id ASC, id ASC"""
|
|||
|
|
author_params = params + dept_ids
|
|||
|
|
authors = db_manager.execute_query(author_sql, author_params)
|
|||
|
|
|
|||
|
|
# 按科室分组
|
|||
|
|
department_dict = {}
|
|||
|
|
for dept in departments:
|
|||
|
|
dept_id = dept['department_id']
|
|||
|
|
department_dict[dept_id] = {
|
|||
|
|
'department_id': dept_id,
|
|||
|
|
'department_name': dept['department_name'],
|
|||
|
|
'department_list': []
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
# 将作者分配到对应科室
|
|||
|
|
for author in authors:
|
|||
|
|
dept_id = author['department_id']
|
|||
|
|
if dept_id in department_dict:
|
|||
|
|
department_dict[dept_id]['department_list'].append(author)
|
|||
|
|
|
|||
|
|
# 转换为数组格式,保持科室顺序
|
|||
|
|
department_list = [department_dict[dept['department_id']] for dept in departments]
|
|||
|
|
|
|||
|
|
return jsonify({
|
|||
|
|
'code': 200,
|
|||
|
|
'data': {
|
|||
|
|
'list': department_list,
|
|||
|
|
'page': page,
|
|||
|
|
'size': size,
|
|||
|
|
'total': total_departments
|
|||
|
|
},
|
|||
|
|
'message': '获取成功',
|
|||
|
|
'timestamp': int(datetime.now().timestamp() * 1000)
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
except Exception as e:
|
|||
|
|
logger.error(f"获取作者列表失败: {e}")
|
|||
|
|
return jsonify({'code': 500, 'message': f'获取失败: {str(e)}'}), 500
|
|||
|
|
|
|||
|
|
|
|||
|
|
@author_bp.route('/list', methods=['GET'])
|
|||
|
|
@require_auth
|
|||
|
|
def get_list_authors():
|
|||
|
|
"""获取作者列表(分页,基于用户关联关系)"""
|
|||
|
|
try:
|
|||
|
|
page = int(request.args.get('page', 1))
|
|||
|
|
size = int(request.args.get('size', 10))
|
|||
|
|
offset = (page - 1) * size
|
|||
|
|
|
|||
|
|
# 搜索参数
|
|||
|
|
search = request.args.get('search', '').strip()
|
|||
|
|
department = request.args.get('department', '').strip()
|
|||
|
|
status = request.args.get('status', '').strip()
|
|||
|
|
channel = request.args.get('channel', '').strip()
|
|||
|
|
|
|||
|
|
db_manager = get_db_manager()
|
|||
|
|
|
|||
|
|
# 获取当前登录用户
|
|||
|
|
current_user = AuthUtils.get_current_user()
|
|||
|
|
logger.info(f"[get_authors] 获取到的用户信息: {current_user}")
|
|||
|
|
if not current_user:
|
|||
|
|
logger.error("[get_authors] 未获取到用户信息")
|
|||
|
|
return jsonify({'code': 401, 'message': '未登录或用户信息缺失'}), 401
|
|||
|
|
|
|||
|
|
user_id = current_user.get('user_id')
|
|||
|
|
logger.info(f"[get_authors] 提取的user_id: {user_id}")
|
|||
|
|
if not user_id:
|
|||
|
|
logger.error(f"[get_authors] user_id缺失,current_user内容: {current_user}")
|
|||
|
|
return jsonify({'code': 401, 'message': '用户ID缺失'}), 401
|
|||
|
|
|
|||
|
|
# Step 1: 先查出当前用户关联的作者ID
|
|||
|
|
user_authors_sql = "SELECT author_id FROM ai_user_authors WHERE user_id = %s"
|
|||
|
|
logger.info(f"[get_authors] 执行查询: {user_authors_sql}, 参数: {[user_id]}")
|
|||
|
|
user_authors = db_manager.execute_query(user_authors_sql, [user_id])
|
|||
|
|
logger.info(f"[get_authors] 查询结果: {user_authors}")
|
|||
|
|
author_ids = [str(row['author_id']) for row in user_authors]
|
|||
|
|
logger.info(f"[get_authors] 提取的author_ids: {author_ids}")
|
|||
|
|
|
|||
|
|
if not author_ids:
|
|||
|
|
# 用户没有绑定任何作者
|
|||
|
|
logger.info(f"[get_authors] 用户{user_id}没有绑定任何作者")
|
|||
|
|
return jsonify({
|
|||
|
|
'code': 200,
|
|||
|
|
'data': {
|
|||
|
|
'list': [],
|
|||
|
|
'page': page,
|
|||
|
|
'size': size,
|
|||
|
|
'total': 0
|
|||
|
|
},
|
|||
|
|
'message': '获取成功',
|
|||
|
|
'timestamp': int(datetime.now().timestamp() * 1000)
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
# Step 2: 构建查询条件(限定 author_id 范围)
|
|||
|
|
where_conditions = ["id IN (" + ",".join(["%s"] * len(author_ids)) + ")"]
|
|||
|
|
params = author_ids
|
|||
|
|
|
|||
|
|
if search:
|
|||
|
|
where_conditions.append("(author_name LIKE %s OR hospital LIKE %s OR title LIKE %s)")
|
|||
|
|
search_param = f"%{search}%"
|
|||
|
|
params.extend([search_param, search_param, search_param])
|
|||
|
|
|
|||
|
|
if department:
|
|||
|
|
where_conditions.append("department = %s")
|
|||
|
|
params.append(department)
|
|||
|
|
|
|||
|
|
if status:
|
|||
|
|
where_conditions.append("status = %s")
|
|||
|
|
params.append(status)
|
|||
|
|
|
|||
|
|
if channel:
|
|||
|
|
where_conditions.append("channel = %s")
|
|||
|
|
params.append(channel)
|
|||
|
|
|
|||
|
|
where_clause = "WHERE " + " AND ".join(where_conditions)
|
|||
|
|
|
|||
|
|
# Step 3: 查询科室总数(用于分页)
|
|||
|
|
dept_count_sql = f"SELECT COUNT(DISTINCT department_id) as total FROM ai_authors {where_clause}"
|
|||
|
|
dept_count_result = db_manager.execute_query(dept_count_sql, params)
|
|||
|
|
total_departments = dept_count_result[0]['total'] if dept_count_result else 0
|
|||
|
|
|
|||
|
|
# Step 4: 获取分页的科室ID列表
|
|||
|
|
dept_sql = f"""SELECT DISTINCT department_id, department_name
|
|||
|
|
FROM ai_authors {where_clause}
|
|||
|
|
ORDER BY department_id ASC
|
|||
|
|
LIMIT %s OFFSET %s"""
|
|||
|
|
dept_params = params + [size, offset]
|
|||
|
|
departments = db_manager.execute_query(dept_sql, dept_params)
|
|||
|
|
|
|||
|
|
if not departments:
|
|||
|
|
department_list = []
|
|||
|
|
else:
|
|||
|
|
# Step 5: 获取这些科室下的所有作者
|
|||
|
|
dept_ids = [str(dept['department_id']) for dept in departments]
|
|||
|
|
dept_id_placeholders = ','.join(['%s'] * len(dept_ids))
|
|||
|
|
|
|||
|
|
author_where_conditions = where_conditions.copy()
|
|||
|
|
author_where_conditions.append(f"department_id IN ({dept_id_placeholders})")
|
|||
|
|
author_where_clause = "WHERE " + " AND ".join(author_where_conditions)
|
|||
|
|
|
|||
|
|
author_sql = f"""SELECT id as author_id, author_name,
|
|||
|
|
department_id, department_name
|
|||
|
|
FROM ai_authors {author_where_clause}
|
|||
|
|
ORDER BY department_id ASC, id ASC"""
|
|||
|
|
author_params = params + dept_ids
|
|||
|
|
authors = db_manager.execute_query(author_sql, author_params)
|
|||
|
|
|
|||
|
|
# Step 6: 按科室分组
|
|||
|
|
department_dict = {}
|
|||
|
|
for dept in departments:
|
|||
|
|
dept_id = dept['department_id']
|
|||
|
|
department_dict[dept_id] = {
|
|||
|
|
'department_id': dept_id,
|
|||
|
|
'department_name': dept['department_name'],
|
|||
|
|
'department_list': []
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
for author in authors:
|
|||
|
|
dept_id = author['department_id']
|
|||
|
|
if dept_id in department_dict:
|
|||
|
|
department_dict[dept_id]['department_list'].append(author)
|
|||
|
|
|
|||
|
|
department_list = [department_dict[dept['department_id']] for dept in departments]
|
|||
|
|
|
|||
|
|
return jsonify({
|
|||
|
|
'code': 200,
|
|||
|
|
'data': {
|
|||
|
|
'list': department_list,
|
|||
|
|
'page': page,
|
|||
|
|
'size': size,
|
|||
|
|
'total': total_departments
|
|||
|
|
},
|
|||
|
|
'message': '获取成功',
|
|||
|
|
'timestamp': int(datetime.now().timestamp() * 1000)
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
except Exception as e:
|
|||
|
|
import traceback
|
|||
|
|
logger.error(f"获取作者列表失败: {e}")
|
|||
|
|
logger.error(f"错误堆栈: {traceback.format_exc()}")
|
|||
|
|
return jsonify({'code': 500, 'message': f'获取失败: {str(e)}'}), 500
|
|||
|
|
|
|||
|
|
@author_bp.route('/<int:author_id>', methods=['GET'])
|
|||
|
|
@require_auth
|
|||
|
|
def get_author(author_id):
|
|||
|
|
"""获取单个作者详情"""
|
|||
|
|
try:
|
|||
|
|
db_manager = get_db_manager()
|
|||
|
|
sql = """SELECT id, author_name, department, department_name, title, hospital, specialty, toutiao_images_cookie,
|
|||
|
|
introduction, avatar_url, status, toutiao_cookie, created_at, updated_at
|
|||
|
|
FROM ai_authors WHERE id = %s"""
|
|||
|
|
author = db_manager.execute_query(sql, (author_id,))
|
|||
|
|
|
|||
|
|
if not author:
|
|||
|
|
return jsonify({'code': 404, 'message': '作者不存在'}), 404
|
|||
|
|
|
|||
|
|
return jsonify({
|
|||
|
|
'code': 200,
|
|||
|
|
'data': author[0],
|
|||
|
|
'message': '获取成功',
|
|||
|
|
'timestamp': int(datetime.now().timestamp() * 1000)
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
except Exception as e:
|
|||
|
|
logger.error(f"获取作者详情失败: {e}")
|
|||
|
|
return jsonify({'code': 500, 'message': f'获取失败: {str(e)}'}), 500
|
|||
|
|
|
|||
|
|
@author_bp.route('', methods=['POST'])
|
|||
|
|
@require_auth
|
|||
|
|
@require_role(['admin', 'editor'])
|
|||
|
|
def create_author():
|
|||
|
|
"""创建新作者"""
|
|||
|
|
try:
|
|||
|
|
data = request.get_json()
|
|||
|
|
logger.info(f"[创建作者] 接收到的数据: {data}")
|
|||
|
|
|
|||
|
|
# 必填字段验证
|
|||
|
|
author_name = data.get('author_name', '').strip()
|
|||
|
|
app_id = data.get('app_id', '').strip()
|
|||
|
|
app_token = data.get('app_token', '').strip()
|
|||
|
|
department_name = data.get('department_name', '').strip()
|
|||
|
|
toutiao_cookie = data.get('toutiao_cookie', '').strip()
|
|||
|
|
toutiao_images_cookie = data.get('toutiao_images_cookie', '').strip()
|
|||
|
|
channel = data.get('channel', 1)
|
|||
|
|
|
|||
|
|
logger.info(f"[创建作者] 字段值 - author_name: '{author_name}', app_id: '{app_id}', app_token: '{app_token}', department_name: '{department_name}'")
|
|||
|
|
|
|||
|
|
if not author_name:
|
|||
|
|
logger.warning(f"[创建作者] 作者姓名为空")
|
|||
|
|
return jsonify({'code': 400, 'message': '作者姓名不能为空'}), 400
|
|||
|
|
if not app_id:
|
|||
|
|
logger.warning(f"[创建作者] app_id为空")
|
|||
|
|
return jsonify({'code': 400, 'message': 'app_id不能为空'}), 400
|
|||
|
|
if not app_token:
|
|||
|
|
logger.warning(f"[创建作者] app_token为空")
|
|||
|
|
return jsonify({'code': 400, 'message': 'app_token不能为空'}), 400
|
|||
|
|
if not department_name:
|
|||
|
|
logger.warning(f"[创建作者] department_name为空")
|
|||
|
|
return jsonify({'code': 400, 'message': '科室不能为空'}), 400
|
|||
|
|
if channel == 2 and not toutiao_cookie:
|
|||
|
|
logger.warning(f"[创建作者] toutiao_cookie为空")
|
|||
|
|
return jsonify({'code': 400, 'message': 'toutiao_cookie不能为空'}), 400
|
|||
|
|
if channel == 2 and not toutiao_images_cookie:
|
|||
|
|
logger.warning(f"[创建作者] toutiao_images_cookie为空")
|
|||
|
|
return jsonify({'code': 400, 'message': 'toutiao_images_cookie不能为空'}), 400
|
|||
|
|
|
|||
|
|
# 兼容旧字段
|
|||
|
|
department = data.get('department', department_name).strip()
|
|||
|
|
|
|||
|
|
# 可选字段
|
|||
|
|
title = data.get('title', '').strip()
|
|||
|
|
hospital = data.get('hospital', '').strip()
|
|||
|
|
specialty = data.get('specialty', '').strip()
|
|||
|
|
introduction = data.get('introduction', '').strip()
|
|||
|
|
avatar_url = data.get('avatar_url', '').strip()
|
|||
|
|
status = data.get('status', 'active')
|
|||
|
|
|
|||
|
|
# 状态验证
|
|||
|
|
if status not in ['active', 'inactive']:
|
|||
|
|
return jsonify({'code': 400, 'message': '状态值无效'}), 400
|
|||
|
|
|
|||
|
|
db_manager = get_db_manager()
|
|||
|
|
|
|||
|
|
# (1) 判断author_name唯一,否则报错
|
|||
|
|
check_author_name_sql = "SELECT id FROM ai_authors WHERE author_name = %s"
|
|||
|
|
existing_author_name = db_manager.execute_query(check_author_name_sql, (author_name,))
|
|||
|
|
if existing_author_name:
|
|||
|
|
logger.warning(f"[创建作者] 作者姓名已存在: {author_name}")
|
|||
|
|
return jsonify({'code': 400, 'message': f'作者姓名 "{author_name}" 已存在'}), 400
|
|||
|
|
|
|||
|
|
# (2) 判断app_id唯一,否则报错
|
|||
|
|
check_app_id_sql = "SELECT id FROM ai_authors WHERE app_id = %s"
|
|||
|
|
existing_app_id = db_manager.execute_query(check_app_id_sql, (app_id,))
|
|||
|
|
if existing_app_id:
|
|||
|
|
logger.warning(f"[创建作者] app_id已存在: {app_id}")
|
|||
|
|
return jsonify({'code': 400, 'message': f'app_id "{app_id}" 已存在'}), 400
|
|||
|
|
|
|||
|
|
# (3) 判断app_token唯一,否则报错
|
|||
|
|
check_app_token_sql = "SELECT id FROM ai_authors WHERE app_token = %s"
|
|||
|
|
existing_app_token = db_manager.execute_query(check_app_token_sql, (app_token,))
|
|||
|
|
if existing_app_token:
|
|||
|
|
logger.warning(f"[创建作者] app_token已存在: {app_token}")
|
|||
|
|
return jsonify({'code': 400, 'message': f'app_token "{app_token}" 已存在'}), 400
|
|||
|
|
|
|||
|
|
# (4) department_name处理逻辑
|
|||
|
|
# 查询ai_departments表中是否存在该科室
|
|||
|
|
check_dept_sql = "SELECT id FROM ai_departments WHERE department_name = %s"
|
|||
|
|
existing_dept = db_manager.execute_query(check_dept_sql, (department_name,))
|
|||
|
|
|
|||
|
|
if existing_dept:
|
|||
|
|
# 科室存在,获取department_id
|
|||
|
|
department_id = existing_dept[0]['id']
|
|||
|
|
logger.info(f"[创建作者] 科室已存在,使用现有ID: {department_id}")
|
|||
|
|
else:
|
|||
|
|
# 科室不存在,插入新科室
|
|||
|
|
insert_dept_sql = "INSERT INTO ai_departments (department_name) VALUES (%s)"
|
|||
|
|
department_id = db_manager.execute_insert(insert_dept_sql, (department_name,))
|
|||
|
|
logger.info(f"[创建作者] 创建新科室,ID: {department_id}")
|
|||
|
|
|
|||
|
|
# 创建作者
|
|||
|
|
insert_sql = """INSERT INTO ai_authors
|
|||
|
|
(author_name, app_id, app_token, department_name, department, department_id, title, hospital, specialty, toutiao_cookie, introduction, avatar_url, status, channel, toutiao_images_cookie)
|
|||
|
|
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)"""
|
|||
|
|
author_id = db_manager.execute_insert(insert_sql,
|
|||
|
|
(author_name, app_id, app_token, department_name, department, department_id, title, hospital,
|
|||
|
|
specialty, toutiao_cookie, introduction, avatar_url, status, channel, toutiao_images_cookie))
|
|||
|
|
|
|||
|
|
# 查询新作者
|
|||
|
|
select_sql = """SELECT id, author_name, app_id, app_token, department_name, department, department_id, title, hospital, specialty,
|
|||
|
|
toutiao_cookie, introduction, avatar_url, status, created_at, updated_at, channel
|
|||
|
|
FROM ai_authors WHERE id = %s"""
|
|||
|
|
new_author = db_manager.execute_query(select_sql, (author_id,))
|
|||
|
|
|
|||
|
|
logger.info(f"[创建作者] 成功创建作者,ID: {author_id}, 科室ID: {department_id}")
|
|||
|
|
|
|||
|
|
return jsonify({
|
|||
|
|
'code': 200,
|
|||
|
|
'data': new_author[0],
|
|||
|
|
'message': '创建成功',
|
|||
|
|
'timestamp': int(datetime.now().timestamp() * 1000)
|
|||
|
|
}), 201
|
|||
|
|
|
|||
|
|
except Exception as e:
|
|||
|
|
logger.error(f"创建作者失败: {e}")
|
|||
|
|
return jsonify({'code': 500, 'message': f'创建失败: {str(e)}'}), 500
|
|||
|
|
|
|||
|
|
@author_bp.route('/<int:author_id>', methods=['PUT'])
|
|||
|
|
@require_auth
|
|||
|
|
@require_role(['admin', 'editor'])
|
|||
|
|
def update_author(author_id):
|
|||
|
|
"""更新作者信息"""
|
|||
|
|
try:
|
|||
|
|
data = request.get_json()
|
|||
|
|
|
|||
|
|
# 必填字段验证
|
|||
|
|
author_name = data.get('author_name', '').strip()
|
|||
|
|
department_name = data.get('department_name', '').strip()
|
|||
|
|
|
|||
|
|
if not author_name:
|
|||
|
|
return jsonify({'code': 400, 'message': '作者姓名不能为空'}), 400
|
|||
|
|
if not department_name:
|
|||
|
|
return jsonify({'code': 400, 'message': '科室不能为空'}), 400
|
|||
|
|
|
|||
|
|
# 可选字段
|
|||
|
|
title = data.get('title', '').strip()
|
|||
|
|
hospital = data.get('hospital', '').strip()
|
|||
|
|
specialty = data.get('specialty', '').strip()
|
|||
|
|
introduction = data.get('introduction', '').strip()
|
|||
|
|
avatar_url = data.get('avatar_url', '').strip()
|
|||
|
|
status = data.get('status', 'active')
|
|||
|
|
toutiao_cookie = data.get('toutiao_cookie', '')
|
|||
|
|
toutiao_images_cookie = data.get('toutiao_images_cookie', '')
|
|||
|
|
|
|||
|
|
# 状态验证
|
|||
|
|
if status not in ['active', 'inactive']:
|
|||
|
|
return jsonify({'code': 400, 'message': '状态值无效'}), 400
|
|||
|
|
|
|||
|
|
db_manager = get_db_manager()
|
|||
|
|
|
|||
|
|
# 检查存在
|
|||
|
|
check_sql = "SELECT id FROM ai_authors WHERE id = %s"
|
|||
|
|
existing = db_manager.execute_query(check_sql, (author_id,))
|
|||
|
|
if not existing:
|
|||
|
|
return jsonify({'code': 404, 'message': '作者不存在'}), 404
|
|||
|
|
|
|||
|
|
# 检查重复(同科室同姓名,排除自己)
|
|||
|
|
duplicate_sql = "SELECT id FROM ai_authors WHERE author_name = %s AND department_name = %s AND id != %s"
|
|||
|
|
duplicate = db_manager.execute_query(duplicate_sql, (author_name, department_name, author_id))
|
|||
|
|
if duplicate:
|
|||
|
|
return jsonify({'code': 400, 'message': '该科室已存在同名作者'}), 400
|
|||
|
|
|
|||
|
|
# (4) department_name处理逻辑
|
|||
|
|
# 查询ai_departments表中是否存在该科室
|
|||
|
|
check_dept_sql = "SELECT id FROM ai_departments WHERE department_name = %s"
|
|||
|
|
existing_dept = db_manager.execute_query(check_dept_sql, (department_name,))
|
|||
|
|
|
|||
|
|
if existing_dept:
|
|||
|
|
# 科室存在,获取department_id
|
|||
|
|
department_id = existing_dept[0]['id']
|
|||
|
|
logger.info(f"[更新作者] 科室已存在,使用现有ID: {department_id}")
|
|||
|
|
else:
|
|||
|
|
# 科室不存在,插入新科室
|
|||
|
|
insert_dept_sql = "INSERT INTO ai_departments (department_name) VALUES (%s)"
|
|||
|
|
department_id = db_manager.execute_insert(insert_dept_sql, (department_name,))
|
|||
|
|
logger.info(f"[更新作者] 创建新科室,ID: {department_id}")
|
|||
|
|
|
|||
|
|
# 更新作者
|
|||
|
|
update_sql = """UPDATE ai_authors SET
|
|||
|
|
author_name = %s, department_name = %s, department_id = %s, title = %s,
|
|||
|
|
hospital = %s, specialty = %s, introduction = %s, avatar_url = %s,
|
|||
|
|
status = %s, toutiao_cookie = %s, toutiao_images_cookie = %s,
|
|||
|
|
updated_at = CURRENT_TIMESTAMP
|
|||
|
|
WHERE id = %s"""
|
|||
|
|
db_manager.execute_update(update_sql,
|
|||
|
|
(author_name, department_name, department_id, title,
|
|||
|
|
hospital, specialty, introduction, avatar_url,
|
|||
|
|
status, toutiao_cookie, toutiao_images_cookie, author_id))
|
|||
|
|
|
|||
|
|
# 查询更新后作者
|
|||
|
|
select_sql = """SELECT id, author_name, department_name, title, hospital, specialty,
|
|||
|
|
introduction, toutiao_cookie, toutiao_images_cookie, avatar_url, status, created_at, updated_at
|
|||
|
|
FROM ai_authors WHERE id = %s"""
|
|||
|
|
updated_author = db_manager.execute_query(select_sql, (author_id,))
|
|||
|
|
|
|||
|
|
return jsonify({
|
|||
|
|
'code': 200,
|
|||
|
|
'data': updated_author[0],
|
|||
|
|
'message': '更新成功',
|
|||
|
|
'timestamp': int(datetime.now().timestamp() * 1000)
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
except Exception as e:
|
|||
|
|
logger.error(f"更新作者失败: {e}")
|
|||
|
|
return jsonify({'code': 500, 'message': f'更新失败: {str(e)}'}), 500
|
|||
|
|
|
|||
|
|
@author_bp.route('/<int:author_id>', methods=['DELETE'])
|
|||
|
|
@require_auth
|
|||
|
|
@require_role(['admin'])
|
|||
|
|
def delete_author(author_id):
|
|||
|
|
"""删除作者"""
|
|||
|
|
try:
|
|||
|
|
db_manager = get_db_manager()
|
|||
|
|
|
|||
|
|
# 检查存在
|
|||
|
|
check_sql = "SELECT id, author_name FROM ai_authors WHERE id = %s"
|
|||
|
|
existing = db_manager.execute_query(check_sql, (author_id,))
|
|||
|
|
if not existing:
|
|||
|
|
return jsonify({'code': 404, 'message': '作者不存在'}), 404
|
|||
|
|
|
|||
|
|
author_name = existing[0]['author_name']
|
|||
|
|
|
|||
|
|
# 检查引用(检查是否有文章引用该作者)
|
|||
|
|
article_check_sql = "SELECT COUNT(*) as count FROM ai_articles WHERE author_name = %s"
|
|||
|
|
article_count = db_manager.execute_query(article_check_sql, (author_name,))
|
|||
|
|
if article_count[0]['count'] > 0:
|
|||
|
|
return jsonify({'code': 400, 'message': f'作者被文章引用,无法删除'}), 400
|
|||
|
|
|
|||
|
|
# 删除作者
|
|||
|
|
delete_sql = "DELETE FROM ai_authors WHERE id = %s"
|
|||
|
|
db_manager.execute_update(delete_sql, (author_id,))
|
|||
|
|
|
|||
|
|
return jsonify({
|
|||
|
|
'code': 200,
|
|||
|
|
'message': '删除成功',
|
|||
|
|
'timestamp': int(datetime.now().timestamp() * 1000)
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
except Exception as e:
|
|||
|
|
logger.error(f"删除作者失败: {e}")
|
|||
|
|
return jsonify({'code': 500, 'message': f'删除失败: {str(e)}'}), 500
|
|||
|
|
|
|||
|
|
@author_bp.route('/departments', methods=['GET'])
|
|||
|
|
@require_auth
|
|||
|
|
def get_author_departments():
|
|||
|
|
"""获取所有作者科室列表"""
|
|||
|
|
try:
|
|||
|
|
db_manager = get_db_manager()
|
|||
|
|
sql = "SELECT DISTINCT department FROM ai_authors WHERE department IS NOT NULL AND department != '' ORDER BY department"
|
|||
|
|
departments = db_manager.execute_query(sql)
|
|||
|
|
|
|||
|
|
department_list = [dept['department'] for dept in departments]
|
|||
|
|
|
|||
|
|
return jsonify({
|
|||
|
|
'code': 200,
|
|||
|
|
'data': department_list,
|
|||
|
|
'message': '获取成功',
|
|||
|
|
'timestamp': int(datetime.now().timestamp() * 1000)
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
except Exception as e:
|
|||
|
|
logger.error(f"获取科室列表失败: {e}")
|
|||
|
|
return jsonify({'code': 500, 'message': f'获取失败: {str(e)}'}), 500
|
|||
|
|
|
|||
|
|
@author_bp.route('/search', methods=['GET'])
|
|||
|
|
@require_auth
|
|||
|
|
def search_authors():
|
|||
|
|
"""搜索作者(用于下拉选择等)"""
|
|||
|
|
try:
|
|||
|
|
keyword = request.args.get('keyword', '').strip()
|
|||
|
|
limit = int(request.args.get('limit', 20))
|
|||
|
|
|
|||
|
|
if not keyword:
|
|||
|
|
return jsonify({
|
|||
|
|
'code': 200,
|
|||
|
|
'data': [],
|
|||
|
|
'message': '获取成功',
|
|||
|
|
'timestamp': int(datetime.now().timestamp() * 1000)
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
db_manager = get_db_manager()
|
|||
|
|
sql = """SELECT id, author_name, department, title, hospital
|
|||
|
|
FROM ai_authors
|
|||
|
|
WHERE status = 'active' AND (author_name LIKE %s OR department LIKE %s)
|
|||
|
|
ORDER BY author_name LIMIT %s"""
|
|||
|
|
|
|||
|
|
search_param = f"%{keyword}%"
|
|||
|
|
authors = db_manager.execute_query(sql, (search_param, search_param, limit))
|
|||
|
|
|
|||
|
|
return jsonify({
|
|||
|
|
'code': 200,
|
|||
|
|
'data': authors,
|
|||
|
|
'message': '获取成功',
|
|||
|
|
'timestamp': int(datetime.now().timestamp() * 1000)
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
except Exception as e:
|
|||
|
|
logger.error(f"搜索作者失败: {e}")
|
|||
|
|
return jsonify({'code': 500, 'message': f'搜索失败: {str(e)}'}), 500
|
|||
|
|
|
|||
|
|
@author_bp.route('/detail_list', methods=['GET'])
|
|||
|
|
@require_auth
|
|||
|
|
def get_detail_list_authors():
|
|||
|
|
"""获取作者详细列表(分页,直接查询ai_authors表,不返回app_token字段)"""
|
|||
|
|
try:
|
|||
|
|
page = int(request.args.get('page', 1))
|
|||
|
|
size = int(request.args.get('size', 10))
|
|||
|
|
offset = (page - 1) * size
|
|||
|
|
|
|||
|
|
# 搜索参数
|
|||
|
|
search = request.args.get('search', '').strip()
|
|||
|
|
department = request.args.get('department', '').strip()
|
|||
|
|
status = request.args.get('status', '').strip()
|
|||
|
|
channel = request.args.get('channel', '')
|
|||
|
|
|
|||
|
|
db_manager = get_db_manager()
|
|||
|
|
|
|||
|
|
# 获取当前登录用户(仅用于权限验证)
|
|||
|
|
current_user = AuthUtils.get_current_user()
|
|||
|
|
logger.info(f"[get_detail_list_authors] 获取到的用户信息: {current_user}")
|
|||
|
|
if not current_user:
|
|||
|
|
logger.error("[get_detail_list_authors] 未获取到用户信息")
|
|||
|
|
return jsonify({'code': 401, 'message': '未登录或用户信息缺失'}), 401
|
|||
|
|
|
|||
|
|
# 构建查询条件
|
|||
|
|
where_conditions = []
|
|||
|
|
params = []
|
|||
|
|
|
|||
|
|
if search:
|
|||
|
|
where_conditions.append("(author_name LIKE %s OR hospital LIKE %s OR title LIKE %s OR department_name LIKE %s)")
|
|||
|
|
search_param = f"%{search}%"
|
|||
|
|
params.extend([search_param, search_param, search_param, search_param])
|
|||
|
|
|
|||
|
|
if department:
|
|||
|
|
where_conditions.append("department = %s")
|
|||
|
|
params.append(department)
|
|||
|
|
|
|||
|
|
if status:
|
|||
|
|
where_conditions.append("status = %s")
|
|||
|
|
params.append(status)
|
|||
|
|
|
|||
|
|
if channel:
|
|||
|
|
where_conditions.append("channel = %s")
|
|||
|
|
params.append(channel)
|
|||
|
|
|
|||
|
|
# 构建WHERE子句
|
|||
|
|
where_clause = ""
|
|||
|
|
if where_conditions:
|
|||
|
|
where_clause = "WHERE " + " AND ".join(where_conditions)
|
|||
|
|
|
|||
|
|
# 查询作者总数(用于分页)
|
|||
|
|
count_sql = f"SELECT COUNT(*) as total FROM ai_authors {where_clause}"
|
|||
|
|
logger.info(f"[get_detail_list_authors] 执行计数查询: {count_sql}, 参数: {params}")
|
|||
|
|
count_result = db_manager.execute_query(count_sql, params)
|
|||
|
|
total_authors = count_result[0]['total'] if count_result else 0
|
|||
|
|
logger.info(f"[get_detail_list_authors] 查询到的总数: {total_authors}")
|
|||
|
|
|
|||
|
|
# 查询作者详细信息,排除app_token字段
|
|||
|
|
author_sql = f"""SELECT id as author_id, author_name, app_id,
|
|||
|
|
department_id, department_name, department, toutiao_cookie,
|
|||
|
|
title, hospital, specialty, introduction,
|
|||
|
|
avatar_url, status, created_at, updated_at
|
|||
|
|
FROM ai_authors {where_clause}
|
|||
|
|
ORDER BY id DESC
|
|||
|
|
LIMIT %s OFFSET %s"""
|
|||
|
|
author_params = params + [size, offset]
|
|||
|
|
logger.info(f"[get_detail_list_authors] 执行作者查询: {author_sql}, 参数: {author_params}")
|
|||
|
|
authors = db_manager.execute_query(author_sql, author_params)
|
|||
|
|
logger.info(f"[get_detail_list_authors] 查询到的作者数量: {len(authors)}")
|
|||
|
|
|
|||
|
|
# 处理时间戳格式
|
|||
|
|
for author in authors:
|
|||
|
|
# 确保时间戳格式正确
|
|||
|
|
if author.get('created_at'):
|
|||
|
|
author['created_at'] = int(author['created_at'].timestamp() * 1000)
|
|||
|
|
if author.get('updated_at'):
|
|||
|
|
author['updated_at'] = int(author['updated_at'].timestamp() * 1000)
|
|||
|
|
|
|||
|
|
return jsonify({
|
|||
|
|
'code': 200,
|
|||
|
|
'data': {
|
|||
|
|
'list': authors,
|
|||
|
|
'page': page,
|
|||
|
|
'size': size,
|
|||
|
|
'total': total_authors
|
|||
|
|
},
|
|||
|
|
'message': '获取成功',
|
|||
|
|
'timestamp': int(datetime.now().timestamp() * 1000)
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
except Exception as e:
|
|||
|
|
import traceback
|
|||
|
|
logger.error(f"获取作者详细列表失败: {e}")
|
|||
|
|
logger.error(f"错误堆栈: {traceback.format_exc()}")
|
|||
|
|
return jsonify({'code': 500, 'message': f'获取失败: {str(e)}'}), 500
|