0723 PPT加海报功能01版本修复
This commit is contained in:
parent
5d28e4518e
commit
effc2a24e4
BIN
api/instance/baodan.db
Normal file
BIN
api/instance/baodan.db
Normal file
Binary file not shown.
@ -1,5 +1,6 @@
|
|||||||
"""PPT/海报管理后台服务。"""
|
"""PPT/海报管理后台服务。"""
|
||||||
import json
|
import json
|
||||||
|
import logging
|
||||||
import os
|
import os
|
||||||
import uuid
|
import uuid
|
||||||
from flask import current_app, request
|
from flask import current_app, request
|
||||||
@ -12,6 +13,8 @@ from insurance.models.poster_record import PosterRecord
|
|||||||
from insurance.models.system_setting import SystemSetting
|
from insurance.models.system_setting import SystemSetting
|
||||||
from sqlalchemy import or_
|
from sqlalchemy import or_
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def _safe_page_params(params: dict) -> tuple[int, int]:
|
def _safe_page_params(params: dict) -> tuple[int, int]:
|
||||||
"""安全获取分页参数,防止负数和超大值。"""
|
"""安全获取分页参数,防止负数和超大值。"""
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
"""保单智能客服系统 — 独立后端入口
|
"""淇濆崟鏅鸿兘瀹㈡湇绯荤粺 鈥?鐙珛鍚庣鍏ュ彛
|
||||||
|
|
||||||
独立部署时使用的 Flask 应用,不依赖 Dify 扩展。
|
鐙珛閮ㄧ讲鏃朵娇鐢ㄧ殑 Flask 搴旂敤锛屼笉渚濊禆 Dify 鎵╁睍銆?"""
|
||||||
"""
|
|
||||||
import os
|
import os
|
||||||
import logging
|
import logging
|
||||||
from logging.handlers import RotatingFileHandler
|
from logging.handlers import RotatingFileHandler
|
||||||
@ -9,26 +8,25 @@ from flask import Flask
|
|||||||
from flask_cors import CORS
|
from flask_cors import CORS
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
|
|
||||||
# 加载环境变量
|
# 鍔犺浇鐜鍙橀噺
|
||||||
load_dotenv()
|
load_dotenv()
|
||||||
|
|
||||||
|
|
||||||
def setup_logging(app: Flask) -> None:
|
def setup_logging(app: Flask) -> None:
|
||||||
"""配置统一的日志系统。"""
|
"""閰嶇疆缁熶竴鐨勬棩蹇楃郴缁熴€?""
|
||||||
log_level = app.config.get('LOG_LEVEL', 'INFO')
|
log_level = app.config.get('LOG_LEVEL', 'INFO')
|
||||||
log_file = app.config.get('LOG_FILE', 'logs/insurance.log')
|
log_file = app.config.get('LOG_FILE', 'logs/insurance.log')
|
||||||
|
|
||||||
# 创建日志目录
|
# 鍒涘缓鏃ュ織鐩綍
|
||||||
log_dir = os.path.dirname(log_file)
|
log_dir = os.path.dirname(log_file)
|
||||||
if log_dir and not os.path.exists(log_dir):
|
if log_dir and not os.path.exists(log_dir):
|
||||||
os.makedirs(log_dir, exist_ok=True)
|
os.makedirs(log_dir, exist_ok=True)
|
||||||
|
|
||||||
# 配置根日志记录器
|
# 閰嶇疆鏍规棩蹇楄褰曞櫒
|
||||||
root_logger = logging.getLogger()
|
root_logger = logging.getLogger()
|
||||||
root_logger.setLevel(getattr(logging, log_level.upper(), logging.INFO))
|
root_logger.setLevel(getattr(logging, log_level.upper(), logging.INFO))
|
||||||
|
|
||||||
# 控制台输出
|
# 鎺у埗鍙拌緭鍑? console_handler = logging.StreamHandler()
|
||||||
console_handler = logging.StreamHandler()
|
|
||||||
console_handler.setLevel(logging.INFO)
|
console_handler.setLevel(logging.INFO)
|
||||||
console_format = logging.Formatter(
|
console_format = logging.Formatter(
|
||||||
'%(asctime)s [%(levelname)s] %(name)s: %(message)s',
|
'%(asctime)s [%(levelname)s] %(name)s: %(message)s',
|
||||||
@ -37,8 +35,7 @@ def setup_logging(app: Flask) -> None:
|
|||||||
console_handler.setFormatter(console_format)
|
console_handler.setFormatter(console_format)
|
||||||
root_logger.addHandler(console_handler)
|
root_logger.addHandler(console_handler)
|
||||||
|
|
||||||
# 文件输出(带轮转)
|
# 鏂囦欢杈撳嚭锛堝甫杞浆锛? try:
|
||||||
try:
|
|
||||||
file_handler = RotatingFileHandler(
|
file_handler = RotatingFileHandler(
|
||||||
log_file,
|
log_file,
|
||||||
maxBytes=10 * 1024 * 1024, # 10MB
|
maxBytes=10 * 1024 * 1024, # 10MB
|
||||||
@ -53,9 +50,9 @@ def setup_logging(app: Flask) -> None:
|
|||||||
file_handler.setFormatter(file_format)
|
file_handler.setFormatter(file_format)
|
||||||
root_logger.addHandler(file_handler)
|
root_logger.addHandler(file_handler)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"[logging] 文件日志配置失败: {e}")
|
print(f"[logging] 鏂囦欢鏃ュ織閰嶇疆澶辫触: {e}")
|
||||||
|
|
||||||
# 将 print 替换为 logging
|
# 灏?print 鏇挎崲涓?logging
|
||||||
import builtins
|
import builtins
|
||||||
original_print = builtins.print
|
original_print = builtins.print
|
||||||
def logging_print(*args, **kwargs):
|
def logging_print(*args, **kwargs):
|
||||||
@ -65,22 +62,22 @@ def setup_logging(app: Flask) -> None:
|
|||||||
|
|
||||||
|
|
||||||
def create_app() -> Flask:
|
def create_app() -> Flask:
|
||||||
"""创建 Flask 应用。"""
|
"""鍒涘缓 Flask 搴旂敤銆?""
|
||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
|
|
||||||
# 配置
|
# 閰嶇疆
|
||||||
app.config['SECRET_KEY'] = os.getenv('JWT_SECRET', 'baodan-jwt-secret-2026')
|
app.config['SECRET_KEY'] = os.getenv('JWT_SECRET', 'baodan-jwt-secret-2026')
|
||||||
app.config['JWT_SECRET'] = os.getenv('JWT_SECRET', 'baodan-jwt-secret-2026')
|
app.config['JWT_SECRET'] = os.getenv('JWT_SECRET', 'baodan-jwt-secret-2026')
|
||||||
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
|
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
|
||||||
app.config['REDIS_URL'] = os.getenv('REDIS_URL')
|
app.config['REDIS_URL'] = os.getenv('REDIS_URL')
|
||||||
|
|
||||||
# 配置日志
|
# 閰嶇疆鏃ュ織
|
||||||
setup_logging(app)
|
setup_logging(app)
|
||||||
|
|
||||||
# 数据库:优先 DATABASE_URL,其次 DB_* 变量构建 PostgreSQL URL,最后回退到 SQLite
|
# 鏁版嵁搴擄細浼樺厛 DATABASE_URL锛屽叾娆?DB_* 鍙橀噺鏋勫缓 PostgreSQL URL锛屾渶鍚庡洖閫€鍒?SQLite
|
||||||
db_url = os.getenv('DATABASE_URL', '')
|
db_url = os.getenv('DATABASE_URL', '')
|
||||||
if not db_url:
|
if not db_url:
|
||||||
# 从 DB_* 环境变量构建 PostgreSQL URL
|
# 浠?DB_* 鐜鍙橀噺鏋勫缓 PostgreSQL URL
|
||||||
db_host = os.getenv('DB_HOST', '')
|
db_host = os.getenv('DB_HOST', '')
|
||||||
db_port = os.getenv('DB_PORT', '5432')
|
db_port = os.getenv('DB_PORT', '5432')
|
||||||
db_user = os.getenv('DB_USERNAME', '')
|
db_user = os.getenv('DB_USERNAME', '')
|
||||||
@ -89,55 +86,53 @@ def create_app() -> Flask:
|
|||||||
if db_host and db_user and db_name:
|
if db_host and db_user and db_name:
|
||||||
db_url = f"postgresql://{db_user}:{db_pass}@{db_host}:{db_port}/{db_name}"
|
db_url = f"postgresql://{db_user}:{db_pass}@{db_host}:{db_port}/{db_name}"
|
||||||
if db_url and 'postgresql' in db_url:
|
if db_url and 'postgresql' in db_url:
|
||||||
# 检查 psycopg2 是否可用
|
# 妫€鏌?psycopg2 鏄惁鍙敤
|
||||||
try:
|
try:
|
||||||
import psycopg2
|
import psycopg2
|
||||||
app.config['SQLALCHEMY_DATABASE_URI'] = db_url
|
app.config['SQLALCHEMY_DATABASE_URI'] = db_url
|
||||||
except ImportError:
|
except ImportError:
|
||||||
print("[warning] psycopg2 未安装,回退到 SQLite")
|
print("[warning] psycopg2 鏈畨瑁咃紝鍥為€€鍒?SQLite")
|
||||||
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///baodan.db'
|
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///baodan.db'
|
||||||
else:
|
else:
|
||||||
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///baodan.db'
|
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///baodan.db'
|
||||||
app.config['MIGRATION_ENABLED'] = True
|
app.config['MIGRATION_ENABLED'] = True
|
||||||
|
|
||||||
# Dify 配置
|
# Dify 閰嶇疆
|
||||||
app.config['BAODAN_API_URL'] = os.getenv('DIFY_BASE_URL', 'http://localhost:5001')
|
app.config['BAODAN_API_URL'] = os.getenv('DIFY_BASE_URL', 'http://localhost:5001')
|
||||||
app.config['BAODAN_CHAT_API_KEY'] = os.getenv('DIFY_CHAT_APP_API_KEY', '')
|
app.config['BAODAN_CHAT_API_KEY'] = os.getenv('DIFY_CHAT_APP_API_KEY', '')
|
||||||
app.config['BAODAN_WORKFLOW_API_KEY'] = os.getenv('DIFY_WORKFLOW_APP_API_KEY', '')
|
app.config['BAODAN_WORKFLOW_API_KEY'] = os.getenv('DIFY_WORKFLOW_APP_API_KEY', '')
|
||||||
|
|
||||||
# 企业微信配置
|
# 浼佷笟寰俊閰嶇疆
|
||||||
app.config['WECOM_CORP_ID'] = os.getenv('WECOM_CORP_ID', '')
|
app.config['WECOM_CORP_ID'] = os.getenv('WECOM_CORP_ID', '')
|
||||||
app.config['WECOM_SECRET'] = os.getenv('WECOM_SECRET', '')
|
app.config['WECOM_SECRET'] = os.getenv('WECOM_SECRET', '')
|
||||||
app.config['WECOM_TOKEN'] = os.getenv('WECOM_TOKEN', '')
|
app.config['WECOM_TOKEN'] = os.getenv('WECOM_TOKEN', '')
|
||||||
app.config['WECOM_AES_KEY'] = os.getenv('WECOM_AES_KEY', '')
|
app.config['WECOM_AES_KEY'] = os.getenv('WECOM_AES_KEY', '')
|
||||||
app.config['WECOM_WEBHOOK_URL'] = os.getenv('WECOM_WEBHOOK_URL', '')
|
app.config['WECOM_WEBHOOK_URL'] = os.getenv('WECOM_WEBHOOK_URL', '')
|
||||||
|
|
||||||
# 访客模式
|
# 璁垮妯″紡
|
||||||
app.config['GUEST_MODE'] = os.getenv('GUEST_MODE', 'true').lower() == 'true'
|
app.config['GUEST_MODE'] = os.getenv('GUEST_MODE', 'true').lower() == 'true'
|
||||||
|
|
||||||
# 域名(分享链接用)
|
# 鍩熷悕锛堝垎浜摼鎺ョ敤锛? app.config['DOMAIN'] = os.getenv('DOMAIN', 'localhost')
|
||||||
app.config['DOMAIN'] = os.getenv('DOMAIN', 'localhost')
|
|
||||||
|
|
||||||
# CORS
|
# CORS
|
||||||
CORS(app, resources={r"/*": {"origins": "*"}})
|
CORS(app, resources={r"/*": {"origins": "*"}})
|
||||||
|
|
||||||
# 初始化数据库
|
# 鍒濆鍖栨暟鎹簱
|
||||||
from insurance.db.database import init_db
|
from insurance.db.database import init_db
|
||||||
init_db(app)
|
init_db(app)
|
||||||
|
|
||||||
# 初始化 Redis
|
# 鍒濆鍖?Redis
|
||||||
from insurance.db.redis import init_redis
|
from insurance.db.redis import init_redis
|
||||||
init_redis(app)
|
init_redis(app)
|
||||||
|
|
||||||
# 注册路由
|
# 娉ㄥ唽璺敱
|
||||||
from insurance.routes import register_insurance_routes
|
from insurance.routes import register_insurance_routes
|
||||||
register_insurance_routes(app)
|
register_insurance_routes(app)
|
||||||
|
|
||||||
# 注册错误处理器
|
# 娉ㄥ唽閿欒澶勭悊鍣? from insurance.utils.error_handler import register_error_handlers
|
||||||
from insurance.utils.error_handler import register_error_handlers
|
|
||||||
register_error_handlers(app)
|
register_error_handlers(app)
|
||||||
|
|
||||||
# 健康检查(检查实际依赖)
|
# 鍋ュ悍妫€鏌ワ紙妫€鏌ュ疄闄呬緷璧栵級
|
||||||
@app.route('/health')
|
@app.route('/health')
|
||||||
def health():
|
def health():
|
||||||
checks = {'status': 'ok', 'service': 'baodanagent-api'}
|
checks = {'status': 'ok', 'service': 'baodanagent-api'}
|
||||||
@ -163,10 +158,11 @@ def create_app() -> Flask:
|
|||||||
return app
|
return app
|
||||||
|
|
||||||
|
|
||||||
# 创建应用实例
|
# 鍒涘缓搴旂敤瀹炰緥
|
||||||
app = create_app()
|
app = create_app()
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
port = int(os.getenv('PORT', 5001))
|
port = int(os.getenv('PORT', 5001))
|
||||||
debug = os.getenv('FLASK_DEBUG', 'false').lower() == 'true'
|
debug = os.getenv('FLASK_DEBUG', 'false').lower() == 'true'
|
||||||
app.run(host='0.0.0.0', port=port, debug=debug)
|
app.run(host='0.0.0.0', port=port, debug=debug)
|
||||||
|
|
||||||
|
|||||||
@ -30,7 +30,10 @@ def init_db(app: Flask) -> None:
|
|||||||
chat_record, chat_session, role, prompt,
|
chat_record, chat_session, role, prompt,
|
||||||
template, notification, department,
|
template, notification, department,
|
||||||
wecom_group, email_verification,
|
wecom_group, email_verification,
|
||||||
ppt_session, ppt_config,
|
ppt_session, ppt_config, ppt_history,
|
||||||
|
poster_case_upload, poster_template_model,
|
||||||
|
poster_copy_template, poster_record,
|
||||||
|
system_setting,
|
||||||
)
|
)
|
||||||
db.create_all()
|
db.create_all()
|
||||||
logger.info("数据库初始化完成")
|
logger.info("数据库初始化完成")
|
||||||
|
|||||||
@ -28,11 +28,212 @@ def _add_column(table, column_def, db):
|
|||||||
logger.warning(f"添加列 {table}.{column_def} 失败: {e}")
|
logger.warning(f"添加列 {table}.{column_def} 失败: {e}")
|
||||||
|
|
||||||
|
|
||||||
|
def _table_exists(table_name, db):
|
||||||
|
"""检查表是否存在。"""
|
||||||
|
from sqlalchemy import text
|
||||||
|
try:
|
||||||
|
dialect = db.engine.dialect.name
|
||||||
|
if dialect == 'postgresql':
|
||||||
|
result = db.session.execute(text(
|
||||||
|
"SELECT EXISTS (SELECT FROM information_schema.tables WHERE table_name = :name)"
|
||||||
|
), {"name": table_name})
|
||||||
|
return result.fetchone()[0]
|
||||||
|
else:
|
||||||
|
# SQLite
|
||||||
|
result = db.session.execute(text(
|
||||||
|
"SELECT name FROM sqlite_master WHERE type='table' AND name=:name"
|
||||||
|
), {"name": table_name})
|
||||||
|
return result.fetchone() is not None
|
||||||
|
except Exception:
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def _get_create_table_sql_postgres():
|
||||||
|
"""返回 PostgreSQL 的 CREATE TABLE 语句。"""
|
||||||
|
return {
|
||||||
|
"insurance_ppt_history": """
|
||||||
|
CREATE TABLE IF NOT EXISTS insurance_ppt_history (
|
||||||
|
id BIGSERIAL PRIMARY KEY,
|
||||||
|
user_id VARCHAR(50) NOT NULL,
|
||||||
|
session_id VARCHAR(50),
|
||||||
|
action_type VARCHAR(20) NOT NULL,
|
||||||
|
company_id VARCHAR(50),
|
||||||
|
product_id VARCHAR(50),
|
||||||
|
template_id VARCHAR(50),
|
||||||
|
content_snapshot TEXT,
|
||||||
|
file_url VARCHAR(500),
|
||||||
|
ip VARCHAR(50),
|
||||||
|
user_agent VARCHAR(500),
|
||||||
|
created_at TIMESTAMP DEFAULT NOW()
|
||||||
|
)
|
||||||
|
""",
|
||||||
|
"poster_case_uploads": """
|
||||||
|
CREATE TABLE IF NOT EXISTS poster_case_uploads (
|
||||||
|
id BIGSERIAL PRIMARY KEY,
|
||||||
|
user_id VARCHAR(50) NOT NULL,
|
||||||
|
product_id VARCHAR(50) NOT NULL,
|
||||||
|
source_file_url VARCHAR(500) NOT NULL,
|
||||||
|
parse_status VARCHAR(20) DEFAULT 'pending',
|
||||||
|
parsed_data TEXT,
|
||||||
|
confirmed_data TEXT,
|
||||||
|
confirmed_by VARCHAR(50),
|
||||||
|
confirmed_at TIMESTAMP,
|
||||||
|
created_at TIMESTAMP DEFAULT NOW()
|
||||||
|
)
|
||||||
|
""",
|
||||||
|
"poster_templates": """
|
||||||
|
CREATE TABLE IF NOT EXISTS poster_templates (
|
||||||
|
id BIGSERIAL PRIMARY KEY,
|
||||||
|
name VARCHAR(100) NOT NULL,
|
||||||
|
scenario_tag VARCHAR(50),
|
||||||
|
style_description TEXT NOT NULL,
|
||||||
|
color_scheme TEXT,
|
||||||
|
reference_image VARCHAR(500),
|
||||||
|
preview_image VARCHAR(500),
|
||||||
|
status SMALLINT DEFAULT 1,
|
||||||
|
created_at TIMESTAMP DEFAULT NOW(),
|
||||||
|
updated_at TIMESTAMP DEFAULT NOW()
|
||||||
|
)
|
||||||
|
""",
|
||||||
|
"poster_copy_templates": """
|
||||||
|
CREATE TABLE IF NOT EXISTS poster_copy_templates (
|
||||||
|
id BIGSERIAL PRIMARY KEY,
|
||||||
|
name VARCHAR(100) NOT NULL,
|
||||||
|
scenario_tag VARCHAR(50),
|
||||||
|
content TEXT NOT NULL,
|
||||||
|
variables TEXT,
|
||||||
|
status SMALLINT DEFAULT 1,
|
||||||
|
created_at TIMESTAMP DEFAULT NOW()
|
||||||
|
)
|
||||||
|
""",
|
||||||
|
"poster_records": """
|
||||||
|
CREATE TABLE IF NOT EXISTS poster_records (
|
||||||
|
id BIGSERIAL PRIMARY KEY,
|
||||||
|
user_id VARCHAR(50) NOT NULL,
|
||||||
|
product_id VARCHAR(50),
|
||||||
|
case_upload_id BIGINT,
|
||||||
|
template_id BIGINT,
|
||||||
|
copy_mode VARCHAR(20),
|
||||||
|
copy_content TEXT,
|
||||||
|
ai_raw_content TEXT,
|
||||||
|
export_url VARCHAR(500),
|
||||||
|
export_format VARCHAR(10),
|
||||||
|
export_size VARCHAR(20),
|
||||||
|
reference_image_used VARCHAR(500),
|
||||||
|
prompt_used TEXT,
|
||||||
|
created_at TIMESTAMP DEFAULT NOW()
|
||||||
|
)
|
||||||
|
""",
|
||||||
|
"system_settings": """
|
||||||
|
CREATE TABLE IF NOT EXISTS system_settings (
|
||||||
|
id BIGSERIAL PRIMARY KEY,
|
||||||
|
key VARCHAR(100) UNIQUE NOT NULL,
|
||||||
|
value TEXT NOT NULL,
|
||||||
|
description VARCHAR(500),
|
||||||
|
updated_by VARCHAR(50),
|
||||||
|
updated_at TIMESTAMP DEFAULT NOW()
|
||||||
|
)
|
||||||
|
""",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def _get_create_table_sql_sqlite():
|
||||||
|
"""返回 SQLite 的 CREATE TABLE 语句。"""
|
||||||
|
return {
|
||||||
|
"insurance_ppt_history": """
|
||||||
|
CREATE TABLE IF NOT EXISTS insurance_ppt_history (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
user_id VARCHAR(50) NOT NULL,
|
||||||
|
session_id VARCHAR(50),
|
||||||
|
action_type VARCHAR(20) NOT NULL,
|
||||||
|
company_id VARCHAR(50),
|
||||||
|
product_id VARCHAR(50),
|
||||||
|
template_id VARCHAR(50),
|
||||||
|
content_snapshot TEXT,
|
||||||
|
file_url VARCHAR(500),
|
||||||
|
ip VARCHAR(50),
|
||||||
|
user_agent VARCHAR(500),
|
||||||
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||||
|
)
|
||||||
|
""",
|
||||||
|
"poster_case_uploads": """
|
||||||
|
CREATE TABLE IF NOT EXISTS poster_case_uploads (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
user_id VARCHAR(50) NOT NULL,
|
||||||
|
product_id VARCHAR(50) NOT NULL,
|
||||||
|
source_file_url VARCHAR(500) NOT NULL,
|
||||||
|
parse_status VARCHAR(20) DEFAULT 'pending',
|
||||||
|
parsed_data TEXT,
|
||||||
|
confirmed_data TEXT,
|
||||||
|
confirmed_by VARCHAR(50),
|
||||||
|
confirmed_at TIMESTAMP,
|
||||||
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||||
|
)
|
||||||
|
""",
|
||||||
|
"poster_templates": """
|
||||||
|
CREATE TABLE IF NOT EXISTS poster_templates (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
name VARCHAR(100) NOT NULL,
|
||||||
|
scenario_tag VARCHAR(50),
|
||||||
|
style_description TEXT NOT NULL,
|
||||||
|
color_scheme TEXT,
|
||||||
|
reference_image VARCHAR(500),
|
||||||
|
preview_image VARCHAR(500),
|
||||||
|
status SMALLINT DEFAULT 1,
|
||||||
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||||
|
)
|
||||||
|
""",
|
||||||
|
"poster_copy_templates": """
|
||||||
|
CREATE TABLE IF NOT EXISTS poster_copy_templates (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
name VARCHAR(100) NOT NULL,
|
||||||
|
scenario_tag VARCHAR(50),
|
||||||
|
content TEXT NOT NULL,
|
||||||
|
variables TEXT,
|
||||||
|
status SMALLINT DEFAULT 1,
|
||||||
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||||
|
)
|
||||||
|
""",
|
||||||
|
"poster_records": """
|
||||||
|
CREATE TABLE IF NOT EXISTS poster_records (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
user_id VARCHAR(50) NOT NULL,
|
||||||
|
product_id VARCHAR(50),
|
||||||
|
case_upload_id BIGINT,
|
||||||
|
template_id BIGINT,
|
||||||
|
copy_mode VARCHAR(20),
|
||||||
|
copy_content TEXT,
|
||||||
|
ai_raw_content TEXT,
|
||||||
|
export_url VARCHAR(500),
|
||||||
|
export_format VARCHAR(10),
|
||||||
|
export_size VARCHAR(20),
|
||||||
|
reference_image_used VARCHAR(500),
|
||||||
|
prompt_used TEXT,
|
||||||
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||||
|
)
|
||||||
|
""",
|
||||||
|
"system_settings": """
|
||||||
|
CREATE TABLE IF NOT EXISTS system_settings (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
key VARCHAR(100) UNIQUE NOT NULL,
|
||||||
|
value TEXT NOT NULL,
|
||||||
|
description VARCHAR(500),
|
||||||
|
updated_by VARCHAR(50),
|
||||||
|
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||||
|
)
|
||||||
|
""",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
def migrate():
|
def migrate():
|
||||||
"""迁移入口。"""
|
"""迁移入口。"""
|
||||||
from insurance.db.compat import db
|
from insurance.db.compat import db
|
||||||
from sqlalchemy import text
|
from sqlalchemy import text
|
||||||
|
|
||||||
|
dialect = db.engine.dialect.name
|
||||||
|
is_postgres = dialect == 'postgresql'
|
||||||
|
|
||||||
# ========== 1. 扩展 insurance_ppt_companies ==========
|
# ========== 1. 扩展 insurance_ppt_companies ==========
|
||||||
logger.info("[migrate_015] 扩展 insurance_ppt_companies ...")
|
logger.info("[migrate_015] 扩展 insurance_ppt_companies ...")
|
||||||
for col in [
|
for col in [
|
||||||
@ -60,7 +261,6 @@ def migrate():
|
|||||||
"extra_fields TEXT",
|
"extra_fields TEXT",
|
||||||
"status SMALLINT DEFAULT 1",
|
"status SMALLINT DEFAULT 1",
|
||||||
"sort_order INTEGER DEFAULT 0",
|
"sort_order INTEGER DEFAULT 0",
|
||||||
"updated_at TIMESTAMP DEFAULT NOW()",
|
|
||||||
"manual_file_url VARCHAR(500)",
|
"manual_file_url VARCHAR(500)",
|
||||||
"manual_parse_status VARCHAR(20) DEFAULT 'none'",
|
"manual_parse_status VARCHAR(20) DEFAULT 'none'",
|
||||||
"manual_parsed_rules TEXT",
|
"manual_parsed_rules TEXT",
|
||||||
@ -68,6 +268,11 @@ def migrate():
|
|||||||
"manual_reviewed_at TIMESTAMP",
|
"manual_reviewed_at TIMESTAMP",
|
||||||
]:
|
]:
|
||||||
_add_column("insurance_ppt_products", col, db)
|
_add_column("insurance_ppt_products", col, db)
|
||||||
|
# updated_at 列可能已存在
|
||||||
|
if is_postgres:
|
||||||
|
_add_column("insurance_ppt_products", "updated_at TIMESTAMP DEFAULT NOW()", db)
|
||||||
|
else:
|
||||||
|
_add_column("insurance_ppt_products", "updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP", db)
|
||||||
db.session.execute(text(
|
db.session.execute(text(
|
||||||
"UPDATE insurance_ppt_products SET status = 1 WHERE status IS NULL"
|
"UPDATE insurance_ppt_products SET status = 1 WHERE status IS NULL"
|
||||||
))
|
))
|
||||||
@ -88,133 +293,52 @@ def migrate():
|
|||||||
db.session.commit()
|
db.session.commit()
|
||||||
logger.info("[migrate_015] insurance_ppt_templates 扩展完成")
|
logger.info("[migrate_015] insurance_ppt_templates 扩展完成")
|
||||||
|
|
||||||
# ========== 4. 创建 insurance_ppt_history ==========
|
# ========== 4-9. 创建新表(根据数据库方言选择 SQL) ==========
|
||||||
logger.info("[migrate_015] 创建 insurance_ppt_history ...")
|
sql_map = _get_create_table_sql_postgres() if is_postgres else _get_create_table_sql_sqlite()
|
||||||
db.session.execute(text("""
|
|
||||||
CREATE TABLE IF NOT EXISTS insurance_ppt_history (
|
|
||||||
id BIGSERIAL PRIMARY KEY,
|
|
||||||
user_id VARCHAR(50) NOT NULL,
|
|
||||||
session_id VARCHAR(50),
|
|
||||||
action_type VARCHAR(20) NOT NULL,
|
|
||||||
company_id VARCHAR(50),
|
|
||||||
product_id VARCHAR(50),
|
|
||||||
template_id VARCHAR(50),
|
|
||||||
content_snapshot TEXT,
|
|
||||||
file_url VARCHAR(500),
|
|
||||||
ip VARCHAR(50),
|
|
||||||
user_agent VARCHAR(500),
|
|
||||||
created_at TIMESTAMP DEFAULT NOW()
|
|
||||||
)
|
|
||||||
"""))
|
|
||||||
db.session.execute(text(
|
|
||||||
"CREATE INDEX IF NOT EXISTS idx_ppt_history_user ON insurance_ppt_history(user_id, created_at DESC)"
|
|
||||||
))
|
|
||||||
db.session.commit()
|
|
||||||
logger.info("[migrate_015] insurance_ppt_history 创建完成")
|
|
||||||
|
|
||||||
# ========== 5. 创建 poster_case_uploads ==========
|
for table_name in ["insurance_ppt_history", "poster_case_uploads", "poster_templates",
|
||||||
logger.info("[migrate_015] 创建 poster_case_uploads ...")
|
"poster_copy_templates", "poster_records", "system_settings"]:
|
||||||
db.session.execute(text("""
|
if _table_exists(table_name, db):
|
||||||
CREATE TABLE IF NOT EXISTS poster_case_uploads (
|
logger.info(f"[migrate_015] 表 {table_name} 已存在,跳过创建")
|
||||||
id BIGSERIAL PRIMARY KEY,
|
continue
|
||||||
user_id VARCHAR(50) NOT NULL,
|
logger.info(f"[migrate_015] 创建 {table_name} ...")
|
||||||
product_id VARCHAR(50) NOT NULL,
|
db.session.execute(text(sql_map[table_name]))
|
||||||
source_file_url VARCHAR(500) NOT NULL,
|
|
||||||
parse_status VARCHAR(20) DEFAULT 'pending',
|
|
||||||
parsed_data TEXT,
|
|
||||||
confirmed_data TEXT,
|
|
||||||
confirmed_by VARCHAR(50),
|
|
||||||
confirmed_at TIMESTAMP,
|
|
||||||
created_at TIMESTAMP DEFAULT NOW()
|
|
||||||
)
|
|
||||||
"""))
|
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
logger.info("[migrate_015] poster_case_uploads 创建完成")
|
logger.info(f"[migrate_015] {table_name} 创建完成")
|
||||||
|
|
||||||
# ========== 6. 创建 poster_templates ==========
|
# ========== 索引 ==========
|
||||||
logger.info("[migrate_015] 创建 poster_templates ...")
|
indexes = [
|
||||||
db.session.execute(text("""
|
"CREATE INDEX IF NOT EXISTS idx_ppt_history_user ON insurance_ppt_history(user_id, created_at DESC)",
|
||||||
CREATE TABLE IF NOT EXISTS poster_templates (
|
"CREATE INDEX IF NOT EXISTS idx_poster_records_user ON poster_records(user_id, created_at DESC)",
|
||||||
id BIGSERIAL PRIMARY KEY,
|
]
|
||||||
name VARCHAR(100) NOT NULL,
|
for idx_sql in indexes:
|
||||||
scenario_tag VARCHAR(50),
|
try:
|
||||||
style_description TEXT NOT NULL,
|
db.session.execute(text(idx_sql))
|
||||||
color_scheme TEXT,
|
except Exception as e:
|
||||||
reference_image VARCHAR(500),
|
logger.warning(f"创建索引失败: {e}")
|
||||||
preview_image VARCHAR(500),
|
|
||||||
status SMALLINT DEFAULT 1,
|
|
||||||
created_at TIMESTAMP DEFAULT NOW(),
|
|
||||||
updated_at TIMESTAMP DEFAULT NOW()
|
|
||||||
)
|
|
||||||
"""))
|
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
logger.info("[migrate_015] poster_templates 创建完成")
|
|
||||||
|
|
||||||
# ========== 7. 创建 poster_copy_templates ==========
|
# ========== 种子数据 ==========
|
||||||
logger.info("[migrate_015] 创建 poster_copy_templates ...")
|
logger.info("[migrate_015] 写入种子数据 ...")
|
||||||
db.session.execute(text("""
|
|
||||||
CREATE TABLE IF NOT EXISTS poster_copy_templates (
|
|
||||||
id BIGSERIAL PRIMARY KEY,
|
|
||||||
name VARCHAR(100) NOT NULL,
|
|
||||||
scenario_tag VARCHAR(50),
|
|
||||||
content TEXT NOT NULL,
|
|
||||||
variables TEXT,
|
|
||||||
status SMALLINT DEFAULT 1,
|
|
||||||
created_at TIMESTAMP DEFAULT NOW()
|
|
||||||
)
|
|
||||||
"""))
|
|
||||||
db.session.commit()
|
|
||||||
logger.info("[migrate_015] poster_copy_templates 创建完成")
|
|
||||||
|
|
||||||
# ========== 8. 创建 poster_records ==========
|
|
||||||
logger.info("[migrate_015] 创建 poster_records ...")
|
|
||||||
db.session.execute(text("""
|
|
||||||
CREATE TABLE IF NOT EXISTS poster_records (
|
|
||||||
id BIGSERIAL PRIMARY KEY,
|
|
||||||
user_id VARCHAR(50) NOT NULL,
|
|
||||||
product_id VARCHAR(50),
|
|
||||||
case_upload_id BIGINT,
|
|
||||||
template_id BIGINT,
|
|
||||||
copy_mode VARCHAR(20),
|
|
||||||
copy_content TEXT,
|
|
||||||
ai_raw_content TEXT,
|
|
||||||
export_url VARCHAR(500),
|
|
||||||
export_format VARCHAR(10),
|
|
||||||
export_size VARCHAR(20),
|
|
||||||
reference_image_used VARCHAR(500),
|
|
||||||
prompt_used TEXT,
|
|
||||||
created_at TIMESTAMP DEFAULT NOW()
|
|
||||||
)
|
|
||||||
"""))
|
|
||||||
db.session.execute(text(
|
|
||||||
"CREATE INDEX IF NOT EXISTS idx_poster_records_user ON poster_records(user_id, created_at DESC)"
|
|
||||||
))
|
|
||||||
db.session.commit()
|
|
||||||
logger.info("[migrate_015] poster_records 创建完成")
|
|
||||||
|
|
||||||
# ========== 9. 创建 system_settings + 种子数据 ==========
|
|
||||||
logger.info("[migrate_015] 创建 system_settings ...")
|
|
||||||
db.session.execute(text("""
|
|
||||||
CREATE TABLE IF NOT EXISTS system_settings (
|
|
||||||
id BIGSERIAL PRIMARY KEY,
|
|
||||||
key VARCHAR(100) UNIQUE NOT NULL,
|
|
||||||
value TEXT NOT NULL,
|
|
||||||
description VARCHAR(500),
|
|
||||||
updated_by VARCHAR(50),
|
|
||||||
updated_at TIMESTAMP DEFAULT NOW()
|
|
||||||
)
|
|
||||||
"""))
|
|
||||||
# 种子数据(幂等:已存在则跳过)
|
|
||||||
for key, value, desc in [
|
for key, value, desc in [
|
||||||
("ppt_history_retention_days", "365", "PPT 历史记录保留天数(0=永久保留)"),
|
("ppt_history_retention_days", "365", "PPT 历史记录保留天数(0=永久保留)"),
|
||||||
("poster_history_retention_days", "365", "海报历史记录保留天数(0=永久保留)"),
|
("poster_history_retention_days", "365", "海报历史记录保留天数(0=永久保留)"),
|
||||||
]:
|
]:
|
||||||
|
if is_postgres:
|
||||||
db.session.execute(text(
|
db.session.execute(text(
|
||||||
"INSERT INTO system_settings (key, value, description) "
|
"INSERT INTO system_settings (key, value, description) "
|
||||||
"SELECT :key, :value, :desc "
|
"SELECT :key, :value, :desc "
|
||||||
"WHERE NOT EXISTS (SELECT 1 FROM system_settings WHERE key = :key)"
|
"WHERE NOT EXISTS (SELECT 1 FROM system_settings WHERE key = :key)"
|
||||||
), {"key": key, "value": value, "desc": desc})
|
), {"key": key, "value": value, "desc": desc})
|
||||||
|
else:
|
||||||
|
# SQLite 不支持 SELECT ... INSERT 语法,用 try/except
|
||||||
|
try:
|
||||||
|
db.session.execute(text(
|
||||||
|
"INSERT INTO system_settings (key, value, description) VALUES (:key, :value, :desc)"
|
||||||
|
), {"key": key, "value": value, "desc": desc})
|
||||||
|
except Exception:
|
||||||
|
pass # 已存在则忽略
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
logger.info("[migrate_015] system_settings 创建完成")
|
logger.info("[migrate_015] 种子数据写入完成")
|
||||||
|
|
||||||
logger.info("[migrate_015] 全部迁移完成")
|
logger.info("[migrate_015] 全部迁移完成")
|
||||||
|
|||||||
@ -39,18 +39,10 @@
|
|||||||
<el-icon><Document /></el-icon>
|
<el-icon><Document /></el-icon>
|
||||||
<span>PPT生成</span>
|
<span>PPT生成</span>
|
||||||
</el-menu-item>
|
</el-menu-item>
|
||||||
<el-menu-item index="/ppt/history">
|
|
||||||
<el-icon><Clock /></el-icon>
|
|
||||||
<span>建议书历史</span>
|
|
||||||
</el-menu-item>
|
|
||||||
<el-menu-item index="/poster">
|
<el-menu-item index="/poster">
|
||||||
<el-icon><Picture /></el-icon>
|
<el-icon><Picture /></el-icon>
|
||||||
<span>海报生成</span>
|
<span>海报生成</span>
|
||||||
</el-menu-item>
|
</el-menu-item>
|
||||||
<el-menu-item index="/poster/history">
|
|
||||||
<el-icon><Clock /></el-icon>
|
|
||||||
<span>海报历史</span>
|
|
||||||
</el-menu-item>
|
|
||||||
<!-- <el-menu-item index="/recommend">-->
|
<!-- <el-menu-item index="/recommend">-->
|
||||||
<!-- <el-icon><Document /></el-icon>-->
|
<!-- <el-icon><Document /></el-icon>-->
|
||||||
<!-- <span>产品推荐</span>-->
|
<!-- <span>产品推荐</span>-->
|
||||||
@ -161,18 +153,10 @@
|
|||||||
<el-icon><Document /></el-icon>
|
<el-icon><Document /></el-icon>
|
||||||
<span>PPT生成</span>
|
<span>PPT生成</span>
|
||||||
</el-menu-item>
|
</el-menu-item>
|
||||||
<el-menu-item index="/ppt/history">
|
|
||||||
<el-icon><Clock /></el-icon>
|
|
||||||
<span>建议书历史</span>
|
|
||||||
</el-menu-item>
|
|
||||||
<el-menu-item index="/poster">
|
<el-menu-item index="/poster">
|
||||||
<el-icon><Picture /></el-icon>
|
<el-icon><Picture /></el-icon>
|
||||||
<span>海报生成</span>
|
<span>海报生成</span>
|
||||||
</el-menu-item>
|
</el-menu-item>
|
||||||
<el-menu-item index="/poster/history">
|
|
||||||
<el-icon><Clock /></el-icon>
|
|
||||||
<span>海报历史</span>
|
|
||||||
</el-menu-item>
|
|
||||||
<!-- <el-menu-item index="/recommend">-->
|
<!-- <el-menu-item index="/recommend">-->
|
||||||
<!-- <el-icon><Document /></el-icon>-->
|
<!-- <el-icon><Document /></el-icon>-->
|
||||||
<!-- <span>产品推荐</span>-->
|
<!-- <span>产品推荐</span>-->
|
||||||
@ -520,6 +504,57 @@ html, body, #app {
|
|||||||
color: inherit !important;
|
color: inherit !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 子菜单标题样式 */
|
||||||
|
.sidebar-menu .el-sub-menu__title,
|
||||||
|
.drawer-menu .el-sub-menu__title {
|
||||||
|
color: rgba(255, 255, 255, 0.7) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-menu .el-sub-menu__title:hover,
|
||||||
|
.drawer-menu .el-sub-menu__title:hover {
|
||||||
|
background: rgba(255, 255, 255, 0.1) !important;
|
||||||
|
color: #fff !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-menu .el-sub-menu__title .el-icon,
|
||||||
|
.drawer-menu .el-sub-menu__title .el-icon {
|
||||||
|
color: inherit !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 子菜单弹出/展开区域背景 */
|
||||||
|
.sidebar-menu .el-menu--popup,
|
||||||
|
.drawer-menu .el-menu--popup {
|
||||||
|
background: #1a1a2e !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-menu .el-sub-menu .el-menu,
|
||||||
|
.drawer-menu .el-sub-menu .el-menu {
|
||||||
|
background: transparent !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-menu .el-sub-menu .el-menu-item,
|
||||||
|
.drawer-menu .el-sub-menu .el-menu-item {
|
||||||
|
color: rgba(255, 255, 255, 0.7) !important;
|
||||||
|
background: transparent !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-menu .el-sub-menu .el-menu-item:hover,
|
||||||
|
.drawer-menu .el-sub-menu .el-menu-item:hover {
|
||||||
|
background: rgba(255, 255, 255, 0.1) !important;
|
||||||
|
color: #fff !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-menu .el-sub-menu .el-menu-item.is-active,
|
||||||
|
.drawer-menu .el-sub-menu .el-menu-item.is-active {
|
||||||
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important;
|
||||||
|
color: #fff !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-menu .el-sub-menu .el-menu-item .el-icon,
|
||||||
|
.drawer-menu .el-sub-menu .el-menu-item .el-icon {
|
||||||
|
color: inherit !important;
|
||||||
|
}
|
||||||
|
|
||||||
/* 底部用户信息 */
|
/* 底部用户信息 */
|
||||||
.sidebar-footer {
|
.sidebar-footer {
|
||||||
padding: 16px;
|
padding: 16px;
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
"""修补基础平台 app.py,注册 insurance Blueprint。
|
"""修补基础平台 app.py,注册 insurance Blueprint。
|
||||||
|
|
||||||
在 Docker 构建时执行,将注册代码注入到 app.py 中。
|
在 Docker 构建时执行,将注册代码注入到 app.py 中。
|
||||||
"""
|
"""
|
||||||
APP_PY = "/app/api/app.py"
|
APP_PY = "/app/api/app.py"
|
||||||
@ -7,9 +6,9 @@ APP_PY = "/app/api/app.py"
|
|||||||
INSURANCE_FUNC = (
|
INSURANCE_FUNC = (
|
||||||
"\n# ====== Insurance Blueprint Auto-Register ======\n"
|
"\n# ====== Insurance Blueprint Auto-Register ======\n"
|
||||||
"def _register_insurance(app):\n"
|
"def _register_insurance(app):\n"
|
||||||
' """在基础平台 app 创建后注册 insurance Blueprint。"""\n'
|
' """Register insurance Blueprint after base platform app is created."""\n'
|
||||||
" import os\n"
|
" import os\n"
|
||||||
" # 加载保险模块配置到 Flask app.config\n"
|
" # Load insurance module config into Flask app.config\n"
|
||||||
" for key in ['BAODAN_CHAT_API_KEY', 'BAODAN_WORKFLOW_API_KEY', 'BAODAN_API_URL',\n"
|
" for key in ['BAODAN_CHAT_API_KEY', 'BAODAN_WORKFLOW_API_KEY', 'BAODAN_API_URL',\n"
|
||||||
" 'BAODAN_KB_API_KEY', 'WECOM_CORP_ID', 'WECOM_SECRET',\n"
|
" 'BAODAN_KB_API_KEY', 'WECOM_CORP_ID', 'WECOM_SECRET',\n"
|
||||||
" 'WECOM_TOKEN', 'WECOM_AES_KEY', 'WECOM_WEBHOOK_URL',\n"
|
" 'WECOM_TOKEN', 'WECOM_AES_KEY', 'WECOM_WEBHOOK_URL',\n"
|
||||||
@ -23,6 +22,11 @@ INSURANCE_FUNC = (
|
|||||||
" try:\n"
|
" try:\n"
|
||||||
" from insurance.routes import register_insurance_routes\n"
|
" from insurance.routes import register_insurance_routes\n"
|
||||||
" register_insurance_routes(app)\n"
|
" register_insurance_routes(app)\n"
|
||||||
|
" # Run insurance database migrations on startup\n"
|
||||||
|
" app.config['MIGRATION_ENABLED'] = True\n"
|
||||||
|
" from insurance.db import run_migrations\n"
|
||||||
|
" with app.app_context():\n"
|
||||||
|
" run_migrations()\n"
|
||||||
" except Exception as e:\n"
|
" except Exception as e:\n"
|
||||||
" import logging\n"
|
" import logging\n"
|
||||||
' logging.warning(f"Insurance module not loaded: {e}")\n'
|
' logging.warning(f"Insurance module not loaded: {e}")\n'
|
||||||
@ -38,20 +42,19 @@ def patch_app_py():
|
|||||||
print("[patch_app.py] Already patched, skipping.")
|
print("[patch_app.py] Already patched, skipping.")
|
||||||
return
|
return
|
||||||
|
|
||||||
# 1. 在文件开头插入 _register_insurance 函数定义
|
# 1. Insert _register_insurance function definition before "def is_db_command"
|
||||||
# 插入到 "def is_db_command" 之前
|
|
||||||
marker = "\ndef is_db_command"
|
marker = "\ndef is_db_command"
|
||||||
if marker in content:
|
if marker in content:
|
||||||
content = content.replace(marker, "\n" + INSURANCE_FUNC + "def is_db_command", 1)
|
content = content.replace(marker, "\n" + INSURANCE_FUNC + "def is_db_command", 1)
|
||||||
|
|
||||||
# 2. db 命令分支:app 创建后注册
|
# 2. db command branch: register after app creation
|
||||||
content = content.replace(
|
content = content.replace(
|
||||||
" app = create_migrations_app()\n socketio_app = app",
|
" app = create_migrations_app()\n socketio_app = app",
|
||||||
" app = create_migrations_app()\n _register_insurance(app)\n socketio_app = app",
|
" app = create_migrations_app()\n _register_insurance(app)\n socketio_app = app",
|
||||||
1,
|
1,
|
||||||
)
|
)
|
||||||
|
|
||||||
# 3. 正常启动分支:flask_app 创建后注册
|
# 3. Normal startup branch: register after flask_app creation
|
||||||
content = content.replace(
|
content = content.replace(
|
||||||
" socketio_app, flask_app = create_app()\n app = flask_app",
|
" socketio_app, flask_app = create_app()\n app = flask_app",
|
||||||
" socketio_app, flask_app = create_app()\n _register_insurance(flask_app)\n app = flask_app",
|
" socketio_app, flask_app = create_app()\n _register_insurance(flask_app)\n app = flask_app",
|
||||||
@ -61,7 +64,7 @@ def patch_app_py():
|
|||||||
with open(APP_PY, "w") as f:
|
with open(APP_PY, "w") as f:
|
||||||
f.write(content)
|
f.write(content)
|
||||||
|
|
||||||
print("[patch_app.py] Patched app.py with insurance Blueprint registration.")
|
print("[patch_app.py] Patched app.py with insurance Blueprint registration and migration.")
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user