modify scripts

This commit is contained in:
2025-08-10 19:14:15 +08:00
parent 76cb84d401
commit d9ba319269
6 changed files with 754 additions and 2 deletions

View File

@ -8,6 +8,9 @@ db_config = {
'password': 'mysqlpw',
'database': 'stockdb'
}
# 读取环境变量,来修改 db_config 中host的值
if 'DB_HOST' in os.environ:
db_config['host'] = os.environ['DB_HOST']
home_dir = os.path.expanduser("~")
global_host_data_dir = f'{home_dir}/hostdir/stock_data'

View File

@ -0,0 +1,195 @@
import pymysql
from pymysql.cursors import DictCursor
import logging
import sys
from datetime import datetime
class DatabaseConnectionError(Exception):
pass
class StockReportMysql:
# 定义类属性(静态变量)
TBL_STOCK = 'reports_stock'
TBL_NEW_STOCK = 'reports_newstrock' # 注意原拼写可能存在笔误strock应为stock
TBL_STRATEGY = 'reports_strategy'
TBL_MACRESEARCH = 'reports_macresearch'
TBL_INDUSTRY = 'reports_industry'
def __init__(self, db_host, db_user, db_password, db_name, port=3306):
"""
初始化MySQL连接
:param db_host: 数据库主机地址
:param db_user: 数据库用户名
:param db_password: 数据库密码
:param db_name: 数据库名称
:param port: 数据库端口默认3306
"""
self.db_host = db_host
self.db_user = db_user
self.db_password = db_password
self.db_name = db_name
self.port = port
self.conn = None
self.cursor = None
try:
self.conn = pymysql.connect(
host=self.db_host,
user=self.db_user,
password=self.db_password,
database=self.db_name,
port=self.port,
charset='utf8mb4' # 支持中文
)
#self.cursor = self.conn.cursor(dictionary=False) # 使用非字典游标,保持与原代码兼容
self.cursor = self.conn.cursor() # 使用默认游标
except pymysql.MySQLError as e:
logging.error(f"数据库连接失败: {e}")
raise DatabaseConnectionError("数据库连接失败")
def __get_table_columns_and_defaults(self, tbl_name):
"""获取表的列信息及默认值适配MySQL"""
try:
# 查询information_schema获取列信息
self.cursor.execute("""
SELECT COLUMN_NAME, COLUMN_DEFAULT
FROM information_schema.COLUMNS
WHERE TABLE_NAME = %s AND TABLE_SCHEMA = %s
""", (tbl_name, self.db_name))
columns = self.cursor.fetchall()
column_info = {}
for col in columns:
col_name = col[0]
default_value = col[1]
# MySQL默认值可能包含函数如CURRENT_TIMESTAMP需要特殊处理
column_info[col_name] = default_value
return column_info
except pymysql.MySQLError as e:
logging.error(f"获取表结构失败: {e}")
return None
def __check_and_process_data(self, data, tbl_name):
"""数据校验和处理(逻辑与原代码保持一致)"""
column_info = self.__get_table_columns_and_defaults(tbl_name=tbl_name)
if column_info is None:
return None
processed_data = {}
for col, default in column_info.items():
if col == 'id': # 自增主键,不需要用户提供
continue
if col == 'created_at' or col == 'updated_at': # 日期字段由数据库或代码控制
continue
if col in ['author', 'authorID']:
# 将列表转换为逗号分隔字符串
values = data.get(col, [])
processed_data[col] = ','.join(values) if values else None
# 确保不超过255字符
if processed_data[col] and len(processed_data[col]) > 250:
processed_data[col] = processed_data[col][:250]
elif col in data:
processed_data[col] = data[col]
else:
# 使用默认值
pass
return processed_data
def insert_or_update_common(self, data, tbl_name, uniq_key='infoCode'):
"""插入或更新数据适配MySQL的ON DUPLICATE KEY UPDATE"""
try:
processed_data = self.__check_and_process_data(data, tbl_name)
if processed_data is None:
return None
columns = ', '.join(processed_data.keys())
values = list(processed_data.values())
placeholders = ', '.join(['%s' for _ in values]) # MySQL使用%s作为占位符
# 构造更新子句(排除唯一键字段)
update_clause = ', '.join(
[f"{col}=VALUES({col})" for col in processed_data.keys() if col != uniq_key]
) + ', updated_at=NOW()' # MySQL使用NOW()获取当前时间
sql = f'''
INSERT INTO {tbl_name} ({columns}, created_at, updated_at)
VALUES ({placeholders}, NOW(), NOW())
ON DUPLICATE KEY UPDATE {update_clause}
'''
self.cursor.execute(sql, values)
self.conn.commit()
# 获取插入或更新后的记录ID
self.cursor.execute(f"SELECT id FROM {tbl_name} WHERE {uniq_key} = %s", (data[uniq_key],))
result = self.cursor.fetchone()
return result[0] if result else None
except pymysql.MySQLError as e:
logging.error(f"插入或更新数据失败: {e}")
self.conn.rollback() # 出错时回滚
return None
def update_pages(self, data, tbl_name, uniq_key='infoCode'):
"""更新附件页数使用参数化查询防止SQL注入"""
try:
# 注意原代码直接拼接SQL有注入风险此处改为参数化查询
sql = f'''
UPDATE {tbl_name}
SET attachPages = %s
WHERE id = %s
'''
self.cursor.execute(sql, (data['attachPages'], data['id']))
self.conn.commit()
return data['id']
except pymysql.MySQLError as e:
logging.error(f"更新页数失败: {e}")
self.conn.rollback()
return None
def query_reports_comm(self, tbl_name, querystr='', limit=None):
"""查询报告列表适配MySQL语法"""
try:
# 验证表名合法性
valid_tables = [
self.TBL_STOCK, self.TBL_NEW_STOCK,
self.TBL_INDUSTRY, self.TBL_MACRESEARCH,
self.TBL_STRATEGY
]
if tbl_name not in valid_tables:
logging.warning(f'无效的表名: {tbl_name}')
return None
# 构造查询SQL
sql = f"""
SELECT id, infoCode, title, orgSName, industryName, stockName, publishDate
FROM {tbl_name}
WHERE 1=1 {querystr}
"""
# 添加限制条件
if limit:
sql += f' LIMIT {limit}'
self.cursor.execute(sql)
results = self.cursor.fetchall()
# 获取列名
column_names = [description[0] for description in self.cursor.description]
# 转换为字典列表
result_dict_list = []
for row in results:
row_dict = {column_names[i]: value for i, value in enumerate(row)}
result_dict_list.append(row_dict)
return result_dict_list
except pymysql.MySQLError as e:
logging.error(f"查询失败: {e}")
return None
def __del__(self):
"""析构函数关闭数据库连接适配pymysql"""
try:
# pymysql中通过ping()判断连接是否有效,若连接已关闭会抛出异常
if self.conn:
self.conn.ping() # 尝试检测连接是否存活
self.cursor.close()
self.conn.close()
except (pymysql.MySQLError, AttributeError):
# 捕获连接已关闭、游标不存在等异常,避免析构时报错
pass

