From 7a786800bdb6903a68121fa051dd69734614863c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=84=A6=E9=BE=99=E8=A8=80?= Date: Mon, 27 Jul 2026 13:33:08 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=EF=BC=9A=E4=BF=9D=E7=95=99?= =?UTF-8?q?=20PostgreSQL=20=E6=97=B6=E9=97=B4=E5=BE=AE=E7=A7=92=E7=B2=BE?= =?UTF-8?q?=E5=BA=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sql/shierfeng-fastapi-pg.sql | 68 +++++++++---------- .../module_business/test_sql_contract.py | 51 ++++++++++++++ 2 files changed, 85 insertions(+), 34 deletions(-) diff --git a/shierfeng-fastapi-backend/sql/shierfeng-fastapi-pg.sql b/shierfeng-fastapi-backend/sql/shierfeng-fastapi-pg.sql index 6f91314..03fc3ec 100644 --- a/shierfeng-fastapi-backend/sql/shierfeng-fastapi-pg.sql +++ b/shierfeng-fastapi-backend/sql/shierfeng-fastapi-pg.sql @@ -1082,9 +1082,9 @@ create table biz_strain ( strain_name varchar(100) not null, status char(1) not null default '0', create_by varchar(64) default '', - create_time timestamp(0), + create_time timestamp without time zone, update_by varchar(64) default '', - update_time timestamp(0), + update_time timestamp without time zone, remark varchar(500) default null, primary key (strain_id), constraint uq_biz_strain_code unique (strain_code), @@ -1098,9 +1098,9 @@ create table biz_warehouse ( address varchar(500), status char(1) not null default '0', create_by varchar(64) default '', - create_time timestamp(0), + create_time timestamp without time zone, update_by varchar(64) default '', - update_time timestamp(0), + update_time timestamp without time zone, remark varchar(500) default null, primary key (warehouse_id), constraint uq_biz_warehouse_code unique (warehouse_code), @@ -1113,9 +1113,9 @@ create table biz_customer ( customer_name varchar(100) not null, status char(1) not null default '0', create_by varchar(64) default '', - create_time timestamp(0), + create_time timestamp without time zone, update_by varchar(64) default '', - update_time timestamp(0), + update_time timestamp without time zone, remark varchar(500) default null, primary key (customer_id), constraint uq_biz_customer_code unique (customer_code), @@ -1130,9 +1130,9 @@ create table biz_customer_site ( address varchar(500), status char(1) not null default '0', create_by varchar(64) default '', - create_time timestamp(0), + create_time timestamp without time zone, update_by varchar(64) default '', - update_time timestamp(0), + update_time timestamp without time zone, remark varchar(500) default null, primary key (site_id), constraint uq_biz_customer_site_customer_code unique (customer_id, site_code), @@ -1147,9 +1147,9 @@ create table biz_production_line ( line_name varchar(100) not null, status char(1) not null default '0', create_by varchar(64) default '', - create_time timestamp(0), + create_time timestamp without time zone, update_by varchar(64) default '', - update_time timestamp(0), + update_time timestamp without time zone, remark varchar(500) default null, primary key (line_id), constraint uq_biz_production_line_site_code unique (site_id, line_code), @@ -1164,9 +1164,9 @@ create table biz_pallet ( capacity_packages integer not null, asset_status char(1) not null default '0', create_by varchar(64) default '', - create_time timestamp(0), + create_time timestamp without time zone, update_by varchar(64) default '', - update_time timestamp(0), + update_time timestamp without time zone, remark varchar(500) default null, primary key (pallet_id), constraint uq_biz_pallet_code unique (pallet_code), @@ -1179,7 +1179,7 @@ create table biz_user_warehouse ( user_id bigint not null, warehouse_id bigint not null, create_by varchar(64) default '', - create_time timestamp(0), + create_time timestamp without time zone, primary key (user_id, warehouse_id), constraint fk_biz_user_warehouse_user foreign key (user_id) references sys_user(user_id), constraint fk_biz_user_warehouse_warehouse foreign key (warehouse_id) references biz_warehouse(warehouse_id) @@ -1189,7 +1189,7 @@ create table biz_user_site ( user_id bigint not null, site_id bigint not null, create_by varchar(64) default '', - create_time timestamp(0), + create_time timestamp without time zone, primary key (user_id, site_id), constraint fk_biz_user_site_user foreign key (user_id) references sys_user(user_id), constraint fk_biz_user_site_site foreign key (site_id) references biz_customer_site(site_id) @@ -1203,11 +1203,11 @@ create table biz_inbound_order ( confirm_idempotency_key varchar(64), confirm_request_hash varchar(64), confirmed_by bigint, - confirmed_at timestamp(0), + confirmed_at timestamp without time zone, create_by varchar(64) default '', - create_time timestamp(0), + create_time timestamp without time zone, update_by varchar(64) default '', - update_time timestamp(0), + update_time timestamp without time zone, remark varchar(500) default null, primary key (inbound_id), constraint uq_biz_inbound_order_no unique (inbound_no), @@ -1222,7 +1222,7 @@ create table biz_inbound_item ( inbound_id bigint not null, pallet_id bigint not null, create_by varchar(64) default '', - create_time timestamp(0), + create_time timestamp without time zone, primary key (inbound_item_id), constraint uq_biz_inbound_item_order_pallet unique (inbound_id, pallet_id), constraint fk_biz_inbound_item_order foreign key (inbound_id) references biz_inbound_order(inbound_id), @@ -1241,15 +1241,15 @@ create table biz_filling_record ( package_spec varchar(64) not null, initial_packages integer not null, active_slot varchar(16), - closed_at timestamp(0), + closed_at timestamp without time zone, idempotency_key varchar(64) not null, request_hash varchar(64) not null, operator_id bigint not null, - operated_at timestamp(0) not null, + operated_at timestamp without time zone not null, create_by varchar(64) default '', - create_time timestamp(0), + create_time timestamp without time zone, update_by varchar(64) default '', - update_time timestamp(0), + update_time timestamp without time zone, remark varchar(500) default null, primary key (filling_id), constraint uq_biz_filling_record_no unique (filling_no), @@ -1272,11 +1272,11 @@ create table biz_shipment_order ( confirm_idempotency_key varchar(64), confirm_request_hash varchar(64), confirmed_by bigint, - confirmed_at timestamp(0), + confirmed_at timestamp without time zone, create_by varchar(64) default '', - create_time timestamp(0), + create_time timestamp without time zone, update_by varchar(64) default '', - update_time timestamp(0), + update_time timestamp without time zone, remark varchar(500) default null, primary key (shipment_id), constraint uq_biz_shipment_order_no unique (shipment_no), @@ -1294,7 +1294,7 @@ create table biz_shipment_item ( active_filling_id bigint, package_qty integer, create_by varchar(64) default '', - create_time timestamp(0), + create_time timestamp without time zone, primary key (shipment_item_id), constraint uq_biz_shipment_item_order_pallet unique (shipment_id, pallet_id), constraint ck_biz_shipment_item_package_qty check (package_qty is null or package_qty > 0), @@ -1315,7 +1315,7 @@ create table biz_usage_record ( idempotency_key varchar(64) not null, request_hash varchar(64) not null, operator_id bigint not null, - occurred_at timestamp(0) not null, + occurred_at timestamp without time zone not null, primary key (usage_id), constraint uq_biz_usage_record_no unique (usage_no), constraint uq_biz_usage_record_idempotency unique (idempotency_key), @@ -1339,13 +1339,13 @@ create table biz_return_order ( confirm_idempotency_key varchar(64), confirm_request_hash varchar(64), requested_by bigint not null, - requested_at timestamp(0) not null, + requested_at timestamp without time zone not null, confirmed_by bigint, - confirmed_at timestamp(0), + confirmed_at timestamp without time zone, create_by varchar(64) default '', - create_time timestamp(0), + create_time timestamp without time zone, update_by varchar(64) default '', - update_time timestamp(0), + update_time timestamp without time zone, remark varchar(500) default null, primary key (return_id), constraint uq_biz_return_order_no unique (return_no), @@ -1365,7 +1365,7 @@ create table biz_return_item ( active_filling_id_snapshot bigint, remaining_packages_snapshot integer not null, create_by varchar(64) default '', - create_time timestamp(0), + create_time timestamp without time zone, primary key (return_item_id), constraint uq_biz_return_item_order_pallet unique (return_id, pallet_id), constraint ck_biz_return_item_remaining_packages_snapshot check (remaining_packages_snapshot >= 0), @@ -1392,7 +1392,7 @@ create table biz_pallet_event ( before_remaining_packages integer not null, after_remaining_packages integer not null, operator_id bigint not null, - occurred_at timestamp(0) not null, + occurred_at timestamp without time zone not null, idempotency_key varchar(64) not null, request_hash varchar(64) not null, primary key (event_id), @@ -1424,7 +1424,7 @@ create table biz_pallet_inventory ( remaining_packages integer not null default 0, last_event_id bigint, version integer not null default 0, - updated_at timestamp(0) not null, + updated_at timestamp without time zone not null, primary key (pallet_id), constraint ck_biz_pallet_inventory_flow_state check ( flow_state in ( diff --git a/shierfeng-fastapi-backend/tests/module_business/test_sql_contract.py b/shierfeng-fastapi-backend/tests/module_business/test_sql_contract.py index 152dd1b..ef5df28 100644 --- a/shierfeng-fastapi-backend/tests/module_business/test_sql_contract.py +++ b/shierfeng-fastapi-backend/tests/module_business/test_sql_contract.py @@ -165,6 +165,27 @@ EXPECTED_COLUMNS = { }, } +EXPECTED_DATETIME_COLUMNS = { + 'biz_strain': {'create_time', 'update_time'}, + 'biz_warehouse': {'create_time', 'update_time'}, + 'biz_customer': {'create_time', 'update_time'}, + 'biz_customer_site': {'create_time', 'update_time'}, + 'biz_production_line': {'create_time', 'update_time'}, + 'biz_pallet': {'create_time', 'update_time'}, + 'biz_user_warehouse': {'create_time'}, + 'biz_user_site': {'create_time'}, + 'biz_inbound_order': {'confirmed_at', 'create_time', 'update_time'}, + 'biz_inbound_item': {'create_time'}, + 'biz_filling_record': {'closed_at', 'operated_at', 'create_time', 'update_time'}, + 'biz_shipment_order': {'confirmed_at', 'create_time', 'update_time'}, + 'biz_shipment_item': {'create_time'}, + 'biz_usage_record': {'occurred_at'}, + 'biz_return_order': {'requested_at', 'confirmed_at', 'create_time', 'update_time'}, + 'biz_return_item': {'create_time'}, + 'biz_pallet_inventory': {'updated_at'}, + 'biz_pallet_event': {'occurred_at'}, +} + EXPECTED_PRIMARY_KEYS = { 'biz_strain': ('strain_id',), 'biz_warehouse': ('warehouse_id',), @@ -577,6 +598,17 @@ def _table_contract( return columns, primary_key, unique_keys, foreign_keys, checks +def _column_definitions(body: str) -> dict[str, str]: + definitions = {} + reserved = {'constraint', 'primary', 'unique', 'foreign', 'check', 'key', 'index'} + for clause in _split_values(body): + clean = clause.strip() + identifier_match = re.match(r'[`"]?([a-z_][a-z0-9_]*)[`"]?', clean, flags=re.I) + if identifier_match and identifier_match.group(1).lower() not in reserved: + definitions[identifier_match.group(1).lower()] = clean + return definitions + + def _business_menu_rows(sql: str) -> dict[int, list]: rows = _insert_rows(sql, 'sys_menu') selected = [row for row in rows if row and str(row[0]).isdigit() and int(row[0]) in BUSINESS_MENU_IDS] @@ -607,6 +639,25 @@ class SqlContractTest(unittest.TestCase): self.assertEqual(EXPECTED_FOREIGN_KEYS[table], foreign_keys) self.assertEqual(EXPECTED_CHECKS[table], checks) + def test_postgres_business_datetimes_preserve_default_microsecond_precision(self) -> None: + self.assertEqual(34, sum(len(columns) for columns in EXPECTED_DATETIME_COLUMNS.values())) + tables = _create_table_bodies((SQL_ROOT / 'shierfeng-fastapi-pg.sql').read_text(encoding='utf-8')) + zero_precision_columns = [] + for table, expected_columns in EXPECTED_DATETIME_COLUMNS.items(): + definitions = _column_definitions(tables[table]) + actual_columns = { + column + for column, definition in definitions.items() + if re.search(r'\btimestamp\b', definition, flags=re.I) + } + self.assertEqual(expected_columns, actual_columns, table) + for column in expected_columns: + definition = definitions[column] + self.assertRegex(definition, r'\btimestamp(?:\s+without\s+time\s+zone)?\b') + if re.search(r'\btimestamp\s*\(\s*0\s*\)', definition, flags=re.I): + zero_precision_columns.append(f'{table}.{column}') + self.assertEqual([], sorted(zero_precision_columns)) + def test_initializers_define_fixed_business_menus_and_permissions(self) -> None: expected_permissions = {page[-1] for page in EXPECTED_PAGES.values()} | { button[-1] for button in EXPECTED_BUTTONS.values()