diff --git a/shierfeng-fastapi-backend/module_business/entity/do/operation_do.py b/shierfeng-fastapi-backend/module_business/entity/do/operation_do.py index 90147e5..af3edac 100644 --- a/shierfeng-fastapi-backend/module_business/entity/do/operation_do.py +++ b/shierfeng-fastapi-backend/module_business/entity/do/operation_do.py @@ -387,7 +387,7 @@ class BizPalletEvent(Base): event_id = Column(BigInteger, primary_key=True, nullable=False, autoincrement=True, comment='事件ID') pallet_id = Column(BigInteger, ForeignKey('biz_pallet.pallet_id'), nullable=False, comment='托盘ID') - event_seq = Column(BigInteger, nullable=False, comment='托盘内事件序号') + event_seq = Column(Integer, nullable=False, comment='托盘内事件序号') event_type = Column(String(32), nullable=False, comment='事件类型') source_type = Column(String(32), nullable=False, comment='来源类型') source_id = Column(BigInteger, nullable=False, comment='来源记录ID') diff --git a/shierfeng-fastapi-backend/tests/module_business/test_model_contract.py b/shierfeng-fastapi-backend/tests/module_business/test_model_contract.py index db8ca37..0e45488 100644 --- a/shierfeng-fastapi-backend/tests/module_business/test_model_contract.py +++ b/shierfeng-fastapi-backend/tests/module_business/test_model_contract.py @@ -1,6 +1,6 @@ import unittest -from sqlalchemy import CHAR, BigInteger, CheckConstraint, Integer, String, Table, UniqueConstraint +from sqlalchemy import CHAR, BigInteger, CheckConstraint, DateTime, Integer, String, Table, UniqueConstraint from config.database import Base from module_business.entity.do import master_do, operation_do @@ -228,6 +228,472 @@ EXPECTED_COLUMNS = { }, } +EXPECTED_COLUMN_TYPES = { + 'biz_strain': { + 'strain_id': (BigInteger, None), + 'strain_code': (String, 64), + 'strain_name': (String, 100), + 'status': (CHAR, 1), + 'create_by': (String, 64), + 'create_time': (DateTime, None), + 'update_by': (String, 64), + 'update_time': (DateTime, None), + 'remark': (String, 500), + }, + 'biz_warehouse': { + 'warehouse_id': (BigInteger, None), + 'warehouse_code': (String, 64), + 'warehouse_name': (String, 100), + 'address': (String, 500), + 'status': (CHAR, 1), + 'create_by': (String, 64), + 'create_time': (DateTime, None), + 'update_by': (String, 64), + 'update_time': (DateTime, None), + 'remark': (String, 500), + }, + 'biz_customer': { + 'customer_id': (BigInteger, None), + 'customer_code': (String, 64), + 'customer_name': (String, 100), + 'status': (CHAR, 1), + 'create_by': (String, 64), + 'create_time': (DateTime, None), + 'update_by': (String, 64), + 'update_time': (DateTime, None), + 'remark': (String, 500), + }, + 'biz_customer_site': { + 'site_id': (BigInteger, None), + 'customer_id': (BigInteger, None), + 'site_code': (String, 64), + 'site_name': (String, 100), + 'address': (String, 500), + 'status': (CHAR, 1), + 'create_by': (String, 64), + 'create_time': (DateTime, None), + 'update_by': (String, 64), + 'update_time': (DateTime, None), + 'remark': (String, 500), + }, + 'biz_production_line': { + 'line_id': (BigInteger, None), + 'site_id': (BigInteger, None), + 'line_code': (String, 64), + 'line_name': (String, 100), + 'status': (CHAR, 1), + 'create_by': (String, 64), + 'create_time': (DateTime, None), + 'update_by': (String, 64), + 'update_time': (DateTime, None), + 'remark': (String, 500), + }, + 'biz_pallet': { + 'pallet_id': (BigInteger, None), + 'pallet_code': (String, 64), + 'qr_token': (String, 128), + 'capacity_packages': (Integer, None), + 'asset_status': (CHAR, 1), + 'create_by': (String, 64), + 'create_time': (DateTime, None), + 'update_by': (String, 64), + 'update_time': (DateTime, None), + 'remark': (String, 500), + }, + 'biz_user_warehouse': { + 'user_id': (BigInteger, None), + 'warehouse_id': (BigInteger, None), + 'create_by': (String, 64), + 'create_time': (DateTime, None), + }, + 'biz_user_site': { + 'user_id': (BigInteger, None), + 'site_id': (BigInteger, None), + 'create_by': (String, 64), + 'create_time': (DateTime, None), + }, + 'biz_inbound_order': { + 'inbound_id': (BigInteger, None), + 'inbound_no': (String, 64), + 'warehouse_id': (BigInteger, None), + 'status': (String, 32), + 'confirm_idempotency_key': (String, 64), + 'confirm_request_hash': (String, 64), + 'confirmed_by': (BigInteger, None), + 'confirmed_at': (DateTime, None), + 'create_by': (String, 64), + 'create_time': (DateTime, None), + 'update_by': (String, 64), + 'update_time': (DateTime, None), + 'remark': (String, 500), + }, + 'biz_inbound_item': { + 'inbound_item_id': (BigInteger, None), + 'inbound_id': (BigInteger, None), + 'pallet_id': (BigInteger, None), + 'create_by': (String, 64), + 'create_time': (DateTime, None), + }, + 'biz_filling_record': { + 'filling_id': (BigInteger, None), + 'filling_no': (String, 64), + 'pallet_id': (BigInteger, None), + 'warehouse_id': (BigInteger, None), + 'strain_id': (BigInteger, None), + 'strain_code_snapshot': (String, 64), + 'strain_name_snapshot': (String, 100), + 'batch_no': (String, 64), + 'package_spec': (String, 64), + 'initial_packages': (Integer, None), + 'active_slot': (String, 16), + 'closed_at': (DateTime, None), + 'idempotency_key': (String, 64), + 'request_hash': (String, 64), + 'operator_id': (BigInteger, None), + 'operated_at': (DateTime, None), + 'create_by': (String, 64), + 'create_time': (DateTime, None), + 'update_by': (String, 64), + 'update_time': (DateTime, None), + 'remark': (String, 500), + }, + 'biz_shipment_order': { + 'shipment_id': (BigInteger, None), + 'shipment_no': (String, 64), + 'source_warehouse_id': (BigInteger, None), + 'target_site_id': (BigInteger, None), + 'status': (String, 32), + 'confirm_idempotency_key': (String, 64), + 'confirm_request_hash': (String, 64), + 'confirmed_by': (BigInteger, None), + 'confirmed_at': (DateTime, None), + 'create_by': (String, 64), + 'create_time': (DateTime, None), + 'update_by': (String, 64), + 'update_time': (DateTime, None), + 'remark': (String, 500), + }, + 'biz_shipment_item': { + 'shipment_item_id': (BigInteger, None), + 'shipment_id': (BigInteger, None), + 'pallet_id': (BigInteger, None), + 'active_filling_id': (BigInteger, None), + 'package_qty': (Integer, None), + 'create_by': (String, 64), + 'create_time': (DateTime, None), + }, + 'biz_usage_record': { + 'usage_id': (BigInteger, None), + 'usage_no': (String, 64), + 'pallet_id': (BigInteger, None), + 'active_filling_id': (BigInteger, None), + 'site_id': (BigInteger, None), + 'line_id': (BigInteger, None), + 'used_packages': (Integer, None), + 'remaining_packages_after': (Integer, None), + 'idempotency_key': (String, 64), + 'request_hash': (String, 64), + 'operator_id': (BigInteger, None), + 'occurred_at': (DateTime, None), + }, + 'biz_return_order': { + 'return_id': (BigInteger, None), + 'return_no': (String, 64), + 'source_site_id': (BigInteger, None), + 'target_warehouse_id': (BigInteger, None), + 'status': (String, 32), + 'request_idempotency_key': (String, 64), + 'request_hash': (String, 64), + 'confirm_idempotency_key': (String, 64), + 'confirm_request_hash': (String, 64), + 'requested_by': (BigInteger, None), + 'requested_at': (DateTime, None), + 'confirmed_by': (BigInteger, None), + 'confirmed_at': (DateTime, None), + 'create_by': (String, 64), + 'create_time': (DateTime, None), + 'update_by': (String, 64), + 'update_time': (DateTime, None), + 'remark': (String, 500), + }, + 'biz_return_item': { + 'return_item_id': (BigInteger, None), + 'return_id': (BigInteger, None), + 'pallet_id': (BigInteger, None), + 'active_filling_id_snapshot': (BigInteger, None), + 'remaining_packages_snapshot': (Integer, None), + 'create_by': (String, 64), + 'create_time': (DateTime, None), + }, + 'biz_pallet_inventory': { + 'pallet_id': (BigInteger, None), + 'active_filling_id': (BigInteger, None), + 'warehouse_id': (BigInteger, None), + 'site_id': (BigInteger, None), + 'current_line_id': (BigInteger, None), + 'flow_state': (String, 32), + 'remaining_packages': (Integer, None), + 'last_event_id': (BigInteger, None), + 'version': (Integer, None), + 'updated_at': (DateTime, None), + }, + 'biz_pallet_event': { + 'event_id': (BigInteger, None), + 'pallet_id': (BigInteger, None), + 'event_seq': (Integer, None), + 'event_type': (String, 32), + 'source_type': (String, 32), + 'source_id': (BigInteger, None), + 'before_state': (String, 32), + 'after_state': (String, 32), + 'before_warehouse_id': (BigInteger, None), + 'before_site_id': (BigInteger, None), + 'before_line_id': (BigInteger, None), + 'after_warehouse_id': (BigInteger, None), + 'after_site_id': (BigInteger, None), + 'after_line_id': (BigInteger, None), + 'before_remaining_packages': (Integer, None), + 'after_remaining_packages': (Integer, None), + 'operator_id': (BigInteger, None), + 'occurred_at': (DateTime, None), + 'idempotency_key': (String, 64), + 'request_hash': (String, 64), + }, +} + +EXPECTED_NULLABILITY = { + 'biz_strain': { + 'strain_id': False, + 'strain_code': False, + 'strain_name': False, + 'status': False, + 'create_by': True, + 'create_time': True, + 'update_by': True, + 'update_time': True, + 'remark': True, + }, + 'biz_warehouse': { + 'warehouse_id': False, + 'warehouse_code': False, + 'warehouse_name': False, + 'address': True, + 'status': False, + 'create_by': True, + 'create_time': True, + 'update_by': True, + 'update_time': True, + 'remark': True, + }, + 'biz_customer': { + 'customer_id': False, + 'customer_code': False, + 'customer_name': False, + 'status': False, + 'create_by': True, + 'create_time': True, + 'update_by': True, + 'update_time': True, + 'remark': True, + }, + 'biz_customer_site': { + 'site_id': False, + 'customer_id': False, + 'site_code': False, + 'site_name': False, + 'address': True, + 'status': False, + 'create_by': True, + 'create_time': True, + 'update_by': True, + 'update_time': True, + 'remark': True, + }, + 'biz_production_line': { + 'line_id': False, + 'site_id': False, + 'line_code': False, + 'line_name': False, + 'status': False, + 'create_by': True, + 'create_time': True, + 'update_by': True, + 'update_time': True, + 'remark': True, + }, + 'biz_pallet': { + 'pallet_id': False, + 'pallet_code': False, + 'qr_token': False, + 'capacity_packages': False, + 'asset_status': False, + 'create_by': True, + 'create_time': True, + 'update_by': True, + 'update_time': True, + 'remark': True, + }, + 'biz_user_warehouse': { + 'user_id': False, + 'warehouse_id': False, + 'create_by': True, + 'create_time': True, + }, + 'biz_user_site': { + 'user_id': False, + 'site_id': False, + 'create_by': True, + 'create_time': True, + }, + 'biz_inbound_order': { + 'inbound_id': False, + 'inbound_no': False, + 'warehouse_id': False, + 'status': False, + 'confirm_idempotency_key': True, + 'confirm_request_hash': True, + 'confirmed_by': True, + 'confirmed_at': True, + 'create_by': True, + 'create_time': True, + 'update_by': True, + 'update_time': True, + 'remark': True, + }, + 'biz_inbound_item': { + 'inbound_item_id': False, + 'inbound_id': False, + 'pallet_id': False, + 'create_by': True, + 'create_time': True, + }, + 'biz_filling_record': { + 'filling_id': False, + 'filling_no': False, + 'pallet_id': False, + 'warehouse_id': False, + 'strain_id': False, + 'strain_code_snapshot': False, + 'strain_name_snapshot': False, + 'batch_no': False, + 'package_spec': False, + 'initial_packages': False, + 'active_slot': True, + 'closed_at': True, + 'idempotency_key': False, + 'request_hash': False, + 'operator_id': False, + 'operated_at': False, + 'create_by': True, + 'create_time': True, + 'update_by': True, + 'update_time': True, + 'remark': True, + }, + 'biz_shipment_order': { + 'shipment_id': False, + 'shipment_no': False, + 'source_warehouse_id': False, + 'target_site_id': False, + 'status': False, + 'confirm_idempotency_key': True, + 'confirm_request_hash': True, + 'confirmed_by': True, + 'confirmed_at': True, + 'create_by': True, + 'create_time': True, + 'update_by': True, + 'update_time': True, + 'remark': True, + }, + 'biz_shipment_item': { + 'shipment_item_id': False, + 'shipment_id': False, + 'pallet_id': False, + 'active_filling_id': True, + 'package_qty': True, + 'create_by': True, + 'create_time': True, + }, + 'biz_usage_record': { + 'usage_id': False, + 'usage_no': False, + 'pallet_id': False, + 'active_filling_id': False, + 'site_id': False, + 'line_id': False, + 'used_packages': False, + 'remaining_packages_after': False, + 'idempotency_key': False, + 'request_hash': False, + 'operator_id': False, + 'occurred_at': False, + }, + 'biz_return_order': { + 'return_id': False, + 'return_no': False, + 'source_site_id': False, + 'target_warehouse_id': False, + 'status': False, + 'request_idempotency_key': False, + 'request_hash': False, + 'confirm_idempotency_key': True, + 'confirm_request_hash': True, + 'requested_by': False, + 'requested_at': False, + 'confirmed_by': True, + 'confirmed_at': True, + 'create_by': True, + 'create_time': True, + 'update_by': True, + 'update_time': True, + 'remark': True, + }, + 'biz_return_item': { + 'return_item_id': False, + 'return_id': False, + 'pallet_id': False, + 'active_filling_id_snapshot': True, + 'remaining_packages_snapshot': False, + 'create_by': True, + 'create_time': True, + }, + 'biz_pallet_inventory': { + 'pallet_id': False, + 'active_filling_id': True, + 'warehouse_id': True, + 'site_id': True, + 'current_line_id': True, + 'flow_state': False, + 'remaining_packages': False, + 'last_event_id': True, + 'version': False, + 'updated_at': False, + }, + 'biz_pallet_event': { + 'event_id': False, + 'pallet_id': False, + 'event_seq': False, + 'event_type': False, + 'source_type': False, + 'source_id': False, + 'before_state': False, + 'after_state': False, + 'before_warehouse_id': True, + 'before_site_id': True, + 'before_line_id': True, + 'after_warehouse_id': True, + 'after_site_id': True, + 'after_line_id': True, + 'before_remaining_packages': False, + 'after_remaining_packages': False, + 'operator_id': False, + 'occurred_at': False, + 'idempotency_key': False, + 'request_hash': False, + }, +} + EXPECTED_PRIMARY_KEYS = { 'biz_strain': ('strain_id',), 'biz_warehouse': ('warehouse_id',), @@ -436,6 +902,34 @@ class BusinessModelContractTest(unittest.TestCase): self.assertEqual(table_name, model.__tablename__) self.assertEqual(EXPECTED_COLUMNS[table_name], set(model.__table__.columns.keys())) + def test_every_column_type_and_string_length_matches_contract(self) -> None: + business_tables = {name for name in Base.metadata.tables if name.startswith('biz_')} + self.assertEqual(business_tables, set(EXPECTED_COLUMN_TYPES)) + + for table_name, expected_columns in EXPECTED_COLUMN_TYPES.items(): + table = Base.metadata.tables[table_name] + with self.subTest(table_name=table_name, contract='all_type_columns'): + self.assertEqual(set(expected_columns), set(table.columns.keys())) + + for column_name, (expected_type, expected_length) in expected_columns.items(): + column_type = table.c[column_name].type + with self.subTest(table_name=table_name, column_name=column_name): + self.assertIs(expected_type, type(column_type)) + self.assertEqual(expected_length, getattr(column_type, 'length', None)) + + def test_every_column_nullable_flag_matches_contract(self) -> None: + business_tables = {name for name in Base.metadata.tables if name.startswith('biz_')} + self.assertEqual(business_tables, set(EXPECTED_NULLABILITY)) + + for table_name, expected_columns in EXPECTED_NULLABILITY.items(): + table = Base.metadata.tables[table_name] + with self.subTest(table_name=table_name, contract='all_nullable_columns'): + self.assertEqual(set(expected_columns), set(table.columns.keys())) + + for column_name, expected_nullable in expected_columns.items(): + with self.subTest(table_name=table_name, column_name=column_name): + self.assertIs(expected_nullable, table.c[column_name].nullable) + def test_primary_keys_and_foreign_keys_match_contract(self) -> None: for table_name, expected_primary_key in EXPECTED_PRIMARY_KEYS.items(): table = Base.metadata.tables[table_name]