View File

@ -11,8 +11,9 @@ from datetime import datetime, timedelta
from functools import partial
import src.crawler.em.reports as em
import src.utils.utils as utils
from src.config.config import global_host_data_dir, global_share_db_dir
from src.config.config import global_host_data_dir, global_share_db_dir, db_config
from src.db_utils.reports import StockReportDB, DatabaseConnectionError
from src.db_utils.reports_mysql import StockReportMysql
from src.logger.logger import setup_logging
import PyPDF2
@ -83,6 +84,17 @@ def fetch_reports_list_general(fetch_func, table_name, s_date, e_date, data_dir_
# 统一以 infoCode 为 UNIQE 键,所以这里对它进行赋值
if row.get('infoCode') is None and row.get('encodeUrl'):
row['infoCode'] = row['encodeUrl']
row['count_all'] = row.get('count', 0) # 兼容旧数据
row['curr_column'] = row.get('column', 'None') # 兼容旧数据
if 'stockName' not in row or row['stockName'] is None or row['stockName']=='':
row['stockName'] = ''
if 'stockCode' not in row or row['stockCode'] is None or row['stockCode']=='':
row['stockCode'] = ''
if 'newPeIssueA' in row and row['newPeIssueA'] is None:
try:
row['newPeIssueA'] = float(row.get('newPeIssueA', 0))
except ValueError:
row['newPeIssueA'] = 0.0
row_id = db_tools.insert_or_update_common(row, table_name)
if row_id:
logging.debug(f'insert one row. rowid:{row_id}, ')
@ -325,7 +337,9 @@ def main(cmd, mode, args_debug, args_force, begin, end):
# 初始化DB
global db_tools
try:
db_tools = StockReportDB(db_path)
#db_tools = StockReportDB(db_path)
db_tools = StockReportMysql(db_host=db_config['host'], db_user=db_config['user'], db_password=db_config['password'], db_name = db_config['database'], port=3306) # 使用配置文件中的数据库配置
# 进行数据库操作
except DatabaseConnectionError as e:
logging.error(f"数据库连接失败: {e}")

View File

@ -0,0 +1,253 @@
"""Auto update from stockdb
Revision ID: 33c9c4443fa8
Revises: 9680e7b8e29b
Create Date: 2025-08-10 18:03:56.817265
"""
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision: str = '33c9c4443fa8'
down_revision: Union[str, Sequence[str], None] = '9680e7b8e29b'
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
"""Upgrade schema."""
# ### commands auto generated by Alembic - please adjust! ###
op.create_table('reports_industry',
sa.Column('id', sa.Integer(), autoincrement=True, nullable=False, comment='自增主键'),
sa.Column('infoCode', sa.String(length=255), nullable=False, comment='报告唯一标识'),
sa.Column('title', sa.String(length=512), nullable=True, comment='报告标题'),
sa.Column('stockName', sa.String(length=100), nullable=True, comment='股票名称'),
sa.Column('stockCode', sa.String(length=50), nullable=True, comment='股票代码'),
sa.Column('orgCode', sa.String(length=50), nullable=True, comment='机构代码'),
sa.Column('orgName', sa.String(length=255), nullable=True, comment='机构全称'),
sa.Column('orgSName', sa.String(length=100), nullable=True, comment='机构简称'),
sa.Column('publishDate', sa.String(length=50), nullable=True, comment='发布日期'),
sa.Column('column', sa.String(length=255), nullable=True, comment='栏目'),
sa.Column('predictNextTwoYearEps', sa.String(length=50), nullable=True, comment='未来两年EPS预测'),
sa.Column('predictNextTwoYearPe', sa.String(length=50), nullable=True, comment='未来两年PE预测'),
sa.Column('predictNextYearEps', sa.String(length=50), nullable=True, comment='明年EPS预测'),
sa.Column('predictNextYearPe', sa.String(length=50), nullable=True, comment='明年PE预测'),
sa.Column('predictThisYearEps', sa.String(length=50), nullable=True, comment='今年EPS预测'),
sa.Column('predictThisYearPe', sa.String(length=50), nullable=True, comment='今年PE预测'),
sa.Column('predictLastYearEps', sa.String(length=50), nullable=True, comment='去年EPS预测'),
sa.Column('predictLastYearPe', sa.String(length=50), nullable=True, comment='去年PE预测'),
sa.Column('actualLastTwoYearEps', sa.String(length=50), nullable=True, comment='前两年实际EPS'),
sa.Column('actualLastYearEps', sa.String(length=50), nullable=True, comment='去年实际EPS'),
sa.Column('industryCode', sa.String(length=50), nullable=True, comment='行业代码'),
sa.Column('industryName', sa.String(length=100), nullable=True, comment='行业名称'),
sa.Column('emIndustryCode', sa.String(length=50), nullable=True, comment='东方财富行业代码'),
sa.Column('indvInduCode', sa.String(length=50), nullable=True, comment='个性化行业代码'),
sa.Column('indvInduName', sa.String(length=100), nullable=True, comment='个性化行业名称'),
sa.Column('emRatingCode', sa.String(length=50), nullable=True, comment='东方财富评级代码'),
sa.Column('emRatingValue', sa.String(length=50), nullable=True, comment='东方财富评级值'),
sa.Column('emRatingName', sa.String(length=100), nullable=True, comment='东方财富评级名称'),
sa.Column('lastEmRatingCode', sa.String(length=50), nullable=True, comment='上次东方财富评级代码'),
sa.Column('lastEmRatingValue', sa.String(length=50), nullable=True, comment='上次东方财富评级值'),
sa.Column('lastEmRatingName', sa.String(length=100), nullable=True, comment='上次东方财富评级名称'),
sa.Column('ratingChange', sa.String(length=50), nullable=True, comment='评级变动'),
sa.Column('reportType', sa.Integer(), nullable=True, comment='报告类型'),
sa.Column('author', sa.String(length=255), nullable=True, comment='作者(逗号分隔)'),
sa.Column('indvIsNew', sa.String(length=20), nullable=True, comment='是否为新研报'),
sa.Column('researcher', sa.String(length=255), nullable=True, comment='研究员'),
sa.Column('newListingDate', sa.String(length=50), nullable=True, comment='新上市日期'),
sa.Column('newPurchaseDate', sa.String(length=50), nullable=True, comment='新买入日期'),
sa.Column('newIssuePrice', sa.String(length=50), nullable=True, comment='新发行价格'),
sa.Column('newPeIssueA', sa.String(length=50), nullable=True, comment='新发行市盈率A'),
sa.Column('indvAimPriceT', sa.String(length=50), nullable=True, comment='目标价上限'),
sa.Column('indvAimPriceL', sa.String(length=50), nullable=True, comment='目标价下限'),
sa.Column('attachType', sa.String(length=50), nullable=True, comment='附件类型'),
sa.Column('attachSize', sa.Integer(), nullable=True, comment='附件大小'),
sa.Column('attachPages', sa.Integer(), nullable=True, comment='附件页数'),
sa.Column('encodeUrl', sa.String(length=512), nullable=True, comment='加密URL'),
sa.Column('sRatingName', sa.String(length=100), nullable=True, comment='评级名称'),
sa.Column('sRatingCode', sa.String(length=50), nullable=True, comment='评级代码'),
sa.Column('market', sa.String(length=50), nullable=True, comment='市场'),
sa.Column('authorID', sa.String(length=255), nullable=True, comment='作者ID逗号分隔'),
sa.Column('count', sa.Integer(), nullable=True, comment='计数'),
sa.Column('orgType', sa.String(length=50), nullable=True, comment='机构类型'),
sa.Column('created_at', sa.DateTime(), nullable=False, comment='创建时间'),
sa.Column('updated_at', sa.DateTime(), nullable=False, comment='更新时间'),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('infoCode')
)
op.create_table('reports_macresearch',
sa.Column('id', sa.Integer(), autoincrement=True, nullable=False, comment='自增主键'),
sa.Column('infoCode', sa.String(length=255), nullable=False, comment='报告唯一标识'),
sa.Column('json_id', sa.String(length=50), nullable=True, comment='JSON ID'),
sa.Column('title', sa.String(length=512), nullable=True, comment='报告标题'),
sa.Column('author', sa.String(length=255), nullable=True, comment='作者(逗号分隔)'),
sa.Column('orgName', sa.String(length=255), nullable=True, comment='机构全称'),
sa.Column('orgCode', sa.String(length=50), nullable=True, comment='机构代码'),
sa.Column('orgSName', sa.String(length=100), nullable=True, comment='机构简称'),
sa.Column('publishDate', sa.String(length=50), nullable=True, comment='发布日期'),
sa.Column('encodeUrl', sa.String(length=512), nullable=True, comment='加密URL'),
sa.Column('researcher', sa.String(length=255), nullable=True, comment='研究员'),
sa.Column('market', sa.String(length=50), nullable=True, comment='市场'),
sa.Column('industryCode', sa.String(length=50), nullable=True, comment='行业代码'),
sa.Column('industryName', sa.String(length=100), nullable=True, comment='行业名称'),
sa.Column('authorID', sa.String(length=255), nullable=True, comment='作者ID逗号分隔'),
sa.Column('count', sa.Integer(), nullable=True, comment='计数'),
sa.Column('orgType', sa.String(length=50), nullable=True, comment='机构类型'),
sa.Column('created_at', sa.DateTime(), nullable=False, comment='创建时间'),
sa.Column('updated_at', sa.DateTime(), nullable=False, comment='更新时间'),
sa.Column('stockCode', sa.String(length=50), nullable=False, comment='股票代码(默认空字符串)'),
sa.Column('stockName', sa.String(length=100), nullable=False, comment='股票名称(默认空字符串)'),
sa.Column('attachPages', sa.Integer(), nullable=True, comment='附件页数'),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('infoCode')
)
op.create_table('reports_newstrock',
sa.Column('id', sa.Integer(), autoincrement=True, nullable=False, comment='自增主键'),
sa.Column('infoCode', sa.String(length=255), nullable=False, comment='报告唯一标识'),
sa.Column('title', sa.String(length=512), nullable=True, comment='报告标题'),
sa.Column('stockName', sa.String(length=100), nullable=True, comment='股票名称'),
sa.Column('stockCode', sa.String(length=50), nullable=True, comment='股票代码'),
sa.Column('orgCode', sa.String(length=50), nullable=True, comment='机构代码'),
sa.Column('orgName', sa.String(length=255), nullable=True, comment='机构全称'),
sa.Column('orgSName', sa.String(length=100), nullable=True, comment='机构简称'),
sa.Column('publishDate', sa.String(length=50), nullable=True, comment='发布日期'),
sa.Column('column', sa.String(length=255), nullable=True, comment='栏目'),
sa.Column('actualLastTwoYearEps', sa.String(length=50), nullable=True, comment='前两年实际EPS'),
sa.Column('actualLastYearEps', sa.String(length=50), nullable=True, comment='去年实际EPS'),
sa.Column('industryCode', sa.String(length=50), nullable=True, comment='行业代码'),
sa.Column('industryName', sa.String(length=100), nullable=True, comment='行业名称'),
sa.Column('emIndustryCode', sa.String(length=50), nullable=True, comment='东方财富行业代码'),
sa.Column('indvInduCode', sa.String(length=50), nullable=True, comment='个性化行业代码'),
sa.Column('indvInduName', sa.String(length=100), nullable=True, comment='个性化行业名称'),
sa.Column('emRatingCode', sa.String(length=50), nullable=True, comment='东方财富评级代码'),
sa.Column('emRatingValue', sa.String(length=50), nullable=True, comment='东方财富评级值'),
sa.Column('emRatingName', sa.String(length=100), nullable=True, comment='东方财富评级名称'),
sa.Column('lastEmRatingCode', sa.String(length=50), nullable=True, comment='上次东方财富评级代码'),
sa.Column('lastEmRatingValue', sa.String(length=50), nullable=True, comment='上次东方财富评级值'),
sa.Column('lastEmRatingName', sa.String(length=100), nullable=True, comment='上次东方财富评级名称'),
sa.Column('ratingChange', sa.String(length=50), nullable=True, comment='评级变动'),
sa.Column('reportType', sa.Integer(), nullable=True, comment='报告类型'),
sa.Column('author', sa.String(length=255), nullable=True, comment='作者(逗号分隔)'),
sa.Column('indvIsNew', sa.String(length=20), nullable=True, comment='是否为新研报'),
sa.Column('researcher', sa.String(length=255), nullable=True, comment='研究员'),
sa.Column('newListingDate', sa.String(length=50), nullable=True, comment='新上市日期'),
sa.Column('newPurchaseDate', sa.String(length=50), nullable=True, comment='新买入日期'),
sa.Column('newIssuePrice', sa.Float(), nullable=True, comment='新发行价格'),
sa.Column('newPeIssueA', sa.Float(), nullable=True, comment='新发行市盈率A'),
sa.Column('indvAimPriceT', sa.String(length=50), nullable=True, comment='目标价上限'),
sa.Column('indvAimPriceL', sa.String(length=50), nullable=True, comment='目标价下限'),
sa.Column('attachType', sa.String(length=50), nullable=True, comment='附件类型'),
sa.Column('attachSize', sa.Integer(), nullable=True, comment='附件大小'),
sa.Column('attachPages', sa.Integer(), nullable=True, comment='附件页数'),
sa.Column('encodeUrl', sa.String(length=512), nullable=True, comment='加密URL'),
sa.Column('sRatingName', sa.String(length=100), nullable=True, comment='评级名称'),
sa.Column('sRatingCode', sa.String(length=50), nullable=True, comment='评级代码'),
sa.Column('market', sa.String(length=50), nullable=True, comment='市场'),
sa.Column('newStockSort', sa.String(length=50), nullable=True, comment='新股分类'),
sa.Column('authorID', sa.String(length=255), nullable=True, comment='作者ID逗号分隔'),
sa.Column('count', sa.Integer(), nullable=True, comment='计数'),
sa.Column('orgType', sa.String(length=50), nullable=True, comment='机构类型'),
sa.Column('created_at', sa.DateTime(), nullable=False, comment='创建时间'),
sa.Column('updated_at', sa.DateTime(), nullable=False, comment='更新时间'),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('infoCode')
)
op.create_table('reports_stock',
sa.Column('id', sa.Integer(), autoincrement=True, nullable=False, comment='自增主键'),
sa.Column('infoCode', sa.String(length=255), nullable=False, comment='报告唯一标识'),
sa.Column('title', sa.String(length=512), nullable=True, comment='报告标题'),
sa.Column('stockName', sa.String(length=100), nullable=True, comment='股票名称'),
sa.Column('stockCode', sa.String(length=50), nullable=True, comment='股票代码'),
sa.Column('orgCode', sa.String(length=50), nullable=True, comment='机构代码'),
sa.Column('orgName', sa.String(length=255), nullable=True, comment='机构全称'),
sa.Column('orgSName', sa.String(length=100), nullable=True, comment='机构简称'),
sa.Column('publishDate', sa.String(length=50), nullable=True, comment='发布日期'),
sa.Column('column', sa.String(length=255), nullable=True, comment='栏目'),
sa.Column('predictNextTwoYearEps', sa.String(length=50), nullable=True, comment='未来两年EPS预测'),
sa.Column('predictNextTwoYearPe', sa.String(length=50), nullable=True, comment='未来两年PE预测'),
sa.Column('predictNextYearEps', sa.String(length=50), nullable=True, comment='明年EPS预测'),
sa.Column('predictNextYearPe', sa.String(length=50), nullable=True, comment='明年PE预测'),
sa.Column('predictThisYearEps', sa.String(length=50), nullable=True, comment='今年EPS预测'),
sa.Column('predictThisYearPe', sa.String(length=50), nullable=True, comment='今年PE预测'),
sa.Column('predictLastYearEps', sa.String(length=50), nullable=True, comment='去年EPS预测'),
sa.Column('predictLastYearPe', sa.String(length=50), nullable=True, comment='去年PE预测'),
sa.Column('actualLastTwoYearEps', sa.String(length=50), nullable=True, comment='前两年实际EPS'),
sa.Column('actualLastYearEps', sa.String(length=50), nullable=True, comment='去年实际EPS'),
sa.Column('industryCode', sa.String(length=50), nullable=True, comment='行业代码'),
sa.Column('industryName', sa.String(length=100), nullable=True, comment='行业名称'),
sa.Column('emIndustryCode', sa.String(length=50), nullable=True, comment='东方财富行业代码'),
sa.Column('indvInduCode', sa.String(length=50), nullable=True, comment='个性化行业代码'),
sa.Column('indvInduName', sa.String(length=100), nullable=True, comment='个性化行业名称'),
sa.Column('emRatingCode', sa.String(length=50), nullable=True, comment='东方财富评级代码'),
sa.Column('emRatingValue', sa.String(length=50), nullable=True, comment='东方财富评级值'),
sa.Column('emRatingName', sa.String(length=100), nullable=True, comment='东方财富评级名称'),
sa.Column('lastEmRatingCode', sa.String(length=50), nullable=True, comment='上次东方财富评级代码'),
sa.Column('lastEmRatingValue', sa.String(length=50), nullable=True, comment='上次东方财富评级值'),
sa.Column('lastEmRatingName', sa.String(length=100), nullable=True, comment='上次东方财富评级名称'),
sa.Column('ratingChange', sa.Integer(), nullable=True, comment='评级变动'),
sa.Column('reportType', sa.Integer(), nullable=True, comment='报告类型'),
sa.Column('author', sa.String(length=255), nullable=True, comment='作者(逗号分隔)'),
sa.Column('indvIsNew', sa.String(length=20), nullable=True, comment='是否为新研报'),
sa.Column('researcher', sa.String(length=255), nullable=True, comment='研究员'),
sa.Column('newListingDate', sa.String(length=50), nullable=True, comment='新上市日期'),
sa.Column('newPurchaseDate', sa.String(length=50), nullable=True, comment='新买入日期'),
sa.Column('newIssuePrice', sa.Float(), nullable=True, comment='新发行价格'),
sa.Column('newPeIssueA', sa.Float(), nullable=True, comment='新发行市盈率A'),
sa.Column('indvAimPriceT', sa.String(length=50), nullable=True, comment='目标价上限'),
sa.Column('indvAimPriceL', sa.String(length=50), nullable=True, comment='目标价下限'),
sa.Column('attachType', sa.String(length=50), nullable=True, comment='附件类型'),
sa.Column('attachSize', sa.Integer(), nullable=True, comment='附件大小'),
sa.Column('attachPages', sa.Integer(), nullable=True, comment='附件页数'),
sa.Column('encodeUrl', sa.String(length=512), nullable=True, comment='加密URL'),
sa.Column('sRatingName', sa.String(length=100), nullable=True, comment='评级名称'),
sa.Column('sRatingCode', sa.String(length=50), nullable=True, comment='评级代码'),
sa.Column('market', sa.String(length=50), nullable=True, comment='市场'),
sa.Column('authorID', sa.String(length=255), nullable=True, comment='作者ID逗号分隔'),
sa.Column('count', sa.Integer(), nullable=True, comment='计数'),
sa.Column('orgType', sa.String(length=50), nullable=True, comment='机构类型'),
sa.Column('created_at', sa.DateTime(), nullable=False, comment='创建时间'),
sa.Column('updated_at', sa.DateTime(), nullable=False, comment='更新时间'),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('infoCode')
)
op.create_table('reports_strategy',
sa.Column('id', sa.Integer(), autoincrement=True, nullable=False, comment='自增主键'),
sa.Column('infoCode', sa.String(length=255), nullable=False, comment='报告唯一标识'),
sa.Column('title', sa.String(length=512), nullable=True, comment='报告标题'),
sa.Column('author', sa.String(length=255), nullable=True, comment='作者(逗号分隔)'),
sa.Column('orgName', sa.String(length=255), nullable=True, comment='机构全称'),
sa.Column('orgCode', sa.String(length=50), nullable=True, comment='机构代码'),
sa.Column('orgSName', sa.String(length=100), nullable=True, comment='机构简称'),
sa.Column('publishDate', sa.String(length=50), nullable=True, comment='发布日期'),
sa.Column('encodeUrl', sa.String(length=512), nullable=True, comment='加密URL'),
sa.Column('researcher', sa.String(length=255), nullable=True, comment='研究员'),
sa.Column('market', sa.String(length=50), nullable=True, comment='市场'),
sa.Column('industryCode', sa.String(length=50), nullable=True, comment='行业代码'),
sa.Column('industryName', sa.String(length=100), nullable=True, comment='行业名称'),
sa.Column('authorID', sa.String(length=255), nullable=True, comment='作者ID逗号分隔'),
sa.Column('count', sa.Integer(), nullable=True, comment='计数'),
sa.Column('orgType', sa.String(length=50), nullable=True, comment='机构类型'),
sa.Column('created_at', sa.DateTime(), nullable=False, comment='创建时间'),
sa.Column('updated_at', sa.DateTime(), nullable=False, comment='更新时间'),
sa.Column('stockName', sa.String(length=100), nullable=False, comment='股票名称(默认空字符串)'),
sa.Column('stockCode', sa.String(length=50), nullable=False, comment='股票代码(默认空字符串)'),
sa.Column('attachPages', sa.Integer(), nullable=True, comment='附件页数'),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('infoCode')
)
# ### end Alembic commands ###
def downgrade() -> None:
"""Downgrade schema."""
# ### commands auto generated by Alembic - please adjust! ###
op.drop_table('reports_strategy')
op.drop_table('reports_stock')
op.drop_table('reports_newstrock')
op.drop_table('reports_macresearch')
op.drop_table('reports_industry')
# ### end Alembic commands ###

View File

@ -0,0 +1,62 @@
"""Auto update from stockdb
Revision ID: c72bb1f27166
Revises: 33c9c4443fa8
Create Date: 2025-08-10 18:36:58.748680
"""
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
from sqlalchemy.dialects import mysql
# revision identifiers, used by Alembic.
revision: str = 'c72bb1f27166'
down_revision: Union[str, Sequence[str], None] = '33c9c4443fa8'
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
"""Upgrade schema."""
# ### commands auto generated by Alembic - please adjust! ###
op.add_column('reports_industry', sa.Column('curr_column', sa.String(length=255), nullable=True, comment='栏目'))
op.add_column('reports_industry', sa.Column('count_all', sa.Integer(), nullable=True, comment='计数'))
op.drop_column('reports_industry', 'column')
op.drop_column('reports_industry', 'count')
op.add_column('reports_macresearch', sa.Column('count_all', sa.Integer(), nullable=True, comment='计数'))
op.drop_column('reports_macresearch', 'count')
op.add_column('reports_newstrock', sa.Column('curr_column', sa.String(length=255), nullable=True, comment='栏目'))
op.add_column('reports_newstrock', sa.Column('count_all', sa.Integer(), nullable=True, comment='计数'))
op.drop_column('reports_newstrock', 'column')
op.drop_column('reports_newstrock', 'count')
op.add_column('reports_stock', sa.Column('curr_column', sa.String(length=255), nullable=True, comment='栏目'))
op.add_column('reports_stock', sa.Column('count_all', sa.Integer(), nullable=True, comment='计数'))
op.drop_column('reports_stock', 'column')
op.drop_column('reports_stock', 'count')
op.add_column('reports_strategy', sa.Column('count_all', sa.Integer(), nullable=True, comment='计数'))
op.drop_column('reports_strategy', 'count')
# ### end Alembic commands ###
def downgrade() -> None:
"""Downgrade schema."""
# ### commands auto generated by Alembic - please adjust! ###
op.add_column('reports_strategy', sa.Column('count', mysql.INTEGER(), autoincrement=False, nullable=True, comment='计数'))
op.drop_column('reports_strategy', 'count_all')
op.add_column('reports_stock', sa.Column('count', mysql.INTEGER(), autoincrement=False, nullable=True, comment='计数'))
op.add_column('reports_stock', sa.Column('column', mysql.VARCHAR(length=255), nullable=True, comment='栏目'))
op.drop_column('reports_stock', 'count_all')
op.drop_column('reports_stock', 'curr_column')
op.add_column('reports_newstrock', sa.Column('count', mysql.INTEGER(), autoincrement=False, nullable=True, comment='计数'))
op.add_column('reports_newstrock', sa.Column('column', mysql.VARCHAR(length=255), nullable=True, comment='栏目'))
op.drop_column('reports_newstrock', 'count_all')
op.drop_column('reports_newstrock', 'curr_column')
op.add_column('reports_macresearch', sa.Column('count', mysql.INTEGER(), autoincrement=False, nullable=True, comment='计数'))
op.drop_column('reports_macresearch', 'count_all')
op.add_column('reports_industry', sa.Column('count', mysql.INTEGER(), autoincrement=False, nullable=True, comment='计数'))
op.add_column('reports_industry', sa.Column('column', mysql.VARCHAR(length=255), nullable=True, comment='栏目'))
op.drop_column('reports_industry', 'count_all')
op.drop_column('reports_industry', 'curr_column')
# ### end Alembic commands ###

View File

@ -1,6 +1,7 @@
from sqlalchemy import BigInteger, Date, DateTime, Double, Float, Integer, String, text, Numeric
from sqlalchemy.dialects.mysql import TINYINT, VARCHAR
from typing import Optional
from sqlalchemy.sql import func
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column
import datetime
@ -446,3 +447,227 @@ class FutuTradingDayModel(Base):
String(20),
comment="交易日类型"
)
class ReportsStock(Base):
__tablename__ = "reports_stock"
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True, comment="自增主键")
infoCode: Mapped[str] = mapped_column(String(255), unique=True, comment="报告唯一标识")
title: Mapped[Optional[str]] = mapped_column(String(512), comment="报告标题")
stockName: Mapped[Optional[str]] = mapped_column(String(100), comment="股票名称")
stockCode: Mapped[Optional[str]] = mapped_column(String(50), comment="股票代码")
orgCode: Mapped[Optional[str]] = mapped_column(String(50), comment="机构代码")
orgName: Mapped[Optional[str]] = mapped_column(String(255), comment="机构全称")
orgSName: Mapped[Optional[str]] = mapped_column(String(100), comment="机构简称")
publishDate: Mapped[Optional[str]] = mapped_column(String(50), comment="发布日期")
curr_column: Mapped[Optional[str]] = mapped_column(String(255), comment="栏目") # 注意column是SQL关键字建议后续改名
predictNextTwoYearEps: Mapped[Optional[str]] = mapped_column(String(50), comment="未来两年EPS预测")
predictNextTwoYearPe: Mapped[Optional[str]] = mapped_column(String(50), comment="未来两年PE预测")
predictNextYearEps: Mapped[Optional[str]] = mapped_column(String(50), comment="明年EPS预测")
predictNextYearPe: Mapped[Optional[str]] = mapped_column(String(50), comment="明年PE预测")
predictThisYearEps: Mapped[Optional[str]] = mapped_column(String(50), comment="今年EPS预测")
predictThisYearPe: Mapped[Optional[str]] = mapped_column(String(50), comment="今年PE预测")
predictLastYearEps: Mapped[Optional[str]] = mapped_column(String(50), comment="去年EPS预测")
predictLastYearPe: Mapped[Optional[str]] = mapped_column(String(50), comment="去年PE预测")
actualLastTwoYearEps: Mapped[Optional[str]] = mapped_column(String(50), comment="前两年实际EPS")
actualLastYearEps: Mapped[Optional[str]] = mapped_column(String(50), comment="去年实际EPS")
industryCode: Mapped[Optional[str]] = mapped_column(String(50), comment="行业代码")
industryName: Mapped[Optional[str]] = mapped_column(String(100), comment="行业名称")
emIndustryCode: Mapped[Optional[str]] = mapped_column(String(50), comment="东方财富行业代码")
indvInduCode: Mapped[Optional[str]] = mapped_column(String(50), comment="个性化行业代码")
indvInduName: Mapped[Optional[str]] = mapped_column(String(100), comment="个性化行业名称")
emRatingCode: Mapped[Optional[str]] = mapped_column(String(50), comment="东方财富评级代码")
emRatingValue: Mapped[Optional[str]] = mapped_column(String(50), comment="东方财富评级值")
emRatingName: Mapped[Optional[str]] = mapped_column(String(100), comment="东方财富评级名称")
lastEmRatingCode: Mapped[Optional[str]] = mapped_column(String(50), comment="上次东方财富评级代码")
lastEmRatingValue: Mapped[Optional[str]] = mapped_column(String(50), comment="上次东方财富评级值")
lastEmRatingName: Mapped[Optional[str]] = mapped_column(String(100), comment="上次东方财富评级名称")
ratingChange: Mapped[Optional[int]] = mapped_column(Integer, comment="评级变动")
reportType: Mapped[Optional[int]] = mapped_column(Integer, comment="报告类型")
author: Mapped[Optional[str]] = mapped_column(String(255), comment="作者(逗号分隔)")
indvIsNew: Mapped[Optional[str]] = mapped_column(String(20), comment="是否为新研报")
researcher: Mapped[Optional[str]] = mapped_column(String(255), comment="研究员")
newListingDate: Mapped[Optional[str]] = mapped_column(String(50), comment="新上市日期")
newPurchaseDate: Mapped[Optional[str]] = mapped_column(String(50), comment="新买入日期")
newIssuePrice: Mapped[Optional[float]] = mapped_column(Float, comment="新发行价格")
newPeIssueA: Mapped[Optional[float]] = mapped_column(Float, comment="新发行市盈率A")
indvAimPriceT: Mapped[Optional[str]] = mapped_column(String(50), comment="目标价上限")
indvAimPriceL: Mapped[Optional[str]] = mapped_column(String(50), comment="目标价下限")
attachType: Mapped[Optional[str]] = mapped_column(String(50), comment="附件类型")
attachSize: Mapped[Optional[int]] = mapped_column(Integer, comment="附件大小")
attachPages: Mapped[Optional[int]] = mapped_column(Integer, comment="附件页数")
encodeUrl: Mapped[Optional[str]] = mapped_column(String(512), comment="加密URL")
sRatingName: Mapped[Optional[str]] = mapped_column(String(100), comment="评级名称")
sRatingCode: Mapped[Optional[str]] = mapped_column(String(50), comment="评级代码")
market: Mapped[Optional[str]] = mapped_column(String(50), comment="市场")
authorID: Mapped[Optional[str]] = mapped_column(String(255), comment="作者ID逗号分隔")
count_all: Mapped[Optional[int]] = mapped_column(Integer, comment="计数")
orgType: Mapped[Optional[str]] = mapped_column(String(50), comment="机构类型")
created_at: Mapped[datetime] = mapped_column(DateTime, default=func.now(), comment="创建时间")
updated_at: Mapped[datetime] = mapped_column(DateTime, default=func.now(), onupdate=func.now(), comment="更新时间")
class ReportsIndustry(Base):
__tablename__ = "reports_industry"
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True, comment="自增主键")
infoCode: Mapped[str] = mapped_column(String(255), unique=True, comment="报告唯一标识")
title: Mapped[Optional[str]] = mapped_column(String(512), comment="报告标题")
stockName: Mapped[Optional[str]] = mapped_column(String(100), comment="股票名称")
stockCode: Mapped[Optional[str]] = mapped_column(String(50), comment="股票代码")
orgCode: Mapped[Optional[str]] = mapped_column(String(50), comment="机构代码")
orgName: Mapped[Optional[str]] = mapped_column(String(255), comment="机构全称")
orgSName: Mapped[Optional[str]] = mapped_column(String(100), comment="机构简称")
publishDate: Mapped[Optional[str]] = mapped_column(String(50), comment="发布日期")
curr_column: Mapped[Optional[str]] = mapped_column(String(255), comment="栏目") # 注意column是SQL关键字
predictNextTwoYearEps: Mapped[Optional[str]] = mapped_column(String(50), comment="未来两年EPS预测")
predictNextTwoYearPe: Mapped[Optional[str]] = mapped_column(String(50), comment="未来两年PE预测")
predictNextYearEps: Mapped[Optional[str]] = mapped_column(String(50), comment="明年EPS预测")
predictNextYearPe: Mapped[Optional[str]] = mapped_column(String(50), comment="明年PE预测")
predictThisYearEps: Mapped[Optional[str]] = mapped_column(String(50), comment="今年EPS预测")
predictThisYearPe: Mapped[Optional[str]] = mapped_column(String(50), comment="今年PE预测")
predictLastYearEps: Mapped[Optional[str]] = mapped_column(String(50), comment="去年EPS预测")
predictLastYearPe: Mapped[Optional[str]] = mapped_column(String(50), comment="去年PE预测")
actualLastTwoYearEps: Mapped[Optional[str]] = mapped_column(String(50), comment="前两年实际EPS")
actualLastYearEps: Mapped[Optional[str]] = mapped_column(String(50), comment="去年实际EPS")
industryCode: Mapped[Optional[str]] = mapped_column(String(50), comment="行业代码")
industryName: Mapped[Optional[str]] = mapped_column(String(100), comment="行业名称")
emIndustryCode: Mapped[Optional[str]] = mapped_column(String(50), comment="东方财富行业代码")
indvInduCode: Mapped[Optional[str]] = mapped_column(String(50), comment="个性化行业代码")
indvInduName: Mapped[Optional[str]] = mapped_column(String(100), comment="个性化行业名称")
emRatingCode: Mapped[Optional[str]] = mapped_column(String(50), comment="东方财富评级代码")
emRatingValue: Mapped[Optional[str]] = mapped_column(String(50), comment="东方财富评级值")
emRatingName: Mapped[Optional[str]] = mapped_column(String(100), comment="东方财富评级名称")
lastEmRatingCode: Mapped[Optional[str]] = mapped_column(String(50), comment="上次东方财富评级代码")
lastEmRatingValue: Mapped[Optional[str]] = mapped_column(String(50), comment="上次东方财富评级值")
lastEmRatingName: Mapped[Optional[str]] = mapped_column(String(100), comment="上次东方财富评级名称")
ratingChange: Mapped[Optional[str]] = mapped_column(String(50), comment="评级变动")
reportType: Mapped[Optional[int]] = mapped_column(Integer, comment="报告类型")
author: Mapped[Optional[str]] = mapped_column(String(255), comment="作者(逗号分隔)")
indvIsNew: Mapped[Optional[str]] = mapped_column(String(20), comment="是否为新研报")
researcher: Mapped[Optional[str]] = mapped_column(String(255), comment="研究员")
newListingDate: Mapped[Optional[str]] = mapped_column(String(50), comment="新上市日期")
newPurchaseDate: Mapped[Optional[str]] = mapped_column(String(50), comment="新买入日期")
newIssuePrice: Mapped[Optional[str]] = mapped_column(String(50), comment="新发行价格")
newPeIssueA: Mapped[Optional[str]] = mapped_column(String(50), comment="新发行市盈率A")
indvAimPriceT: Mapped[Optional[str]] = mapped_column(String(50), comment="目标价上限")
indvAimPriceL: Mapped[Optional[str]] = mapped_column(String(50), comment="目标价下限")
attachType: Mapped[Optional[str]] = mapped_column(String(50), comment="附件类型")
attachSize: Mapped[Optional[int]] = mapped_column(Integer, comment="附件大小")
attachPages: Mapped[Optional[int]] = mapped_column(Integer, comment="附件页数")
encodeUrl: Mapped[Optional[str]] = mapped_column(String(512), comment="加密URL")
sRatingName: Mapped[Optional[str]] = mapped_column(String(100), comment="评级名称")
sRatingCode: Mapped[Optional[str]] = mapped_column(String(50), comment="评级代码")
market: Mapped[Optional[str]] = mapped_column(String(50), comment="市场")
authorID: Mapped[Optional[str]] = mapped_column(String(255), comment="作者ID逗号分隔")
count_all: Mapped[Optional[int]] = mapped_column(Integer, comment="计数")
orgType: Mapped[Optional[str]] = mapped_column(String(50), comment="机构类型")
created_at: Mapped[datetime] = mapped_column(DateTime, default=func.now(), comment="创建时间")
updated_at: Mapped[datetime] = mapped_column(DateTime, default=func.now(), onupdate=func.now(), comment="更新时间")
class ReportsNewstrock(Base):
__tablename__ = "reports_newstrock" # 注意原表名可能存在笔误应为newstock
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True, comment="自增主键")
infoCode: Mapped[str] = mapped_column(String(255), unique=True, comment="报告唯一标识")
title: Mapped[Optional[str]] = mapped_column(String(512), comment="报告标题")
stockName: Mapped[Optional[str]] = mapped_column(String(100), comment="股票名称")
stockCode: Mapped[Optional[str]] = mapped_column(String(50), comment="股票代码")
orgCode: Mapped[Optional[str]] = mapped_column(String(50), comment="机构代码")
orgName: Mapped[Optional[str]] = mapped_column(String(255), comment="机构全称")
orgSName: Mapped[Optional[str]] = mapped_column(String(100), comment="机构简称")
publishDate: Mapped[Optional[str]] = mapped_column(String(50), comment="发布日期")
curr_column: Mapped[Optional[str]] = mapped_column(String(255), comment="栏目") # 注意column是SQL关键字
actualLastTwoYearEps: Mapped[Optional[str]] = mapped_column(String(50), comment="前两年实际EPS")
actualLastYearEps: Mapped[Optional[str]] = mapped_column(String(50), comment="去年实际EPS")
industryCode: Mapped[Optional[str]] = mapped_column(String(50), comment="行业代码")
industryName: Mapped[Optional[str]] = mapped_column(String(100), comment="行业名称")
emIndustryCode: Mapped[Optional[str]] = mapped_column(String(50), comment="东方财富行业代码")
indvInduCode: Mapped[Optional[str]] = mapped_column(String(50), comment="个性化行业代码")
indvInduName: Mapped[Optional[str]] = mapped_column(String(100), comment="个性化行业名称")
emRatingCode: Mapped[Optional[str]] = mapped_column(String(50), comment="东方财富评级代码")
emRatingValue: Mapped[Optional[str]] = mapped_column(String(50), comment="东方财富评级值")
emRatingName: Mapped[Optional[str]] = mapped_column(String(100), comment="东方财富评级名称")
lastEmRatingCode: Mapped[Optional[str]] = mapped_column(String(50), comment="上次东方财富评级代码")
lastEmRatingValue: Mapped[Optional[str]] = mapped_column(String(50), comment="上次东方财富评级值")
lastEmRatingName: Mapped[Optional[str]] = mapped_column(String(100), comment="上次东方财富评级名称")
ratingChange: Mapped[Optional[str]] = mapped_column(String(50), comment="评级变动")
reportType: Mapped[Optional[int]] = mapped_column(Integer, comment="报告类型")
author: Mapped[Optional[str]] = mapped_column(String(255), comment="作者(逗号分隔)")
indvIsNew: Mapped[Optional[str]] = mapped_column(String(20), comment="是否为新研报")
researcher: Mapped[Optional[str]] = mapped_column(String(255), comment="研究员")
newListingDate: Mapped[Optional[str]] = mapped_column(String(50), comment="新上市日期")
newPurchaseDate: Mapped[Optional[str]] = mapped_column(String(50), comment="新买入日期")
newIssuePrice: Mapped[Optional[float]] = mapped_column(Float, comment="新发行价格")
newPeIssueA: Mapped[Optional[float]] = mapped_column(Float, comment="新发行市盈率A")
indvAimPriceT: Mapped[Optional[str]] = mapped_column(String(50), comment="目标价上限")
indvAimPriceL: Mapped[Optional[str]] = mapped_column(String(50), comment="目标价下限")
attachType: Mapped[Optional[str]] = mapped_column(String(50), comment="附件类型")
attachSize: Mapped[Optional[int]] = mapped_column(Integer, comment="附件大小")
attachPages: Mapped[Optional[int]] = mapped_column(Integer, comment="附件页数")
encodeUrl: Mapped[Optional[str]] = mapped_column(String(512), comment="加密URL")
sRatingName: Mapped[Optional[str]] = mapped_column(String(100), comment="评级名称")
sRatingCode: Mapped[Optional[str]] = mapped_column(String(50), comment="评级代码")
market: Mapped[Optional[str]] = mapped_column(String(50), comment="市场")
newStockSort: Mapped[Optional[str]] = mapped_column(String(50), comment="新股分类")
authorID: Mapped[Optional[str]] = mapped_column(String(255), comment="作者ID逗号分隔")
count_all: Mapped[Optional[int]] = mapped_column(Integer, comment="计数")
orgType: Mapped[Optional[str]] = mapped_column(String(50), comment="机构类型")
created_at: Mapped[datetime] = mapped_column(DateTime, default=func.now(), comment="创建时间")
updated_at: Mapped[datetime] = mapped_column(DateTime, default=func.now(), onupdate=func.now(), comment="更新时间")
class ReportsStrategy(Base):
__tablename__ = "reports_strategy"
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True, comment="自增主键")
infoCode: Mapped[str] = mapped_column(String(255), unique=True, comment="报告唯一标识")
title: Mapped[Optional[str]] = mapped_column(String(512), comment="报告标题")
author: Mapped[Optional[str]] = mapped_column(String(255), comment="作者(逗号分隔)")
orgName: Mapped[Optional[str]] = mapped_column(String(255), comment="机构全称")
orgCode: Mapped[Optional[str]] = mapped_column(String(50), comment="机构代码")
orgSName: Mapped[Optional[str]] = mapped_column(String(100), comment="机构简称")
publishDate: Mapped[Optional[str]] = mapped_column(String(50), comment="发布日期")
encodeUrl: Mapped[Optional[str]] = mapped_column(String(512), comment="加密URL")
researcher: Mapped[Optional[str]] = mapped_column(String(255), comment="研究员")
market: Mapped[Optional[str]] = mapped_column(String(50), comment="市场")
industryCode: Mapped[Optional[str]] = mapped_column(String(50), comment="行业代码")
industryName: Mapped[Optional[str]] = mapped_column(String(100), comment="行业名称")
authorID: Mapped[Optional[str]] = mapped_column(String(255), comment="作者ID逗号分隔")
count_all: Mapped[Optional[int]] = mapped_column(Integer, comment="计数")
orgType: Mapped[Optional[str]] = mapped_column(String(50), comment="机构类型")
created_at: Mapped[datetime] = mapped_column(DateTime, default=func.now(), comment="创建时间")
updated_at: Mapped[datetime] = mapped_column(DateTime, default=func.now(), onupdate=func.now(), comment="更新时间")
stockName: Mapped[str] = mapped_column(String(100), default="", nullable=False, comment="股票名称(默认空字符串)")
stockCode: Mapped[str] = mapped_column(String(50), default="", nullable=False, comment="股票代码(默认空字符串)")
attachPages: Mapped[Optional[int]] = mapped_column(Integer, comment="附件页数")
class ReportsMacresearch(Base):
__tablename__ = "reports_macresearch"
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True, comment="自增主键")
infoCode: Mapped[str] = mapped_column(String(255), unique=True, comment="报告唯一标识")
json_id: Mapped[Optional[str]] = mapped_column(String(50), comment="JSON ID")
title: Mapped[Optional[str]] = mapped_column(String(512), comment="报告标题")
author: Mapped[Optional[str]] = mapped_column(String(255), comment="作者(逗号分隔)")
orgName: Mapped[Optional[str]] = mapped_column(String(255), comment="机构全称")
orgCode: Mapped[Optional[str]] = mapped_column(String(50), comment="机构代码")
orgSName: Mapped[Optional[str]] = mapped_column(String(100), comment="机构简称")
publishDate: Mapped[Optional[str]] = mapped_column(String(50), comment="发布日期")
encodeUrl: Mapped[Optional[str]] = mapped_column(String(512), comment="加密URL")
researcher: Mapped[Optional[str]] = mapped_column(String(255), comment="研究员")
market: Mapped[Optional[str]] = mapped_column(String(50), comment="市场")
industryCode: Mapped[Optional[str]] = mapped_column(String(50), comment="行业代码")
industryName: Mapped[Optional[str]] = mapped_column(String(100), comment="行业名称")
authorID: Mapped[Optional[str]] = mapped_column(String(255), comment="作者ID逗号分隔")
count_all: Mapped[Optional[int]] = mapped_column(Integer, comment="计数")
orgType: Mapped[Optional[str]] = mapped_column(String(50), comment="机构类型")
created_at: Mapped[datetime] = mapped_column(DateTime, default=func.now(), comment="创建时间")
updated_at: Mapped[datetime] = mapped_column(DateTime, default=func.now(), onupdate=func.now(), comment="更新时间")
stockCode: Mapped[str] = mapped_column(String(50), default="", nullable=False, comment="股票代码(默认空字符串)")
stockName: Mapped[str] = mapped_column(String(100), default="", nullable=False, comment="股票名称(默认空字符串)")
attachPages: Mapped[Optional[int]] = mapped_column(Integer, comment="附件页数")