pl_child_sync.py 2.5 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768
  1. """Scoped child reconciliation. Callers supply the complete writable scope, never global IDs."""
  2. from datetime import datetime
  3. from fastapi import HTTPException
  4. from app.core.utils import generate_uuid
  5. def validate_child_ids(items, existing, label):
  6. """Validate the whole collection before a caller mutates any ORM instance."""
  7. by_id = {row.id: row for row in existing}
  8. seen = set()
  9. for item in items or []:
  10. row_id = (getattr(item, 'id', None) or '').strip()
  11. if not row_id:
  12. continue
  13. if row_id in seen:
  14. raise HTTPException(422, f'{label} id 重复:{row_id}')
  15. if row_id not in by_id:
  16. raise HTTPException(422, f'{label} id 无效、已删除或不属于当前主单:{row_id}')
  17. seen.add(row_id)
  18. return by_id, seen
  19. def prepare_keyed_rows(existing, values, key_fields, label):
  20. """Natural-key relations keep their identities; ambiguous keys must be corrected explicitly."""
  21. old = {}
  22. for row in existing:
  23. key = tuple(getattr(row, field, None) or '' for field in key_fields)
  24. if key in old:
  25. raise HTTPException(409, f'{label}存在重复历史记录,请先核对')
  26. old[key] = row
  27. seen = set()
  28. pairs = []
  29. for value in values:
  30. key = tuple(value.get(field) or '' for field in key_fields)
  31. if key in seen:
  32. raise HTTPException(422, f'{label}不能重复提交')
  33. seen.add(key)
  34. row = old.get(key)
  35. if value.get('id') and (row is None or value['id'] != row.id):
  36. raise HTTPException(422, f'{label} id 无效或不属于当前主单')
  37. pairs.append((row, value))
  38. return pairs, [row for key, row in old.items() if key not in seen]
  39. def apply_keyed_rows(db, model, prepared, *, user_id, dept_id='', order_start=0):
  40. pairs, removed = prepared
  41. now = datetime.now()
  42. result = []
  43. for idx, (row, value) in enumerate(pairs, start=order_start):
  44. fields = {key: val for key, val in value.items() if key != 'id'}
  45. if row is None:
  46. row = model(id=generate_uuid(), create_by=user_id, create_dept_id=dept_id or '',
  47. create_time=now, del_flag=0, **fields)
  48. db.add(row)
  49. else:
  50. for key, val in fields.items():
  51. setattr(row, key, val)
  52. row.order_num = idx
  53. row.update_by = user_id
  54. row.update_time = now
  55. result.append(row)
  56. for row in removed:
  57. row.del_flag = 1
  58. row.update_by = user_id
  59. row.update_time = now
  60. return result