""" 资产编码生成工具 使用PostgreSQL Advisory Lock保证并发安全 """ from datetime import datetime from sqlalchemy import text from sqlalchemy.ext.asyncio import AsyncSession from app.core.config import settings async def generate_asset_code(db: AsyncSession) -> str: """ 生成资产编码 格式: AS + YYYYMMDD + 流水号(4位) 示例: AS202501240001 使用PostgreSQL Advisory Lock保证并发安全 Args: db: 数据库会话 Returns: 资产编码 """ # 获取当前日期字符串 date_str = datetime.now().strftime("%Y%m%d") prefix = f"AS{date_str}" # 使用Advisory Lock保证并发安全 # 使用日期作为锁ID,避免不同日期的锁冲突 lock_id = int(date_str) try: # 获取锁 await db.execute(text(f"SELECT pg_advisory_lock({lock_id})")) # 查询今天最大的序号 result = await db.execute( text(""" SELECT CAST(SUBSTRING(asset_code FROM 13 FOR 4) AS INTEGER) as max_seq FROM assets WHERE asset_code LIKE :prefix AND deleted_at IS NULL ORDER BY asset_code DESC LIMIT 1 """), {"prefix": f"{prefix}%"} ) row = result.fetchone() max_seq = row[0] if row and row[0] else 0 # 生成新序号 new_seq = max_seq + 1 seq_str = f"{new_seq:04d}" # 补零到4位 # 组合编码 asset_code = f"{prefix}{seq_str}" return asset_code finally: # 释放锁 await db.execute(text(f"SELECT pg_advisory_unlock({lock_id})")) def validate_asset_code(asset_code: str) -> bool: """ 验证资产编码格式 Args: asset_code: 资产编码 Returns: 是否有效 """ if not asset_code or len(asset_code) != 14: return False # 检查前缀 if not asset_code.startswith("AS"): return False # 检查日期部分 date_str = asset_code[2:10] try: datetime.strptime(date_str, "%Y%m%d") except ValueError: return False # 检查序号部分 seq_str = asset_code[10:] if not seq_str.isdigit(): return False return True