from typing import List, Dict, Any, Tuple from loguru import logger from sqlalchemy import text from .database_manager import DatabaseManager class BatchOperations: """批量数据库操作工具""" def __init__(self): self.db_manager = DatabaseManager() def batch_insert_update_positions(self, positions_data: List[Dict]) -> Tuple[int, int]: """批量插入/更新持仓数据""" session = self.db_manager.get_session() try: if not positions_data: return 0, 0 # 按账号分组 positions_by_account = {} for position in positions_data: k_id = position.get('k_id') if k_id not in positions_by_account: positions_by_account[k_id] = [] positions_by_account[k_id].append(position) total_processed = 0 total_deleted = 0 with session.begin(): for k_id, positions in positions_by_account.items(): processed, deleted = self._process_account_positions(session, k_id, positions) total_processed += processed total_deleted += deleted logger.info(f"批量处理持仓完成: 处理 {total_processed} 条,删除 {total_deleted} 条") return total_processed, total_deleted except Exception as e: logger.error(f"批量处理持仓失败: {e}") return 0, 0 finally: session.close() def _process_account_positions(self, session, k_id: int, positions: List[Dict]) -> Tuple[int, int]: """处理单个账号的持仓数据""" try: st_id = positions[0].get('st_id', 0) if positions else 0 # 准备数据 insert_data = [] keep_keys = set() for pos_data in positions: # 转换数据 pos_dict = self._convert_position_data(pos_data) if not all([pos_dict.get('symbol'), pos_dict.get('side')]): continue # 重命名qty为sum if 'qty' in pos_dict: pos_dict['sum'] = pos_dict.pop('qty') insert_data.append(pos_dict) keep_keys.add((pos_dict['symbol'], pos_dict['side'])) if not insert_data: # 清空该账号持仓 result = session.execute( text("DELETE FROM deh_strategy_position_new WHERE k_id = :k_id AND st_id = :st_id"), {'k_id': k_id, 'st_id': st_id} ) return 0, result.rowcount # 批量插入/更新 sql = """ INSERT INTO deh_strategy_position_new (st_id, k_id, asset, symbol, side, price, `sum`, asset_num, asset_profit, leverage, uptime, profit_price, stop_price, liquidation_price) VALUES (:st_id, :k_id, :asset, :symbol, :side, :price, :sum, :asset_num, :asset_profit, :leverage, :uptime, :profit_price, :stop_price, :liquidation_price) ON DUPLICATE KEY UPDATE price = VALUES(price), `sum` = VALUES(`sum`), asset_num = VALUES(asset_num), asset_profit = VALUES(asset_profit), leverage = VALUES(leverage), uptime = VALUES(uptime), profit_price = VALUES(profit_price), stop_price = VALUES(stop_price), liquidation_price = VALUES(liquidation_price) """ # 分块执行 chunk_size = 500 processed_count = 0 for i in range(0, len(insert_data), chunk_size): chunk = insert_data[i:i + chunk_size] session.execute(text(sql), chunk) processed_count += len(chunk) # 删除多余持仓 deleted_count = 0 if keep_keys: # 构建删除条件 conditions = [] for symbol, side in keep_keys: safe_symbol = symbol.replace("'", "''") if symbol else '' safe_side = side.replace("'", "''") if side else '' conditions.append(f"(symbol = '{safe_symbol}' AND side = '{safe_side}')") if conditions: conditions_str = " OR ".join(conditions) delete_sql = f""" DELETE FROM deh_strategy_position_new WHERE k_id = {k_id} AND st_id = {st_id} AND NOT ({conditions_str}) """ result = session.execute(text(delete_sql)) deleted_count = result.rowcount return processed_count, deleted_count except Exception as e: logger.error(f"处理账号 {k_id} 持仓失败: {e}") return 0, 0 def _convert_position_data(self, data: Dict) -> Dict: """转换持仓数据格式""" # 转换逻辑... pass # 类似的批量方法 for orders and account info...