"""淇濆崟鏅鸿兘瀹㈡湇绯荤粺 鈥?鐙珛鍚庣鍏ュ彛 鐙珛閮ㄧ讲鏃朵娇鐢ㄧ殑 Flask 搴旂敤锛屼笉渚濊禆 Dify 鎵╁睍銆?""" import os import logging from logging.handlers import RotatingFileHandler from flask import Flask from flask_cors import CORS from dotenv import load_dotenv # 鍔犺浇鐜鍙橀噺 load_dotenv() def setup_logging(app: Flask) -> None: """閰嶇疆缁熶竴鐨勬棩蹇楃郴缁熴€?"" log_level = app.config.get('LOG_LEVEL', 'INFO') log_file = app.config.get('LOG_FILE', 'logs/insurance.log') # 鍒涘缓鏃ュ織鐩綍 log_dir = os.path.dirname(log_file) if log_dir and not os.path.exists(log_dir): os.makedirs(log_dir, exist_ok=True) # 閰嶇疆鏍规棩蹇楄褰曞櫒 root_logger = logging.getLogger() root_logger.setLevel(getattr(logging, log_level.upper(), logging.INFO)) # 鎺у埗鍙拌緭鍑? console_handler = logging.StreamHandler() console_handler.setLevel(logging.INFO) console_format = logging.Formatter( '%(asctime)s [%(levelname)s] %(name)s: %(message)s', datefmt='%Y-%m-%d %H:%M:%S' ) console_handler.setFormatter(console_format) root_logger.addHandler(console_handler) # 鏂囦欢杈撳嚭锛堝甫杞浆锛? try: file_handler = RotatingFileHandler( log_file, maxBytes=10 * 1024 * 1024, # 10MB backupCount=5, encoding='utf-8' ) file_handler.setLevel(logging.DEBUG) file_format = logging.Formatter( '%(asctime)s [%(levelname)s] %(name)s [%(filename)s:%(lineno)d]: %(message)s', datefmt='%Y-%m-%d %H:%M:%S' ) file_handler.setFormatter(file_format) root_logger.addHandler(file_handler) except Exception as e: print(f"[logging] 鏂囦欢鏃ュ織閰嶇疆澶辫触: {e}") # 灏?print 鏇挎崲涓?logging import builtins original_print = builtins.print def logging_print(*args, **kwargs): msg = ' '.join(str(arg) for arg in args) logging.info(msg) builtins.print = logging_print def create_app() -> Flask: """鍒涘缓 Flask 搴旂敤銆?"" app = Flask(__name__) # 閰嶇疆 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['SQLALCHEMY_TRACK_MODIFICATIONS'] = False app.config['REDIS_URL'] = os.getenv('REDIS_URL') # 閰嶇疆鏃ュ織 setup_logging(app) # 鏁版嵁搴擄細浼樺厛 DATABASE_URL锛屽叾娆?DB_* 鍙橀噺鏋勫缓 PostgreSQL URL锛屾渶鍚庡洖閫€鍒?SQLite db_url = os.getenv('DATABASE_URL', '') if not db_url: # 浠?DB_* 鐜鍙橀噺鏋勫缓 PostgreSQL URL db_host = os.getenv('DB_HOST', '') db_port = os.getenv('DB_PORT', '5432') db_user = os.getenv('DB_USERNAME', '') db_pass = os.getenv('DB_PASSWORD', '') db_name = os.getenv('DB_DATABASE', '') if db_host and db_user and db_name: db_url = f"postgresql://{db_user}:{db_pass}@{db_host}:{db_port}/{db_name}" if db_url and 'postgresql' in db_url: # 妫€鏌?psycopg2 鏄惁鍙敤 try: import psycopg2 app.config['SQLALCHEMY_DATABASE_URI'] = db_url except ImportError: print("[warning] psycopg2 鏈畨瑁咃紝鍥為€€鍒?SQLite") app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///baodan.db' else: app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///baodan.db' app.config['MIGRATION_ENABLED'] = True # Dify 閰嶇疆 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_WORKFLOW_API_KEY'] = os.getenv('DIFY_WORKFLOW_APP_API_KEY', '') # 浼佷笟寰俊閰嶇疆 app.config['WECOM_CORP_ID'] = os.getenv('WECOM_CORP_ID', '') app.config['WECOM_SECRET'] = os.getenv('WECOM_SECRET', '') app.config['WECOM_TOKEN'] = os.getenv('WECOM_TOKEN', '') app.config['WECOM_AES_KEY'] = os.getenv('WECOM_AES_KEY', '') app.config['WECOM_WEBHOOK_URL'] = os.getenv('WECOM_WEBHOOK_URL', '') # 璁垮妯″紡 app.config['GUEST_MODE'] = os.getenv('GUEST_MODE', 'true').lower() == 'true' # 鍩熷悕锛堝垎浜摼鎺ョ敤锛? app.config['DOMAIN'] = os.getenv('DOMAIN', 'localhost') # CORS CORS(app, resources={r"/*": {"origins": "*"}}) # 鍒濆鍖栨暟鎹簱 from insurance.db.database import init_db init_db(app) # 鍒濆鍖?Redis from insurance.db.redis import init_redis init_redis(app) # 娉ㄥ唽璺敱 from insurance.routes import register_insurance_routes register_insurance_routes(app) # 娉ㄥ唽閿欒澶勭悊鍣? from insurance.utils.error_handler import register_error_handlers register_error_handlers(app) # 鍋ュ悍妫€鏌ワ紙妫€鏌ュ疄闄呬緷璧栵級 @app.route('/health') def health(): checks = {'status': 'ok', 'service': 'baodanagent-api'} try: from insurance.db.compat import db db.session.execute(db.text('SELECT 1')) checks['database'] = 'ok' except Exception as e: checks['database'] = f'error: {str(e)[:100]}' checks['status'] = 'degraded' try: from insurance.db.compat import redis_client if redis_client: redis_client.ping() checks['redis'] = 'ok' else: checks['redis'] = 'not configured' except Exception as e: checks['redis'] = f'error: {str(e)[:100]}' checks['status'] = 'degraded' return checks return app # 鍒涘缓搴旂敤瀹炰緥 app = create_app() if __name__ == '__main__': port = int(os.getenv('PORT', 5001)) debug = os.getenv('FLASK_DEBUG', 'false').lower() == 'true' app.run(host='0.0.0.0', port=port, debug=debug)