#!/usr/bin/env python3 """Renumber the current history import to its source application IDs.""" import argparse import gzip import json import re import sys from datetime import datetime from pathlib import Path ROOT = Path(__file__).resolve().parents[1] sys.path.insert(0, str(ROOT)) def run(local=False): if local: import test_wsgi # noqa: F401 from services.db import get_db conn = get_db() foreign_keys_disabled = False try: with conn.cursor() as cur: cur.execute("""SELECT a.id,a.status,a.product_name,a.image_path FROM spot_applications a ORDER BY a.id FOR UPDATE""") applications = cur.fetchall() imported = [] for row in applications: match = re.fullmatch(r'/static/uploads/spot_application/history_(\d+)_[0-9a-f]{16}\.[A-Za-z0-9]+', row.get('image_path') or '') if match: imported.append((row['id'], int(match.group(1)))) if len(imported) != 547 or {target for _, target in imported} != set(range(1, 548)): raise RuntimeError('导入记录不是完整且唯一的1-547,停止重排') imported_ids = {old for old, _ in imported} retained = [row for row in applications if row['id'] not in imported_ids] max_id = max(row['id'] for row in applications) retained_moves = [] next_id = max_id + 1000 targets = {target for _, target in imported} for row in retained: if row['id'] in targets: retained_moves.append((row['id'], next_id)) next_id += 1 backup = ROOT / 'var/manual-updates' / ('spot-confirmation-renumber-' + datetime.now().strftime('%Y%m%dT%H%M%S') + '.json.gz') backup.parent.mkdir(parents=True, exist_ok=True) with gzip.open(backup, 'wt', encoding='utf-8') as handle: json.dump({'imported': imported, 'retained_moves': retained_moves}, handle, ensure_ascii=False) offset = next_id + 1000 cur.execute('SET FOREIGN_KEY_CHECKS=0') foreign_keys_disabled = True for old, new in retained_moves: cur.execute('UPDATE spot_confirmations SET application_id=%s WHERE application_id=%s', (new, old)) cur.execute('UPDATE spot_applications SET id=%s WHERE id=%s', (new, old)) for old, target in imported: temporary = offset + target cur.execute('UPDATE spot_confirmations SET application_id=%s WHERE application_id=%s', (temporary, old)) cur.execute('UPDATE spot_applications SET id=%s WHERE id=%s', (temporary, old)) for _, target in imported: temporary = offset + target cur.execute('UPDATE spot_applications SET id=%s WHERE id=%s', (target, temporary)) cur.execute('UPDATE spot_confirmations SET application_id=%s WHERE application_id=%s', (target, temporary)) cur.execute('SET FOREIGN_KEY_CHECKS=1') foreign_keys_disabled = False conn.commit() print(json.dumps({'renumbered': len(imported), 'retained_moved': retained_moves, 'backup': str(backup.relative_to(ROOT))}, ensure_ascii=False)) except Exception: conn.rollback() raise finally: if foreign_keys_disabled: try: with conn.cursor() as cur: cur.execute('SET FOREIGN_KEY_CHECKS=1') except Exception: pass conn.close() if __name__ == '__main__': parser = argparse.ArgumentParser() parser.add_argument('--local', action='store_true') run(parser.parse_args().local)