功能:新增托盘业务数据库脚本

This commit is contained in:
焦龙言
2026-07-27 13:12:19 +08:00
parent 626e15fe89
commit b858340890
4 changed files with 2283 additions and 0 deletions
@@ -0,0 +1,489 @@
-- 警告:MySQL DDL 会隐式提交,不能依赖事务整体回滚。
-- 应用前必须完成数据库备份并由 DBA 人工审查;请在维护窗口内分节执行和验证。
-- 本文件仅作为人工增量补丁交付,未自动执行。
-- ==================== DDL:托盘流转业务表 ====================
-- 菌种主数据表
create table if not exists biz_strain (
strain_id bigint(20) not null auto_increment comment '菌种ID',
strain_code varchar(64) not null comment '菌种编码',
strain_name varchar(100) not null comment '菌种名称',
status char(1) not null default '0' comment '状态(0正常 1停用)',
create_by varchar(64) default '' comment '创建者',
create_time datetime comment '创建时间',
update_by varchar(64) default '' comment '更新者',
update_time datetime comment '更新时间',
remark varchar(500) default null comment '备注',
primary key (strain_id),
constraint uq_biz_strain_code unique (strain_code),
constraint ck_biz_strain_status check (status in ('0', '1'))
) engine=innodb comment = '菌种主数据表';
-- 仓库主数据表
create table if not exists biz_warehouse (
warehouse_id bigint(20) not null auto_increment comment '仓库ID',
warehouse_code varchar(64) not null comment '仓库编码',
warehouse_name varchar(100) not null comment '仓库名称',
address varchar(500) comment '仓库地址',
status char(1) not null default '0' comment '状态(0正常 1停用)',
create_by varchar(64) default '' comment '创建者',
create_time datetime comment '创建时间',
update_by varchar(64) default '' comment '更新者',
update_time datetime comment '更新时间',
remark varchar(500) default null comment '备注',
primary key (warehouse_id),
constraint uq_biz_warehouse_code unique (warehouse_code),
constraint ck_biz_warehouse_status check (status in ('0', '1'))
) engine=innodb comment = '仓库主数据表';
-- 客户主数据表
create table if not exists biz_customer (
customer_id bigint(20) not null auto_increment comment '客户ID',
customer_code varchar(64) not null comment '客户编码',
customer_name varchar(100) not null comment '客户名称',
status char(1) not null default '0' comment '状态(0正常 1停用)',
create_by varchar(64) default '' comment '创建者',
create_time datetime comment '创建时间',
update_by varchar(64) default '' comment '更新者',
update_time datetime comment '更新时间',
remark varchar(500) default null comment '备注',
primary key (customer_id),
constraint uq_biz_customer_code unique (customer_code),
constraint ck_biz_customer_status check (status in ('0', '1'))
) engine=innodb comment = '客户主数据表';
-- 客户现场主数据表
create table if not exists biz_customer_site (
site_id bigint(20) not null auto_increment comment '现场ID',
customer_id bigint(20) not null comment '客户ID',
site_code varchar(64) not null comment '现场编码',
site_name varchar(100) not null comment '现场名称',
address varchar(500) comment '现场地址',
status char(1) not null default '0' comment '状态(0正常 1停用)',
create_by varchar(64) default '' comment '创建者',
create_time datetime comment '创建时间',
update_by varchar(64) default '' comment '更新者',
update_time datetime comment '更新时间',
remark varchar(500) default null comment '备注',
primary key (site_id),
constraint uq_biz_customer_site_customer_code unique (customer_id, site_code),
constraint fk_biz_customer_site_customer foreign key (customer_id) references biz_customer (customer_id),
constraint ck_biz_customer_site_status check (status in ('0', '1'))
) engine=innodb comment = '客户现场主数据表';
-- 客户现场产线主数据表
create table if not exists biz_production_line (
line_id bigint(20) not null auto_increment comment '产线ID',
site_id bigint(20) not null comment '现场ID',
line_code varchar(64) not null comment '产线编码',
line_name varchar(100) not null comment '产线名称',
status char(1) not null default '0' comment '状态(0正常 1停用)',
create_by varchar(64) default '' comment '创建者',
create_time datetime comment '创建时间',
update_by varchar(64) default '' comment '更新者',
update_time datetime comment '更新时间',
remark varchar(500) default null comment '备注',
primary key (line_id),
constraint uq_biz_production_line_site_code unique (site_id, line_code),
constraint fk_biz_production_line_site foreign key (site_id) references biz_customer_site (site_id),
constraint ck_biz_production_line_status check (status in ('0', '1'))
) engine=innodb comment = '客户现场产线主数据表';
-- 托盘资产主数据表
create table if not exists biz_pallet (
pallet_id bigint(20) not null auto_increment comment '托盘ID',
pallet_code varchar(64) not null comment '托盘编号',
qr_token varchar(128) not null comment '二维码固定标识',
capacity_packages int not null comment '容量包数',
asset_status char(1) not null default '0' comment '资产状态(0正常 1停用)',
create_by varchar(64) default '' comment '创建者',
create_time datetime comment '创建时间',
update_by varchar(64) default '' comment '更新者',
update_time datetime comment '更新时间',
remark varchar(500) default null comment '备注',
primary key (pallet_id),
constraint uq_biz_pallet_code unique (pallet_code),
constraint uq_biz_pallet_qr_token unique (qr_token),
constraint ck_biz_pallet_capacity_packages check (capacity_packages > 0),
constraint ck_biz_pallet_asset_status check (asset_status in ('0', '1'))
) engine=innodb comment = '托盘资产主数据表';
-- 用户与仓库关联表
create table if not exists biz_user_warehouse (
user_id bigint(20) not null comment '用户ID',
warehouse_id bigint(20) not null comment '仓库ID',
create_by varchar(64) default '' comment '创建者',
create_time datetime comment '创建时间',
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)
) engine=innodb comment = '用户与仓库关联表';
-- 用户与客户现场关联表
create table if not exists biz_user_site (
user_id bigint(20) not null comment '用户ID',
site_id bigint(20) not null comment '现场ID',
create_by varchar(64) default '' comment '创建者',
create_time datetime comment '创建时间',
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)
) engine=innodb comment = '用户与客户现场关联表';
-- 首次入库单
create table if not exists biz_inbound_order (
inbound_id bigint(20) not null auto_increment comment '入库单ID',
inbound_no varchar(64) not null comment '入库单号',
warehouse_id bigint(20) not null comment '仓库ID',
status varchar(32) not null default 'DRAFT' comment '单据状态',
confirm_idempotency_key varchar(64) comment '确认幂等键',
confirm_request_hash varchar(64) comment '确认请求摘要',
confirmed_by bigint(20) comment '确认人ID',
confirmed_at datetime comment '确认时间',
create_by varchar(64) default '' comment '创建者',
create_time datetime comment '创建时间',
update_by varchar(64) default '' comment '更新者',
update_time datetime comment '更新时间',
remark varchar(500) default null comment '备注',
primary key (inbound_id),
constraint uq_biz_inbound_order_no unique (inbound_no),
constraint uq_biz_inbound_order_confirm_idempotency unique (confirm_idempotency_key),
constraint fk_biz_inbound_order_warehouse foreign key (warehouse_id) references biz_warehouse (warehouse_id),
constraint fk_biz_inbound_order_confirmed_by foreign key (confirmed_by) references sys_user (user_id),
constraint ck_biz_inbound_order_status check (status in ('DRAFT', 'CONFIRMED'))
) engine=innodb comment = '首次入库单';
-- 首次入库单托盘明细
create table if not exists biz_inbound_item (
inbound_item_id bigint(20) not null auto_increment comment '入库明细ID',
inbound_id bigint(20) not null comment '入库单ID',
pallet_id bigint(20) not null comment '托盘ID',
create_by varchar(64) default '' comment '创建者',
create_time datetime comment '创建时间',
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),
constraint fk_biz_inbound_item_pallet foreign key (pallet_id) references biz_pallet (pallet_id)
) engine=innodb comment = '首次入库单托盘明细';
-- 托盘装填记录
create table if not exists biz_filling_record (
filling_id bigint(20) not null auto_increment comment '装填记录ID',
filling_no varchar(64) not null comment '装填单号',
pallet_id bigint(20) not null comment '托盘ID',
warehouse_id bigint(20) not null comment '仓库ID',
strain_id bigint(20) not null comment '菌种ID',
strain_code_snapshot varchar(64) not null comment '菌种编码快照',
strain_name_snapshot varchar(100) not null comment '菌种名称快照',
batch_no varchar(64) not null comment '生产批次',
package_spec varchar(64) not null comment '包装规格',
initial_packages int not null comment '初始包数',
active_slot varchar(16) comment '活动装填槽',
closed_at datetime comment '装填周期关闭时间',
idempotency_key varchar(64) not null comment '幂等键',
request_hash varchar(64) not null comment '请求摘要',
operator_id bigint(20) not null comment '操作人ID',
operated_at datetime not null comment '操作时间',
create_by varchar(64) default '' comment '创建者',
create_time datetime comment '创建时间',
update_by varchar(64) default '' comment '更新者',
update_time datetime comment '更新时间',
remark varchar(500) default null comment '备注',
primary key (filling_id),
constraint uq_biz_filling_record_no unique (filling_no),
constraint uq_biz_filling_record_idempotency unique (idempotency_key),
constraint uq_biz_filling_record_pallet_active_slot unique (pallet_id, active_slot),
constraint fk_biz_filling_record_pallet foreign key (pallet_id) references biz_pallet (pallet_id),
constraint fk_biz_filling_record_warehouse foreign key (warehouse_id) references biz_warehouse (warehouse_id),
constraint fk_biz_filling_record_strain foreign key (strain_id) references biz_strain (strain_id),
constraint fk_biz_filling_record_operator foreign key (operator_id) references sys_user (user_id),
constraint ck_biz_filling_record_initial_packages check (initial_packages > 0),
constraint ck_biz_filling_record_active_slot check (active_slot is null or active_slot = 'ACTIVE')
) engine=innodb comment = '托盘装填记录';
-- 整托发货单
create table if not exists biz_shipment_order (
shipment_id bigint(20) not null auto_increment comment '发货单ID',
shipment_no varchar(64) not null comment '发货单号',
source_warehouse_id bigint(20) not null comment '来源仓库ID',
target_site_id bigint(20) not null comment '目标现场ID',
status varchar(32) not null default 'DRAFT' comment '单据状态',
confirm_idempotency_key varchar(64) comment '确认幂等键',
confirm_request_hash varchar(64) comment '确认请求摘要',
confirmed_by bigint(20) comment '确认人ID',
confirmed_at datetime comment '确认时间',
create_by varchar(64) default '' comment '创建者',
create_time datetime comment '创建时间',
update_by varchar(64) default '' comment '更新者',
update_time datetime comment '更新时间',
remark varchar(500) default null comment '备注',
primary key (shipment_id),
constraint uq_biz_shipment_order_no unique (shipment_no),
constraint uq_biz_shipment_order_confirm_idempotency unique (confirm_idempotency_key),
constraint fk_biz_shipment_order_warehouse foreign key (source_warehouse_id) references biz_warehouse (warehouse_id),
constraint fk_biz_shipment_order_site foreign key (target_site_id) references biz_customer_site (site_id),
constraint fk_biz_shipment_order_confirmed_by foreign key (confirmed_by) references sys_user (user_id),
constraint ck_biz_shipment_order_status check (status in ('DRAFT', 'CONFIRMED'))
) engine=innodb comment = '整托发货单';
-- 整托发货明细
create table if not exists biz_shipment_item (
shipment_item_id bigint(20) not null auto_increment comment '发货明细ID',
shipment_id bigint(20) not null comment '发货单ID',
pallet_id bigint(20) not null comment '托盘ID',
active_filling_id bigint(20) comment '确认时活动装填记录ID',
package_qty int comment '确认时包数快照',
create_by varchar(64) default '' comment '创建者',
create_time datetime comment '创建时间',
primary key (shipment_item_id),
constraint uq_biz_shipment_item_order_pallet unique (shipment_id, pallet_id),
constraint fk_biz_shipment_item_order foreign key (shipment_id) references biz_shipment_order (shipment_id),
constraint fk_biz_shipment_item_pallet foreign key (pallet_id) references biz_pallet (pallet_id),
constraint fk_biz_shipment_item_filling foreign key (active_filling_id) references biz_filling_record (filling_id),
constraint ck_biz_shipment_item_package_qty check (package_qty is null or package_qty > 0)
) engine=innodb comment = '整托发货明细';
-- 客户现场使用事实记录
create table if not exists biz_usage_record (
usage_id bigint(20) not null auto_increment comment '使用记录ID',
usage_no varchar(64) not null comment '使用记录号',
pallet_id bigint(20) not null comment '托盘ID',
active_filling_id bigint(20) not null comment '活动装填记录ID',
site_id bigint(20) not null comment '现场ID',
line_id bigint(20) not null comment '产线ID',
used_packages int not null comment '本次使用包数',
remaining_packages_after int not null comment '使用后剩余包数',
idempotency_key varchar(64) not null comment '幂等键',
request_hash varchar(64) not null comment '请求摘要',
operator_id bigint(20) not null comment '操作人ID',
occurred_at datetime not null comment '发生时间',
primary key (usage_id),
constraint uq_biz_usage_record_no unique (usage_no),
constraint uq_biz_usage_record_idempotency unique (idempotency_key),
constraint fk_biz_usage_record_pallet foreign key (pallet_id) references biz_pallet (pallet_id),
constraint fk_biz_usage_record_filling foreign key (active_filling_id) references biz_filling_record (filling_id),
constraint fk_biz_usage_record_site foreign key (site_id) references biz_customer_site (site_id),
constraint fk_biz_usage_record_line foreign key (line_id) references biz_production_line (line_id),
constraint fk_biz_usage_record_operator foreign key (operator_id) references sys_user (user_id),
constraint ck_biz_usage_record_used_packages check (used_packages > 0),
constraint ck_biz_usage_record_remaining_packages_after check (remaining_packages_after >= 0)
) engine=innodb comment = '客户现场使用事实记录';
-- 托盘退回单
create table if not exists biz_return_order (
return_id bigint(20) not null auto_increment comment '退回单ID',
return_no varchar(64) not null comment '退回单号',
source_site_id bigint(20) not null comment '来源现场ID',
target_warehouse_id bigint(20) not null comment '目标仓库ID',
status varchar(32) not null default 'REQUESTED' comment '退回状态',
request_idempotency_key varchar(64) not null comment '申请幂等键',
request_hash varchar(64) not null comment '申请请求摘要',
confirm_idempotency_key varchar(64) comment '确认幂等键',
confirm_request_hash varchar(64) comment '确认请求摘要',
requested_by bigint(20) not null comment '申请人ID',
requested_at datetime not null comment '申请时间',
confirmed_by bigint(20) comment '确认人ID',
confirmed_at datetime comment '确认时间',
create_by varchar(64) default '' comment '创建者',
create_time datetime comment '创建时间',
update_by varchar(64) default '' comment '更新者',
update_time datetime comment '更新时间',
remark varchar(500) default null comment '备注',
primary key (return_id),
constraint uq_biz_return_order_no unique (return_no),
constraint uq_biz_return_order_request_idempotency unique (request_idempotency_key),
constraint uq_biz_return_order_confirm_idempotency unique (confirm_idempotency_key),
constraint fk_biz_return_order_site foreign key (source_site_id) references biz_customer_site (site_id),
constraint fk_biz_return_order_warehouse foreign key (target_warehouse_id) references biz_warehouse (warehouse_id),
constraint fk_biz_return_order_requested_by foreign key (requested_by) references sys_user (user_id),
constraint fk_biz_return_order_confirmed_by foreign key (confirmed_by) references sys_user (user_id),
constraint ck_biz_return_order_status check (status in ('REQUESTED', 'CONFIRMED'))
) engine=innodb comment = '托盘退回单';
-- 托盘退回明细
create table if not exists biz_return_item (
return_item_id bigint(20) not null auto_increment comment '退回明细ID',
return_id bigint(20) not null comment '退回单ID',
pallet_id bigint(20) not null comment '托盘ID',
active_filling_id_snapshot bigint(20) comment '申请时活动装填记录快照',
remaining_packages_snapshot int not null comment '申请时剩余包数快照',
create_by varchar(64) default '' comment '创建者',
create_time datetime comment '创建时间',
primary key (return_item_id),
constraint uq_biz_return_item_order_pallet unique (return_id, pallet_id),
constraint fk_biz_return_item_order foreign key (return_id) references biz_return_order (return_id),
constraint fk_biz_return_item_pallet foreign key (pallet_id) references biz_pallet (pallet_id),
constraint fk_biz_return_item_filling foreign key (active_filling_id_snapshot) references biz_filling_record (filling_id),
constraint ck_biz_return_item_remaining_packages_snapshot check (remaining_packages_snapshot >= 0)
) engine=innodb comment = '托盘退回明细';
-- 托盘只追加流转事件;必须先于库存快照创建
create table if not exists biz_pallet_event (
event_id bigint(20) not null auto_increment comment '事件ID',
pallet_id bigint(20) not null comment '托盘ID',
event_seq int not null comment '托盘内事件序号',
event_type varchar(32) not null comment '事件类型',
source_type varchar(32) not null comment '来源类型',
source_id bigint(20) not null comment '来源记录ID',
before_state varchar(32) not null comment '变更前状态',
after_state varchar(32) not null comment '变更后状态',
before_warehouse_id bigint(20) comment '变更前仓库ID',
before_site_id bigint(20) comment '变更前现场ID',
before_line_id bigint(20) comment '变更前产线ID',
after_warehouse_id bigint(20) comment '变更后仓库ID',
after_site_id bigint(20) comment '变更后现场ID',
after_line_id bigint(20) comment '变更后产线ID',
before_remaining_packages int not null comment '变更前剩余包数',
after_remaining_packages int not null comment '变更后剩余包数',
operator_id bigint(20) not null comment '操作人ID',
occurred_at datetime not null comment '发生时间',
idempotency_key varchar(64) not null comment '幂等键',
request_hash varchar(64) not null comment '请求摘要',
primary key (event_id),
constraint uq_biz_pallet_event_pallet_seq unique (pallet_id, event_seq),
constraint uq_biz_pallet_event_pallet_idempotency unique (pallet_id, idempotency_key),
constraint fk_biz_pallet_event_pallet foreign key (pallet_id) references biz_pallet (pallet_id),
constraint fk_biz_pallet_event_before_warehouse foreign key (before_warehouse_id) references biz_warehouse (warehouse_id),
constraint fk_biz_pallet_event_before_site foreign key (before_site_id) references biz_customer_site (site_id),
constraint fk_biz_pallet_event_before_line foreign key (before_line_id) references biz_production_line (line_id),
constraint fk_biz_pallet_event_after_warehouse foreign key (after_warehouse_id) references biz_warehouse (warehouse_id),
constraint fk_biz_pallet_event_after_site foreign key (after_site_id) references biz_customer_site (site_id),
constraint fk_biz_pallet_event_after_line foreign key (after_line_id) references biz_production_line (line_id),
constraint fk_biz_pallet_event_operator foreign key (operator_id) references sys_user (user_id),
constraint ck_biz_pallet_event_event_type check (
event_type in (
'INBOUND_CONFIRMED',
'FILLING_CONFIRMED',
'SHIPMENT_CONFIRMED',
'USAGE_RECORDED',
'RETURN_REQUESTED',
'RETURN_CONFIRMED'
)
)
) engine=innodb comment = '托盘只追加流转事件';
-- 托盘当前库存、位置和流转状态
create table if not exists biz_pallet_inventory (
pallet_id bigint(20) not null comment '托盘ID',
active_filling_id bigint(20) comment '当前活动装填记录ID',
warehouse_id bigint(20) comment '当前仓库ID',
site_id bigint(20) comment '当前现场ID',
current_line_id bigint(20) comment '当前驻场周期绑定产线ID',
flow_state varchar(32) not null default 'UNSTOCKED' comment '托盘流转状态',
remaining_packages int not null default 0 comment '当前剩余包数',
last_event_id bigint(20) comment '最后事件ID',
version int not null default 0 comment '并发版本号',
updated_at datetime not null comment '更新时间',
primary key (pallet_id),
constraint fk_biz_pallet_inventory_pallet foreign key (pallet_id) references biz_pallet (pallet_id),
constraint fk_biz_pallet_inventory_filling foreign key (active_filling_id) references biz_filling_record (filling_id),
constraint fk_biz_pallet_inventory_warehouse foreign key (warehouse_id) references biz_warehouse (warehouse_id),
constraint fk_biz_pallet_inventory_site foreign key (site_id) references biz_customer_site (site_id),
constraint fk_biz_pallet_inventory_line foreign key (current_line_id) references biz_production_line (line_id),
constraint fk_biz_pallet_inventory_event foreign key (last_event_id) references biz_pallet_event (event_id),
constraint ck_biz_pallet_inventory_flow_state check (
flow_state in (
'UNSTOCKED',
'EMPTY_IN_WAREHOUSE',
'FILLED_IN_WAREHOUSE',
'AT_SITE_UNUSED',
'AT_SITE_PARTIAL',
'AT_SITE_EMPTY',
'RETURNING'
)
),
constraint ck_biz_pallet_inventory_remaining_packages check (remaining_packages >= 0),
constraint ck_biz_pallet_inventory_version check (version >= 0),
constraint ck_biz_pallet_inventory_location check (not (warehouse_id is not null and site_id is not null))
) engine=innodb comment = '托盘当前库存、位置和流转状态';
-- ==================== DML:清理旧 seed 并写入托盘业务菜单 ====================
-- 先解除所有待清理菜单的历史角色关联。
delete from sys_role_menu
where menu_id in (4, 99, 118, 119, 1061, 1062, 1063, 1064);
-- 再清理原 AI 菜单;外链菜单 99 按要求在字典清理后单独删除。
delete from sys_menu
where menu_id in (4, 118, 119, 1061, 1062, 1063, 1064);
delete from sys_dict_data
where dict_code between 33 and 69
or dict_type = 'ai_provider_type';
delete from sys_dict_type
where dict_id = 12
or dict_type = 'ai_provider_type';
delete from sys_menu
where menu_id = 99;
-- INSERT IGNORE 使明确 ID 的菜单 seed 可重复应用而不会生成重复记录。
insert ignore into sys_menu values
(121, '基础资料维护', 1, '10', 'businessMaster', null, '', '', 1, 0, 'M', '0', '0', '', '#', 'admin', sysdate(), '', null, ''),
(122, '业务管理', 1, '11', 'businessOperation', null, '', '', 1, 0, 'M', '0', '0', '', '#', 'admin', sysdate(), '', null, ''),
(123, '报表查询', 1, '12', 'businessReport', null, '', '', 1, 0, 'M', '0', '0', '', '#', 'admin', sysdate(), '', null, ''),
(502, '菌种资料', 121, '1', 'strain', 'master/strain/index', '', '', 1, 0, 'C', '0', '0', 'master:strain:list', '#', 'admin', sysdate(), '', null, ''),
(503, '客户资料', 121, '2', 'customer', 'master/customer/index', '', '', 1, 0, 'C', '0', '0', 'master:customer:list', '#', 'admin', sysdate(), '', null, ''),
(504, '客户现场', 121, '3', 'site', 'master/site/index', '', '', 1, 0, 'C', '0', '0', 'master:site:list', '#', 'admin', sysdate(), '', null, ''),
(505, '产线资料', 121, '4', 'line', 'master/line/index', '', '', 1, 0, 'C', '0', '0', 'master:line:list', '#', 'admin', sysdate(), '', null, ''),
(506, '仓库资料', 121, '5', 'warehouse', 'master/warehouse/index', '', '', 1, 0, 'C', '0', '0', 'master:warehouse:list', '#', 'admin', sysdate(), '', null, ''),
(507, '托盘资料', 121, '6', 'pallet', 'master/pallet/index', '', '', 1, 0, 'C', '0', '0', 'master:pallet:list', '#', 'admin', sysdate(), '', null, ''),
(508, '仓库入库', 122, '1', 'inbound', 'business/inbound/index', '', '', 1, 0, 'C', '0', '0', 'business:inbound:list', '#', 'admin', sysdate(), '', null, ''),
(509, '工厂装填', 122, '2', 'filling', 'business/filling/index', '', '', 1, 0, 'C', '0', '0', 'business:filling:list', '#', 'admin', sysdate(), '', null, ''),
(510, '发货出库', 122, '3', 'shipment', 'business/shipment/index', '', '', 1, 0, 'C', '0', '0', 'business:shipment:list', '#', 'admin', sysdate(), '', null, ''),
(511, '客户现场使用', 122, '4', 'usage', 'business/usage/index', '', '', 1, 0, 'C', '0', '0', 'business:usage:list', '#', 'admin', sysdate(), '', null, ''),
(512, '退回仓库', 122, '5', 'return', 'business/return/index', '', '', 1, 0, 'C', '0', '0', 'business:return:list', '#', 'admin', sysdate(), '', null, ''),
(513, '托盘库存', 123, '1', 'palletInventory', 'report/palletInventory/index', '', '', 1, 0, 'C', '0', '0', 'report:palletinventory:list', '#', 'admin', sysdate(), '', null, ''),
(514, '装填记录', 123, '2', 'reportFilling', 'report/filling/index', '', '', 1, 0, 'C', '0', '0', 'report:filling:list', '#', 'admin', sysdate(), '', null, ''),
(515, '发货出库', 123, '3', 'reportShipment', 'report/shipment/index', '', '', 1, 0, 'C', '0', '0', 'report:shipment:list', '#', 'admin', sysdate(), '', null, ''),
(516, '产线消耗', 123, '4', 'consumption', 'report/consumption/index', '', '', 1, 0, 'C', '0', '0', 'report:consumption:list', '#', 'admin', sysdate(), '', null, ''),
(517, '退回仓库', 123, '5', 'reportReturn', 'report/return/index', '', '', 1, 0, 'C', '0', '0', 'report:return:list', '#', 'admin', sysdate(), '', null, ''),
(518, '托盘追溯', 123, '6', 'palletTrace', 'report/palletTrace/index', '', '', 1, 0, 'C', '0', '0', 'report:pallettrace:list', '#', 'admin', sysdate(), '', null, ''),
(1065, '菌种查询', 502, '1', '', '', '', '', 1, 0, 'F', '0', '0', 'master:strain:query', '#', 'admin', sysdate(), '', null, ''),
(1066, '菌种新增', 502, '2', '', '', '', '', 1, 0, 'F', '0', '0', 'master:strain:add', '#', 'admin', sysdate(), '', null, ''),
(1067, '菌种修改', 502, '3', '', '', '', '', 1, 0, 'F', '0', '0', 'master:strain:edit', '#', 'admin', sysdate(), '', null, ''),
(1068, '菌种删除', 502, '4', '', '', '', '', 1, 0, 'F', '0', '0', 'master:strain:remove', '#', 'admin', sysdate(), '', null, ''),
(1069, '客户查询', 503, '1', '', '', '', '', 1, 0, 'F', '0', '0', 'master:customer:query', '#', 'admin', sysdate(), '', null, ''),
(1070, '客户新增', 503, '2', '', '', '', '', 1, 0, 'F', '0', '0', 'master:customer:add', '#', 'admin', sysdate(), '', null, ''),
(1071, '客户修改', 503, '3', '', '', '', '', 1, 0, 'F', '0', '0', 'master:customer:edit', '#', 'admin', sysdate(), '', null, ''),
(1072, '客户删除', 503, '4', '', '', '', '', 1, 0, 'F', '0', '0', 'master:customer:remove', '#', 'admin', sysdate(), '', null, ''),
(1073, '现场查询', 504, '1', '', '', '', '', 1, 0, 'F', '0', '0', 'master:site:query', '#', 'admin', sysdate(), '', null, ''),
(1074, '现场新增', 504, '2', '', '', '', '', 1, 0, 'F', '0', '0', 'master:site:add', '#', 'admin', sysdate(), '', null, ''),
(1075, '现场修改', 504, '3', '', '', '', '', 1, 0, 'F', '0', '0', 'master:site:edit', '#', 'admin', sysdate(), '', null, ''),
(1076, '现场删除', 504, '4', '', '', '', '', 1, 0, 'F', '0', '0', 'master:site:remove', '#', 'admin', sysdate(), '', null, ''),
(1077, '分配用户', 504, '5', '', '', '', '', 1, 0, 'F', '0', '0', 'master:site:assign', '#', 'admin', sysdate(), '', null, ''),
(1078, '产线查询', 505, '1', '', '', '', '', 1, 0, 'F', '0', '0', 'master:line:query', '#', 'admin', sysdate(), '', null, ''),
(1079, '产线新增', 505, '2', '', '', '', '', 1, 0, 'F', '0', '0', 'master:line:add', '#', 'admin', sysdate(), '', null, ''),
(1080, '产线修改', 505, '3', '', '', '', '', 1, 0, 'F', '0', '0', 'master:line:edit', '#', 'admin', sysdate(), '', null, ''),
(1081, '产线删除', 505, '4', '', '', '', '', 1, 0, 'F', '0', '0', 'master:line:remove', '#', 'admin', sysdate(), '', null, ''),
(1082, '仓库查询', 506, '1', '', '', '', '', 1, 0, 'F', '0', '0', 'master:warehouse:query', '#', 'admin', sysdate(), '', null, ''),
(1083, '仓库新增', 506, '2', '', '', '', '', 1, 0, 'F', '0', '0', 'master:warehouse:add', '#', 'admin', sysdate(), '', null, ''),
(1084, '仓库修改', 506, '3', '', '', '', '', 1, 0, 'F', '0', '0', 'master:warehouse:edit', '#', 'admin', sysdate(), '', null, ''),
(1085, '仓库删除', 506, '4', '', '', '', '', 1, 0, 'F', '0', '0', 'master:warehouse:remove', '#', 'admin', sysdate(), '', null, ''),
(1086, '分配用户', 506, '5', '', '', '', '', 1, 0, 'F', '0', '0', 'master:warehouse:assign', '#', 'admin', sysdate(), '', null, ''),
(1087, '托盘查询', 507, '1', '', '', '', '', 1, 0, 'F', '0', '0', 'master:pallet:query', '#', 'admin', sysdate(), '', null, ''),
(1088, '托盘新增', 507, '2', '', '', '', '', 1, 0, 'F', '0', '0', 'master:pallet:add', '#', 'admin', sysdate(), '', null, ''),
(1089, '托盘修改', 507, '3', '', '', '', '', 1, 0, 'F', '0', '0', 'master:pallet:edit', '#', 'admin', sysdate(), '', null, ''),
(1090, '托盘删除', 507, '4', '', '', '', '', 1, 0, 'F', '0', '0', 'master:pallet:remove', '#', 'admin', sysdate(), '', null, ''),
(1091, '托盘打印', 507, '5', '', '', '', '', 1, 0, 'F', '0', '0', 'master:pallet:print', '#', 'admin', sysdate(), '', null, ''),
(1092, '入库查询', 508, '1', '', '', '', '', 1, 0, 'F', '0', '0', 'business:inbound:query', '#', 'admin', sysdate(), '', null, ''),
(1093, '入库新增', 508, '2', '', '', '', '', 1, 0, 'F', '0', '0', 'business:inbound:add', '#', 'admin', sysdate(), '', null, ''),
(1094, '入库修改', 508, '3', '', '', '', '', 1, 0, 'F', '0', '0', 'business:inbound:edit', '#', 'admin', sysdate(), '', null, ''),
(1095, '入库删除', 508, '4', '', '', '', '', 1, 0, 'F', '0', '0', 'business:inbound:remove', '#', 'admin', sysdate(), '', null, ''),
(1096, '入库确认', 508, '5', '', '', '', '', 1, 0, 'F', '0', '0', 'business:inbound:confirm', '#', 'admin', sysdate(), '', null, ''),
(1097, '装填查询', 509, '1', '', '', '', '', 1, 0, 'F', '0', '0', 'business:filling:query', '#', 'admin', sysdate(), '', null, ''),
(1098, '装填新增', 509, '2', '', '', '', '', 1, 0, 'F', '0', '0', 'business:filling:add', '#', 'admin', sysdate(), '', null, ''),
(1099, '发货查询', 510, '1', '', '', '', '', 1, 0, 'F', '0', '0', 'business:shipment:query', '#', 'admin', sysdate(), '', null, ''),
(1100, '发货新增', 510, '2', '', '', '', '', 1, 0, 'F', '0', '0', 'business:shipment:add', '#', 'admin', sysdate(), '', null, ''),
(1101, '发货修改', 510, '3', '', '', '', '', 1, 0, 'F', '0', '0', 'business:shipment:edit', '#', 'admin', sysdate(), '', null, ''),
(1102, '发货删除', 510, '4', '', '', '', '', 1, 0, 'F', '0', '0', 'business:shipment:remove', '#', 'admin', sysdate(), '', null, ''),
(1103, '发货确认', 510, '5', '', '', '', '', 1, 0, 'F', '0', '0', 'business:shipment:confirm', '#', 'admin', sysdate(), '', null, ''),
(1104, '使用查询', 511, '1', '', '', '', '', 1, 0, 'F', '0', '0', 'business:usage:query', '#', 'admin', sysdate(), '', null, ''),
(1105, '使用新增', 511, '2', '', '', '', '', 1, 0, 'F', '0', '0', 'business:usage:add', '#', 'admin', sysdate(), '', null, ''),
(1106, '退回查询', 512, '1', '', '', '', '', 1, 0, 'F', '0', '0', 'business:return:query', '#', 'admin', sysdate(), '', null, ''),
(1107, '退回申请', 512, '2', '', '', '', '', 1, 0, 'F', '0', '0', 'business:return:request', '#', 'admin', sysdate(), '', null, ''),
(1108, '退回确认', 512, '3', '', '', '', '', 1, 0, 'F', '0', '0', 'business:return:confirm', '#', 'admin', sysdate(), '', null, '');
-- 应用完成后仅人工清理 /getRouters、用户信息以及 sys_dict:ai_provider_type 相关缓存;本补丁不执行缓存命令。
@@ -1,3 +1,25 @@
-- ----------------------------
-- 托盘业务表(须先于被引用的系统表按反向依赖删除)
-- ----------------------------
drop table if exists biz_pallet_inventory;
drop table if exists biz_pallet_event;
drop table if exists biz_return_item;
drop table if exists biz_return_order;
drop table if exists biz_usage_record;
drop table if exists biz_shipment_item;
drop table if exists biz_shipment_order;
drop table if exists biz_filling_record;
drop table if exists biz_inbound_item;
drop table if exists biz_inbound_order;
drop table if exists biz_user_site;
drop table if exists biz_user_warehouse;
drop table if exists biz_pallet;
drop table if exists biz_production_line;
drop table if exists biz_customer_site;
drop table if exists biz_customer;
drop table if exists biz_warehouse;
drop table if exists biz_strain;
-- ----------------------------
-- 1、部门表
-- ----------------------------
@@ -1050,3 +1072,444 @@ RETURN pg_catalog.array_to_string(tokens[indexnum:length], $2);
END IF;
END;
$$ IMMUTABLE STRICT LANGUAGE PLPGSQL;
-- ----------------------------
-- 20、托盘流转业务表
-- ----------------------------
create table biz_strain (
strain_id bigserial not null,
strain_code varchar(64) not null,
strain_name varchar(100) not null,
status char(1) not null default '0',
create_by varchar(64) default '',
create_time timestamp(0),
update_by varchar(64) default '',
update_time timestamp(0),
remark varchar(500) default null,
primary key (strain_id),
constraint uq_biz_strain_code unique (strain_code),
constraint ck_biz_strain_status check (status in ('0', '1'))
);
create table biz_warehouse (
warehouse_id bigserial not null,
warehouse_code varchar(64) not null,
warehouse_name varchar(100) not null,
address varchar(500),
status char(1) not null default '0',
create_by varchar(64) default '',
create_time timestamp(0),
update_by varchar(64) default '',
update_time timestamp(0),
remark varchar(500) default null,
primary key (warehouse_id),
constraint uq_biz_warehouse_code unique (warehouse_code),
constraint ck_biz_warehouse_status check (status in ('0', '1'))
);
create table biz_customer (
customer_id bigserial not null,
customer_code varchar(64) not null,
customer_name varchar(100) not null,
status char(1) not null default '0',
create_by varchar(64) default '',
create_time timestamp(0),
update_by varchar(64) default '',
update_time timestamp(0),
remark varchar(500) default null,
primary key (customer_id),
constraint uq_biz_customer_code unique (customer_code),
constraint ck_biz_customer_status check (status in ('0', '1'))
);
create table biz_customer_site (
site_id bigserial not null,
customer_id bigint not null,
site_code varchar(64) not null,
site_name varchar(100) not null,
address varchar(500),
status char(1) not null default '0',
create_by varchar(64) default '',
create_time timestamp(0),
update_by varchar(64) default '',
update_time timestamp(0),
remark varchar(500) default null,
primary key (site_id),
constraint uq_biz_customer_site_customer_code unique (customer_id, site_code),
constraint ck_biz_customer_site_status check (status in ('0', '1')),
constraint fk_biz_customer_site_customer foreign key (customer_id) references biz_customer(customer_id)
);
create table biz_production_line (
line_id bigserial not null,
site_id bigint not null,
line_code varchar(64) not null,
line_name varchar(100) not null,
status char(1) not null default '0',
create_by varchar(64) default '',
create_time timestamp(0),
update_by varchar(64) default '',
update_time timestamp(0),
remark varchar(500) default null,
primary key (line_id),
constraint uq_biz_production_line_site_code unique (site_id, line_code),
constraint ck_biz_production_line_status check (status in ('0', '1')),
constraint fk_biz_production_line_site foreign key (site_id) references biz_customer_site(site_id)
);
create table biz_pallet (
pallet_id bigserial not null,
pallet_code varchar(64) not null,
qr_token varchar(128) not null,
capacity_packages integer not null,
asset_status char(1) not null default '0',
create_by varchar(64) default '',
create_time timestamp(0),
update_by varchar(64) default '',
update_time timestamp(0),
remark varchar(500) default null,
primary key (pallet_id),
constraint uq_biz_pallet_code unique (pallet_code),
constraint uq_biz_pallet_qr_token unique (qr_token),
constraint ck_biz_pallet_capacity_packages check (capacity_packages > 0),
constraint ck_biz_pallet_asset_status check (asset_status in ('0', '1'))
);
create table biz_user_warehouse (
user_id bigint not null,
warehouse_id bigint not null,
create_by varchar(64) default '',
create_time timestamp(0),
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)
);
create table biz_user_site (
user_id bigint not null,
site_id bigint not null,
create_by varchar(64) default '',
create_time timestamp(0),
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)
);
create table biz_inbound_order (
inbound_id bigserial not null,
inbound_no varchar(64) not null,
warehouse_id bigint not null,
status varchar(32) not null default 'DRAFT',
confirm_idempotency_key varchar(64),
confirm_request_hash varchar(64),
confirmed_by bigint,
confirmed_at timestamp(0),
create_by varchar(64) default '',
create_time timestamp(0),
update_by varchar(64) default '',
update_time timestamp(0),
remark varchar(500) default null,
primary key (inbound_id),
constraint uq_biz_inbound_order_no unique (inbound_no),
constraint uq_biz_inbound_order_confirm_key unique (confirm_idempotency_key),
constraint ck_biz_inbound_order_status check (status in ('DRAFT', 'CONFIRMED')),
constraint fk_biz_inbound_order_warehouse foreign key (warehouse_id) references biz_warehouse(warehouse_id),
constraint fk_biz_inbound_order_confirmer foreign key (confirmed_by) references sys_user(user_id)
);
create table biz_inbound_item (
inbound_item_id bigserial not null,
inbound_id bigint not null,
pallet_id bigint not null,
create_by varchar(64) default '',
create_time timestamp(0),
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),
constraint fk_biz_inbound_item_pallet foreign key (pallet_id) references biz_pallet(pallet_id)
);
create table biz_filling_record (
filling_id bigserial not null,
filling_no varchar(64) not null,
pallet_id bigint not null,
warehouse_id bigint not null,
strain_id bigint not null,
strain_code_snapshot varchar(64) not null,
strain_name_snapshot varchar(100) not null,
batch_no varchar(64) not null,
package_spec varchar(64) not null,
initial_packages integer not null,
active_slot varchar(16),
closed_at timestamp(0),
idempotency_key varchar(64) not null,
request_hash varchar(64) not null,
operator_id bigint not null,
operated_at timestamp(0) not null,
create_by varchar(64) default '',
create_time timestamp(0),
update_by varchar(64) default '',
update_time timestamp(0),
remark varchar(500) default null,
primary key (filling_id),
constraint uq_biz_filling_record_no unique (filling_no),
constraint uq_biz_filling_record_idempotency unique (idempotency_key),
constraint uq_biz_filling_record_pallet_active_slot unique (pallet_id, active_slot),
constraint ck_biz_filling_record_initial_packages check (initial_packages > 0),
constraint ck_biz_filling_record_active_slot check (active_slot is null or active_slot = 'ACTIVE'),
constraint fk_biz_filling_record_pallet foreign key (pallet_id) references biz_pallet(pallet_id),
constraint fk_biz_filling_record_warehouse foreign key (warehouse_id) references biz_warehouse(warehouse_id),
constraint fk_biz_filling_record_strain foreign key (strain_id) references biz_strain(strain_id),
constraint fk_biz_filling_record_operator foreign key (operator_id) references sys_user(user_id)
);
create table biz_shipment_order (
shipment_id bigserial not null,
shipment_no varchar(64) not null,
source_warehouse_id bigint not null,
target_site_id bigint not null,
status varchar(32) not null default 'DRAFT',
confirm_idempotency_key varchar(64),
confirm_request_hash varchar(64),
confirmed_by bigint,
confirmed_at timestamp(0),
create_by varchar(64) default '',
create_time timestamp(0),
update_by varchar(64) default '',
update_time timestamp(0),
remark varchar(500) default null,
primary key (shipment_id),
constraint uq_biz_shipment_order_no unique (shipment_no),
constraint uq_biz_shipment_order_confirm_key unique (confirm_idempotency_key),
constraint ck_biz_shipment_order_status check (status in ('DRAFT', 'CONFIRMED')),
constraint fk_biz_shipment_order_warehouse foreign key (source_warehouse_id) references biz_warehouse(warehouse_id),
constraint fk_biz_shipment_order_site foreign key (target_site_id) references biz_customer_site(site_id),
constraint fk_biz_shipment_order_confirmer foreign key (confirmed_by) references sys_user(user_id)
);
create table biz_shipment_item (
shipment_item_id bigserial not null,
shipment_id bigint not null,
pallet_id bigint not null,
active_filling_id bigint,
package_qty integer,
create_by varchar(64) default '',
create_time timestamp(0),
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),
constraint fk_biz_shipment_item_order foreign key (shipment_id) references biz_shipment_order(shipment_id),
constraint fk_biz_shipment_item_pallet foreign key (pallet_id) references biz_pallet(pallet_id),
constraint fk_biz_shipment_item_filling foreign key (active_filling_id) references biz_filling_record(filling_id)
);
create table biz_usage_record (
usage_id bigserial not null,
usage_no varchar(64) not null,
pallet_id bigint not null,
active_filling_id bigint not null,
site_id bigint not null,
line_id bigint not null,
used_packages integer not null,
remaining_packages_after integer not null,
idempotency_key varchar(64) not null,
request_hash varchar(64) not null,
operator_id bigint not null,
occurred_at timestamp(0) not null,
primary key (usage_id),
constraint uq_biz_usage_record_no unique (usage_no),
constraint uq_biz_usage_record_idempotency unique (idempotency_key),
constraint ck_biz_usage_record_used_packages check (used_packages > 0),
constraint ck_biz_usage_record_remaining_packages_after check (remaining_packages_after >= 0),
constraint fk_biz_usage_record_pallet foreign key (pallet_id) references biz_pallet(pallet_id),
constraint fk_biz_usage_record_filling foreign key (active_filling_id) references biz_filling_record(filling_id),
constraint fk_biz_usage_record_site foreign key (site_id) references biz_customer_site(site_id),
constraint fk_biz_usage_record_line foreign key (line_id) references biz_production_line(line_id),
constraint fk_biz_usage_record_operator foreign key (operator_id) references sys_user(user_id)
);
create table biz_return_order (
return_id bigserial not null,
return_no varchar(64) not null,
source_site_id bigint not null,
target_warehouse_id bigint not null,
status varchar(32) not null default 'REQUESTED',
request_idempotency_key varchar(64) not null,
request_hash varchar(64) not null,
confirm_idempotency_key varchar(64),
confirm_request_hash varchar(64),
requested_by bigint not null,
requested_at timestamp(0) not null,
confirmed_by bigint,
confirmed_at timestamp(0),
create_by varchar(64) default '',
create_time timestamp(0),
update_by varchar(64) default '',
update_time timestamp(0),
remark varchar(500) default null,
primary key (return_id),
constraint uq_biz_return_order_no unique (return_no),
constraint uq_biz_return_order_request_key unique (request_idempotency_key),
constraint uq_biz_return_order_confirm_key unique (confirm_idempotency_key),
constraint ck_biz_return_order_status check (status in ('REQUESTED', 'CONFIRMED')),
constraint fk_biz_return_order_site foreign key (source_site_id) references biz_customer_site(site_id),
constraint fk_biz_return_order_warehouse foreign key (target_warehouse_id) references biz_warehouse(warehouse_id),
constraint fk_biz_return_order_requester foreign key (requested_by) references sys_user(user_id),
constraint fk_biz_return_order_confirmer foreign key (confirmed_by) references sys_user(user_id)
);
create table biz_return_item (
return_item_id bigserial not null,
return_id bigint not null,
pallet_id bigint not null,
active_filling_id_snapshot bigint,
remaining_packages_snapshot integer not null,
create_by varchar(64) default '',
create_time timestamp(0),
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),
constraint fk_biz_return_item_order foreign key (return_id) references biz_return_order(return_id),
constraint fk_biz_return_item_pallet foreign key (pallet_id) references biz_pallet(pallet_id),
constraint fk_biz_return_item_filling foreign key (active_filling_id_snapshot) references biz_filling_record(filling_id)
);
create table biz_pallet_event (
event_id bigserial not null,
pallet_id bigint not null,
event_seq integer not null,
event_type varchar(32) not null,
source_type varchar(32) not null,
source_id bigint not null,
before_state varchar(32) not null,
after_state varchar(32) not null,
before_warehouse_id bigint,
before_site_id bigint,
before_line_id bigint,
after_warehouse_id bigint,
after_site_id bigint,
after_line_id bigint,
before_remaining_packages integer not null,
after_remaining_packages integer not null,
operator_id bigint not null,
occurred_at timestamp(0) not null,
idempotency_key varchar(64) not null,
request_hash varchar(64) not null,
primary key (event_id),
constraint uq_biz_pallet_event_pallet_seq unique (pallet_id, event_seq),
constraint uq_biz_pallet_event_pallet_idempotency unique (pallet_id, idempotency_key),
constraint ck_biz_pallet_event_event_type check (
event_type in (
'INBOUND_CONFIRMED', 'FILLING_CONFIRMED', 'SHIPMENT_CONFIRMED',
'USAGE_RECORDED', 'RETURN_REQUESTED', 'RETURN_CONFIRMED'
)
),
constraint fk_biz_pallet_event_pallet foreign key (pallet_id) references biz_pallet(pallet_id),
constraint fk_biz_pallet_event_before_warehouse foreign key (before_warehouse_id) references biz_warehouse(warehouse_id),
constraint fk_biz_pallet_event_before_site foreign key (before_site_id) references biz_customer_site(site_id),
constraint fk_biz_pallet_event_before_line foreign key (before_line_id) references biz_production_line(line_id),
constraint fk_biz_pallet_event_after_warehouse foreign key (after_warehouse_id) references biz_warehouse(warehouse_id),
constraint fk_biz_pallet_event_after_site foreign key (after_site_id) references biz_customer_site(site_id),
constraint fk_biz_pallet_event_after_line foreign key (after_line_id) references biz_production_line(line_id),
constraint fk_biz_pallet_event_operator foreign key (operator_id) references sys_user(user_id)
);
create table biz_pallet_inventory (
pallet_id bigint not null,
active_filling_id bigint,
warehouse_id bigint,
site_id bigint,
current_line_id bigint,
flow_state varchar(32) not null default 'UNSTOCKED',
remaining_packages integer not null default 0,
last_event_id bigint,
version integer not null default 0,
updated_at timestamp(0) not null,
primary key (pallet_id),
constraint ck_biz_pallet_inventory_flow_state check (
flow_state in (
'UNSTOCKED', 'EMPTY_IN_WAREHOUSE', 'FILLED_IN_WAREHOUSE', 'AT_SITE_UNUSED',
'AT_SITE_PARTIAL', 'AT_SITE_EMPTY', 'RETURNING'
)
),
constraint ck_biz_pallet_inventory_remaining_packages check (remaining_packages >= 0),
constraint ck_biz_pallet_inventory_version check (version >= 0),
constraint ck_biz_pallet_inventory_location check (
not (warehouse_id is not null and site_id is not null)
),
constraint fk_biz_pallet_inventory_pallet foreign key (pallet_id) references biz_pallet(pallet_id),
constraint fk_biz_pallet_inventory_filling foreign key (active_filling_id) references biz_filling_record(filling_id),
constraint fk_biz_pallet_inventory_warehouse foreign key (warehouse_id) references biz_warehouse(warehouse_id),
constraint fk_biz_pallet_inventory_site foreign key (site_id) references biz_customer_site(site_id),
constraint fk_biz_pallet_inventory_line foreign key (current_line_id) references biz_production_line(line_id),
constraint fk_biz_pallet_inventory_event foreign key (last_event_id) references biz_pallet_event(event_id)
);
-- ----------------------------
-- 初始化-托盘业务菜单和权限
-- ----------------------------
insert into sys_menu values
(121, '基础资料维护', 1, '10', 'businessMaster', null, '', '', 1, 0, 'M', '0', '0', '', '#', 'admin', current_timestamp, '', null, ''),
(122, '业务管理', 1, '11', 'businessOperation', null, '', '', 1, 0, 'M', '0', '0', '', '#', 'admin', current_timestamp, '', null, ''),
(123, '报表查询', 1, '12', 'businessReport', null, '', '', 1, 0, 'M', '0', '0', '', '#', 'admin', current_timestamp, '', null, ''),
(502, '菌种资料', 121, '1', 'strain', 'master/strain/index', '', '', 1, 0, 'C', '0', '0', 'master:strain:list', '#', 'admin', current_timestamp, '', null, ''),
(503, '客户资料', 121, '2', 'customer', 'master/customer/index', '', '', 1, 0, 'C', '0', '0', 'master:customer:list', '#', 'admin', current_timestamp, '', null, ''),
(504, '客户现场', 121, '3', 'site', 'master/site/index', '', '', 1, 0, 'C', '0', '0', 'master:site:list', '#', 'admin', current_timestamp, '', null, ''),
(505, '产线资料', 121, '4', 'line', 'master/line/index', '', '', 1, 0, 'C', '0', '0', 'master:line:list', '#', 'admin', current_timestamp, '', null, ''),
(506, '仓库资料', 121, '5', 'warehouse', 'master/warehouse/index', '', '', 1, 0, 'C', '0', '0', 'master:warehouse:list', '#', 'admin', current_timestamp, '', null, ''),
(507, '托盘资料', 121, '6', 'pallet', 'master/pallet/index', '', '', 1, 0, 'C', '0', '0', 'master:pallet:list', '#', 'admin', current_timestamp, '', null, ''),
(508, '仓库入库', 122, '1', 'inbound', 'business/inbound/index', '', '', 1, 0, 'C', '0', '0', 'business:inbound:list', '#', 'admin', current_timestamp, '', null, ''),
(509, '工厂装填', 122, '2', 'filling', 'business/filling/index', '', '', 1, 0, 'C', '0', '0', 'business:filling:list', '#', 'admin', current_timestamp, '', null, ''),
(510, '发货出库', 122, '3', 'shipment', 'business/shipment/index', '', '', 1, 0, 'C', '0', '0', 'business:shipment:list', '#', 'admin', current_timestamp, '', null, ''),
(511, '客户现场使用', 122, '4', 'usage', 'business/usage/index', '', '', 1, 0, 'C', '0', '0', 'business:usage:list', '#', 'admin', current_timestamp, '', null, ''),
(512, '退回仓库', 122, '5', 'return', 'business/return/index', '', '', 1, 0, 'C', '0', '0', 'business:return:list', '#', 'admin', current_timestamp, '', null, ''),
(513, '托盘库存', 123, '1', 'palletInventory', 'report/palletInventory/index', '', '', 1, 0, 'C', '0', '0', 'report:palletinventory:list', '#', 'admin', current_timestamp, '', null, ''),
(514, '装填记录', 123, '2', 'reportFilling', 'report/filling/index', '', '', 1, 0, 'C', '0', '0', 'report:filling:list', '#', 'admin', current_timestamp, '', null, ''),
(515, '发货出库', 123, '3', 'reportShipment', 'report/shipment/index', '', '', 1, 0, 'C', '0', '0', 'report:shipment:list', '#', 'admin', current_timestamp, '', null, ''),
(516, '产线消耗', 123, '4', 'consumption', 'report/consumption/index', '', '', 1, 0, 'C', '0', '0', 'report:consumption:list', '#', 'admin', current_timestamp, '', null, ''),
(517, '退回仓库', 123, '5', 'reportReturn', 'report/return/index', '', '', 1, 0, 'C', '0', '0', 'report:return:list', '#', 'admin', current_timestamp, '', null, ''),
(518, '托盘追溯', 123, '6', 'palletTrace', 'report/palletTrace/index', '', '', 1, 0, 'C', '0', '0', 'report:pallettrace:list', '#', 'admin', current_timestamp, '', null, ''),
(1065, '菌种查询', 502, '1', '', '', '', '', 1, 0, 'F', '0', '0', 'master:strain:query', '#', 'admin', current_timestamp, '', null, ''),
(1066, '菌种新增', 502, '2', '', '', '', '', 1, 0, 'F', '0', '0', 'master:strain:add', '#', 'admin', current_timestamp, '', null, ''),
(1067, '菌种修改', 502, '3', '', '', '', '', 1, 0, 'F', '0', '0', 'master:strain:edit', '#', 'admin', current_timestamp, '', null, ''),
(1068, '菌种删除', 502, '4', '', '', '', '', 1, 0, 'F', '0', '0', 'master:strain:remove', '#', 'admin', current_timestamp, '', null, ''),
(1069, '客户查询', 503, '1', '', '', '', '', 1, 0, 'F', '0', '0', 'master:customer:query', '#', 'admin', current_timestamp, '', null, ''),
(1070, '客户新增', 503, '2', '', '', '', '', 1, 0, 'F', '0', '0', 'master:customer:add', '#', 'admin', current_timestamp, '', null, ''),
(1071, '客户修改', 503, '3', '', '', '', '', 1, 0, 'F', '0', '0', 'master:customer:edit', '#', 'admin', current_timestamp, '', null, ''),
(1072, '客户删除', 503, '4', '', '', '', '', 1, 0, 'F', '0', '0', 'master:customer:remove', '#', 'admin', current_timestamp, '', null, ''),
(1073, '现场查询', 504, '1', '', '', '', '', 1, 0, 'F', '0', '0', 'master:site:query', '#', 'admin', current_timestamp, '', null, ''),
(1074, '现场新增', 504, '2', '', '', '', '', 1, 0, 'F', '0', '0', 'master:site:add', '#', 'admin', current_timestamp, '', null, ''),
(1075, '现场修改', 504, '3', '', '', '', '', 1, 0, 'F', '0', '0', 'master:site:edit', '#', 'admin', current_timestamp, '', null, ''),
(1076, '现场删除', 504, '4', '', '', '', '', 1, 0, 'F', '0', '0', 'master:site:remove', '#', 'admin', current_timestamp, '', null, ''),
(1077, '分配用户', 504, '5', '', '', '', '', 1, 0, 'F', '0', '0', 'master:site:assign', '#', 'admin', current_timestamp, '', null, ''),
(1078, '产线查询', 505, '1', '', '', '', '', 1, 0, 'F', '0', '0', 'master:line:query', '#', 'admin', current_timestamp, '', null, ''),
(1079, '产线新增', 505, '2', '', '', '', '', 1, 0, 'F', '0', '0', 'master:line:add', '#', 'admin', current_timestamp, '', null, ''),
(1080, '产线修改', 505, '3', '', '', '', '', 1, 0, 'F', '0', '0', 'master:line:edit', '#', 'admin', current_timestamp, '', null, ''),
(1081, '产线删除', 505, '4', '', '', '', '', 1, 0, 'F', '0', '0', 'master:line:remove', '#', 'admin', current_timestamp, '', null, ''),
(1082, '仓库查询', 506, '1', '', '', '', '', 1, 0, 'F', '0', '0', 'master:warehouse:query', '#', 'admin', current_timestamp, '', null, ''),
(1083, '仓库新增', 506, '2', '', '', '', '', 1, 0, 'F', '0', '0', 'master:warehouse:add', '#', 'admin', current_timestamp, '', null, ''),
(1084, '仓库修改', 506, '3', '', '', '', '', 1, 0, 'F', '0', '0', 'master:warehouse:edit', '#', 'admin', current_timestamp, '', null, ''),
(1085, '仓库删除', 506, '4', '', '', '', '', 1, 0, 'F', '0', '0', 'master:warehouse:remove', '#', 'admin', current_timestamp, '', null, ''),
(1086, '分配用户', 506, '5', '', '', '', '', 1, 0, 'F', '0', '0', 'master:warehouse:assign', '#', 'admin', current_timestamp, '', null, ''),
(1087, '托盘查询', 507, '1', '', '', '', '', 1, 0, 'F', '0', '0', 'master:pallet:query', '#', 'admin', current_timestamp, '', null, ''),
(1088, '托盘新增', 507, '2', '', '', '', '', 1, 0, 'F', '0', '0', 'master:pallet:add', '#', 'admin', current_timestamp, '', null, ''),
(1089, '托盘修改', 507, '3', '', '', '', '', 1, 0, 'F', '0', '0', 'master:pallet:edit', '#', 'admin', current_timestamp, '', null, ''),
(1090, '托盘删除', 507, '4', '', '', '', '', 1, 0, 'F', '0', '0', 'master:pallet:remove', '#', 'admin', current_timestamp, '', null, ''),
(1091, '托盘打印', 507, '5', '', '', '', '', 1, 0, 'F', '0', '0', 'master:pallet:print', '#', 'admin', current_timestamp, '', null, ''),
(1092, '入库查询', 508, '1', '', '', '', '', 1, 0, 'F', '0', '0', 'business:inbound:query', '#', 'admin', current_timestamp, '', null, ''),
(1093, '入库新增', 508, '2', '', '', '', '', 1, 0, 'F', '0', '0', 'business:inbound:add', '#', 'admin', current_timestamp, '', null, ''),
(1094, '入库修改', 508, '3', '', '', '', '', 1, 0, 'F', '0', '0', 'business:inbound:edit', '#', 'admin', current_timestamp, '', null, ''),
(1095, '入库删除', 508, '4', '', '', '', '', 1, 0, 'F', '0', '0', 'business:inbound:remove', '#', 'admin', current_timestamp, '', null, ''),
(1096, '入库确认', 508, '5', '', '', '', '', 1, 0, 'F', '0', '0', 'business:inbound:confirm', '#', 'admin', current_timestamp, '', null, ''),
(1097, '装填查询', 509, '1', '', '', '', '', 1, 0, 'F', '0', '0', 'business:filling:query', '#', 'admin', current_timestamp, '', null, ''),
(1098, '装填新增', 509, '2', '', '', '', '', 1, 0, 'F', '0', '0', 'business:filling:add', '#', 'admin', current_timestamp, '', null, ''),
(1099, '发货查询', 510, '1', '', '', '', '', 1, 0, 'F', '0', '0', 'business:shipment:query', '#', 'admin', current_timestamp, '', null, ''),
(1100, '发货新增', 510, '2', '', '', '', '', 1, 0, 'F', '0', '0', 'business:shipment:add', '#', 'admin', current_timestamp, '', null, ''),
(1101, '发货修改', 510, '3', '', '', '', '', 1, 0, 'F', '0', '0', 'business:shipment:edit', '#', 'admin', current_timestamp, '', null, ''),
(1102, '发货删除', 510, '4', '', '', '', '', 1, 0, 'F', '0', '0', 'business:shipment:remove', '#', 'admin', current_timestamp, '', null, ''),
(1103, '发货确认', 510, '5', '', '', '', '', 1, 0, 'F', '0', '0', 'business:shipment:confirm', '#', 'admin', current_timestamp, '', null, ''),
(1104, '使用查询', 511, '1', '', '', '', '', 1, 0, 'F', '0', '0', 'business:usage:query', '#', 'admin', current_timestamp, '', null, ''),
(1105, '使用新增', 511, '2', '', '', '', '', 1, 0, 'F', '0', '0', 'business:usage:add', '#', 'admin', current_timestamp, '', null, ''),
(1106, '退回查询', 512, '1', '', '', '', '', 1, 0, 'F', '0', '0', 'business:return:query', '#', 'admin', current_timestamp, '', null, ''),
(1107, '退回申请', 512, '2', '', '', '', '', 1, 0, 'F', '0', '0', 'business:return:request', '#', 'admin', current_timestamp, '', null, ''),
(1108, '退回确认', 512, '3', '', '', '', '', 1, 0, 'F', '0', '0', 'business:return:confirm', '#', 'admin', current_timestamp, '', null, '');
@@ -1,3 +1,25 @@
-- ----------------------------
-- 托盘业务表(须先于被引用的系统表按反向依赖删除)
-- ----------------------------
drop table if exists biz_pallet_inventory;
drop table if exists biz_pallet_event;
drop table if exists biz_return_item;
drop table if exists biz_return_order;
drop table if exists biz_usage_record;
drop table if exists biz_shipment_item;
drop table if exists biz_shipment_order;
drop table if exists biz_filling_record;
drop table if exists biz_inbound_item;
drop table if exists biz_inbound_order;
drop table if exists biz_user_site;
drop table if exists biz_user_warehouse;
drop table if exists biz_pallet;
drop table if exists biz_production_line;
drop table if exists biz_customer_site;
drop table if exists biz_customer;
drop table if exists biz_warehouse;
drop table if exists biz_strain;
-- ----------------------------
-- 1、部门表
-- ----------------------------
@@ -260,6 +282,76 @@ insert into sys_menu values('1058', '导入代码', '116', '4', '#', '', '', '',
insert into sys_menu values('1059', '预览代码', '116', '5', '#', '', '', '', 1, 0, 'F', '0', '0', 'tool:gen:preview', '#', 'admin', sysdate(), '', null, '');
insert into sys_menu values('1060', '生成代码', '116', '6', '#', '', '', '', 1, 0, 'F', '0', '0', 'tool:gen:code', '#', 'admin', sysdate(), '', null, '');
-- 托盘业务目录
insert into sys_menu values('121', '基础资料维护', '1', '10', 'businessMaster', null, '', '', 1, 0, 'M', '0', '0', '', '#', 'admin', sysdate(), '', null, '基础资料维护目录');
insert into sys_menu values('122', '业务管理', '1', '11', 'businessOperation', null, '', '', 1, 0, 'M', '0', '0', '', '#', 'admin', sysdate(), '', null, '业务管理目录');
insert into sys_menu values('123', '报表查询', '1', '12', 'businessReport', null, '', '', 1, 0, 'M', '0', '0', '', '#', 'admin', sysdate(), '', null, '报表查询目录');
-- 托盘业务页面
insert into sys_menu values('502', '菌种资料', '121', '1', 'strain', 'master/strain/index', '', '', 1, 0, 'C', '0', '0', 'master:strain:list', '#', 'admin', sysdate(), '', null, '菌种资料菜单');
insert into sys_menu values('503', '客户资料', '121', '2', 'customer', 'master/customer/index', '', '', 1, 0, 'C', '0', '0', 'master:customer:list', '#', 'admin', sysdate(), '', null, '客户资料菜单');
insert into sys_menu values('504', '客户现场', '121', '3', 'site', 'master/site/index', '', '', 1, 0, 'C', '0', '0', 'master:site:list', '#', 'admin', sysdate(), '', null, '客户现场菜单');
insert into sys_menu values('505', '产线资料', '121', '4', 'line', 'master/line/index', '', '', 1, 0, 'C', '0', '0', 'master:line:list', '#', 'admin', sysdate(), '', null, '产线资料菜单');
insert into sys_menu values('506', '仓库资料', '121', '5', 'warehouse', 'master/warehouse/index', '', '', 1, 0, 'C', '0', '0', 'master:warehouse:list', '#', 'admin', sysdate(), '', null, '仓库资料菜单');
insert into sys_menu values('507', '托盘资料', '121', '6', 'pallet', 'master/pallet/index', '', '', 1, 0, 'C', '0', '0', 'master:pallet:list', '#', 'admin', sysdate(), '', null, '托盘资料菜单');
insert into sys_menu values('508', '仓库入库', '122', '1', 'inbound', 'business/inbound/index', '', '', 1, 0, 'C', '0', '0', 'business:inbound:list', '#', 'admin', sysdate(), '', null, '仓库入库菜单');
insert into sys_menu values('509', '工厂装填', '122', '2', 'filling', 'business/filling/index', '', '', 1, 0, 'C', '0', '0', 'business:filling:list', '#', 'admin', sysdate(), '', null, '工厂装填菜单');
insert into sys_menu values('510', '发货出库', '122', '3', 'shipment', 'business/shipment/index', '', '', 1, 0, 'C', '0', '0', 'business:shipment:list', '#', 'admin', sysdate(), '', null, '发货出库菜单');
insert into sys_menu values('511', '客户现场使用', '122', '4', 'usage', 'business/usage/index', '', '', 1, 0, 'C', '0', '0', 'business:usage:list', '#', 'admin', sysdate(), '', null, '客户现场使用菜单');
insert into sys_menu values('512', '退回仓库', '122', '5', 'return', 'business/return/index', '', '', 1, 0, 'C', '0', '0', 'business:return:list', '#', 'admin', sysdate(), '', null, '退回仓库菜单');
insert into sys_menu values('513', '托盘库存', '123', '1', 'palletInventory', 'report/palletInventory/index', '', '', 1, 0, 'C', '0', '0', 'report:palletinventory:list', '#', 'admin', sysdate(), '', null, '托盘库存菜单');
insert into sys_menu values('514', '装填记录', '123', '2', 'reportFilling', 'report/filling/index', '', '', 1, 0, 'C', '0', '0', 'report:filling:list', '#', 'admin', sysdate(), '', null, '装填记录菜单');
insert into sys_menu values('515', '发货出库', '123', '3', 'reportShipment', 'report/shipment/index', '', '', 1, 0, 'C', '0', '0', 'report:shipment:list', '#', 'admin', sysdate(), '', null, '发货出库报表菜单');
insert into sys_menu values('516', '产线消耗', '123', '4', 'consumption', 'report/consumption/index', '', '', 1, 0, 'C', '0', '0', 'report:consumption:list', '#', 'admin', sysdate(), '', null, '产线消耗菜单');
insert into sys_menu values('517', '退回仓库', '123', '5', 'reportReturn', 'report/return/index', '', '', 1, 0, 'C', '0', '0', 'report:return:list', '#', 'admin', sysdate(), '', null, '退回仓库报表菜单');
insert into sys_menu values('518', '托盘追溯', '123', '6', 'palletTrace', 'report/palletTrace/index', '', '', 1, 0, 'C', '0', '0', 'report:pallettrace:list', '#', 'admin', sysdate(), '', null, '托盘追溯菜单');
-- 托盘业务按钮
insert into sys_menu values('1065', '菌种查询', '502', '1', '', '', '', '', 1, 0, 'F', '0', '0', 'master:strain:query', '#', 'admin', sysdate(), '', null, '');
insert into sys_menu values('1066', '菌种新增', '502', '2', '', '', '', '', 1, 0, 'F', '0', '0', 'master:strain:add', '#', 'admin', sysdate(), '', null, '');
insert into sys_menu values('1067', '菌种修改', '502', '3', '', '', '', '', 1, 0, 'F', '0', '0', 'master:strain:edit', '#', 'admin', sysdate(), '', null, '');
insert into sys_menu values('1068', '菌种删除', '502', '4', '', '', '', '', 1, 0, 'F', '0', '0', 'master:strain:remove', '#', 'admin', sysdate(), '', null, '');
insert into sys_menu values('1069', '客户查询', '503', '1', '', '', '', '', 1, 0, 'F', '0', '0', 'master:customer:query', '#', 'admin', sysdate(), '', null, '');
insert into sys_menu values('1070', '客户新增', '503', '2', '', '', '', '', 1, 0, 'F', '0', '0', 'master:customer:add', '#', 'admin', sysdate(), '', null, '');
insert into sys_menu values('1071', '客户修改', '503', '3', '', '', '', '', 1, 0, 'F', '0', '0', 'master:customer:edit', '#', 'admin', sysdate(), '', null, '');
insert into sys_menu values('1072', '客户删除', '503', '4', '', '', '', '', 1, 0, 'F', '0', '0', 'master:customer:remove', '#', 'admin', sysdate(), '', null, '');
insert into sys_menu values('1073', '现场查询', '504', '1', '', '', '', '', 1, 0, 'F', '0', '0', 'master:site:query', '#', 'admin', sysdate(), '', null, '');
insert into sys_menu values('1074', '现场新增', '504', '2', '', '', '', '', 1, 0, 'F', '0', '0', 'master:site:add', '#', 'admin', sysdate(), '', null, '');
insert into sys_menu values('1075', '现场修改', '504', '3', '', '', '', '', 1, 0, 'F', '0', '0', 'master:site:edit', '#', 'admin', sysdate(), '', null, '');
insert into sys_menu values('1076', '现场删除', '504', '4', '', '', '', '', 1, 0, 'F', '0', '0', 'master:site:remove', '#', 'admin', sysdate(), '', null, '');
insert into sys_menu values('1077', '分配用户', '504', '5', '', '', '', '', 1, 0, 'F', '0', '0', 'master:site:assign', '#', 'admin', sysdate(), '', null, '');
insert into sys_menu values('1078', '产线查询', '505', '1', '', '', '', '', 1, 0, 'F', '0', '0', 'master:line:query', '#', 'admin', sysdate(), '', null, '');
insert into sys_menu values('1079', '产线新增', '505', '2', '', '', '', '', 1, 0, 'F', '0', '0', 'master:line:add', '#', 'admin', sysdate(), '', null, '');
insert into sys_menu values('1080', '产线修改', '505', '3', '', '', '', '', 1, 0, 'F', '0', '0', 'master:line:edit', '#', 'admin', sysdate(), '', null, '');
insert into sys_menu values('1081', '产线删除', '505', '4', '', '', '', '', 1, 0, 'F', '0', '0', 'master:line:remove', '#', 'admin', sysdate(), '', null, '');
insert into sys_menu values('1082', '仓库查询', '506', '1', '', '', '', '', 1, 0, 'F', '0', '0', 'master:warehouse:query', '#', 'admin', sysdate(), '', null, '');
insert into sys_menu values('1083', '仓库新增', '506', '2', '', '', '', '', 1, 0, 'F', '0', '0', 'master:warehouse:add', '#', 'admin', sysdate(), '', null, '');
insert into sys_menu values('1084', '仓库修改', '506', '3', '', '', '', '', 1, 0, 'F', '0', '0', 'master:warehouse:edit', '#', 'admin', sysdate(), '', null, '');
insert into sys_menu values('1085', '仓库删除', '506', '4', '', '', '', '', 1, 0, 'F', '0', '0', 'master:warehouse:remove', '#', 'admin', sysdate(), '', null, '');
insert into sys_menu values('1086', '分配用户', '506', '5', '', '', '', '', 1, 0, 'F', '0', '0', 'master:warehouse:assign', '#', 'admin', sysdate(), '', null, '');
insert into sys_menu values('1087', '托盘查询', '507', '1', '', '', '', '', 1, 0, 'F', '0', '0', 'master:pallet:query', '#', 'admin', sysdate(), '', null, '');
insert into sys_menu values('1088', '托盘新增', '507', '2', '', '', '', '', 1, 0, 'F', '0', '0', 'master:pallet:add', '#', 'admin', sysdate(), '', null, '');
insert into sys_menu values('1089', '托盘修改', '507', '3', '', '', '', '', 1, 0, 'F', '0', '0', 'master:pallet:edit', '#', 'admin', sysdate(), '', null, '');
insert into sys_menu values('1090', '托盘删除', '507', '4', '', '', '', '', 1, 0, 'F', '0', '0', 'master:pallet:remove', '#', 'admin', sysdate(), '', null, '');
insert into sys_menu values('1091', '托盘打印', '507', '5', '', '', '', '', 1, 0, 'F', '0', '0', 'master:pallet:print', '#', 'admin', sysdate(), '', null, '');
insert into sys_menu values('1092', '入库查询', '508', '1', '', '', '', '', 1, 0, 'F', '0', '0', 'business:inbound:query', '#', 'admin', sysdate(), '', null, '');
insert into sys_menu values('1093', '入库新增', '508', '2', '', '', '', '', 1, 0, 'F', '0', '0', 'business:inbound:add', '#', 'admin', sysdate(), '', null, '');
insert into sys_menu values('1094', '入库修改', '508', '3', '', '', '', '', 1, 0, 'F', '0', '0', 'business:inbound:edit', '#', 'admin', sysdate(), '', null, '');
insert into sys_menu values('1095', '入库删除', '508', '4', '', '', '', '', 1, 0, 'F', '0', '0', 'business:inbound:remove', '#', 'admin', sysdate(), '', null, '');
insert into sys_menu values('1096', '入库确认', '508', '5', '', '', '', '', 1, 0, 'F', '0', '0', 'business:inbound:confirm', '#', 'admin', sysdate(), '', null, '');
insert into sys_menu values('1097', '装填查询', '509', '1', '', '', '', '', 1, 0, 'F', '0', '0', 'business:filling:query', '#', 'admin', sysdate(), '', null, '');
insert into sys_menu values('1098', '装填新增', '509', '2', '', '', '', '', 1, 0, 'F', '0', '0', 'business:filling:add', '#', 'admin', sysdate(), '', null, '');
insert into sys_menu values('1099', '发货查询', '510', '1', '', '', '', '', 1, 0, 'F', '0', '0', 'business:shipment:query', '#', 'admin', sysdate(), '', null, '');
insert into sys_menu values('1100', '发货新增', '510', '2', '', '', '', '', 1, 0, 'F', '0', '0', 'business:shipment:add', '#', 'admin', sysdate(), '', null, '');
insert into sys_menu values('1101', '发货修改', '510', '3', '', '', '', '', 1, 0, 'F', '0', '0', 'business:shipment:edit', '#', 'admin', sysdate(), '', null, '');
insert into sys_menu values('1102', '发货删除', '510', '4', '', '', '', '', 1, 0, 'F', '0', '0', 'business:shipment:remove', '#', 'admin', sysdate(), '', null, '');
insert into sys_menu values('1103', '发货确认', '510', '5', '', '', '', '', 1, 0, 'F', '0', '0', 'business:shipment:confirm', '#', 'admin', sysdate(), '', null, '');
insert into sys_menu values('1104', '使用查询', '511', '1', '', '', '', '', 1, 0, 'F', '0', '0', 'business:usage:query', '#', 'admin', sysdate(), '', null, '');
insert into sys_menu values('1105', '使用新增', '511', '2', '', '', '', '', 1, 0, 'F', '0', '0', 'business:usage:add', '#', 'admin', sysdate(), '', null, '');
insert into sys_menu values('1106', '退回查询', '512', '1', '', '', '', '', 1, 0, 'F', '0', '0', 'business:return:query', '#', 'admin', sysdate(), '', null, '');
insert into sys_menu values('1107', '退回申请', '512', '2', '', '', '', '', 1, 0, 'F', '0', '0', 'business:return:request', '#', 'admin', sysdate(), '', null, '');
insert into sys_menu values('1108', '退回确认', '512', '3', '', '', '', '', 1, 0, 'F', '0', '0', 'business:return:confirm', '#', 'admin', sysdate(), '', null, '');
-- ----------------------------
-- 6、用户和角色关联表 用户N-1角色
@@ -714,3 +806,401 @@ create table gen_table_column (
update_time datetime comment '更新时间',
primary key (column_id)
) engine=innodb auto_increment=1 comment = '代码生成业务表字段';
-- ----------------------------
-- 20、托盘业务表(按依赖创建)
-- ----------------------------
-- 菌种主数据表
create table biz_strain (
strain_id bigint(20) not null auto_increment comment '菌种ID',
strain_code varchar(64) not null comment '菌种编码',
strain_name varchar(100) not null comment '菌种名称',
status char(1) not null default '0' comment '状态(0正常 1停用)',
create_by varchar(64) default '' comment '创建者',
create_time datetime comment '创建时间',
update_by varchar(64) default '' comment '更新者',
update_time datetime comment '更新时间',
remark varchar(500) default null comment '备注',
primary key (strain_id),
constraint uq_biz_strain_code unique (strain_code),
constraint ck_biz_strain_status check (status in ('0', '1'))
) engine=innodb comment = '菌种主数据表';
-- 仓库主数据表
create table biz_warehouse (
warehouse_id bigint(20) not null auto_increment comment '仓库ID',
warehouse_code varchar(64) not null comment '仓库编码',
warehouse_name varchar(100) not null comment '仓库名称',
address varchar(500) comment '仓库地址',
status char(1) not null default '0' comment '状态(0正常 1停用)',
create_by varchar(64) default '' comment '创建者',
create_time datetime comment '创建时间',
update_by varchar(64) default '' comment '更新者',
update_time datetime comment '更新时间',
remark varchar(500) default null comment '备注',
primary key (warehouse_id),
constraint uq_biz_warehouse_code unique (warehouse_code),
constraint ck_biz_warehouse_status check (status in ('0', '1'))
) engine=innodb comment = '仓库主数据表';
-- 客户主数据表
create table biz_customer (
customer_id bigint(20) not null auto_increment comment '客户ID',
customer_code varchar(64) not null comment '客户编码',
customer_name varchar(100) not null comment '客户名称',
status char(1) not null default '0' comment '状态(0正常 1停用)',
create_by varchar(64) default '' comment '创建者',
create_time datetime comment '创建时间',
update_by varchar(64) default '' comment '更新者',
update_time datetime comment '更新时间',
remark varchar(500) default null comment '备注',
primary key (customer_id),
constraint uq_biz_customer_code unique (customer_code),
constraint ck_biz_customer_status check (status in ('0', '1'))
) engine=innodb comment = '客户主数据表';
-- 客户现场主数据表
create table biz_customer_site (
site_id bigint(20) not null auto_increment comment '现场ID',
customer_id bigint(20) not null comment '客户ID',
site_code varchar(64) not null comment '现场编码',
site_name varchar(100) not null comment '现场名称',
address varchar(500) comment '现场地址',
status char(1) not null default '0' comment '状态(0正常 1停用)',
create_by varchar(64) default '' comment '创建者',
create_time datetime comment '创建时间',
update_by varchar(64) default '' comment '更新者',
update_time datetime comment '更新时间',
remark varchar(500) default null comment '备注',
primary key (site_id),
constraint uq_biz_customer_site_customer_code unique (customer_id, site_code),
constraint fk_biz_customer_site_customer foreign key (customer_id) references biz_customer (customer_id),
constraint ck_biz_customer_site_status check (status in ('0', '1'))
) engine=innodb comment = '客户现场主数据表';
-- 客户现场产线主数据表
create table biz_production_line (
line_id bigint(20) not null auto_increment comment '产线ID',
site_id bigint(20) not null comment '现场ID',
line_code varchar(64) not null comment '产线编码',
line_name varchar(100) not null comment '产线名称',
status char(1) not null default '0' comment '状态(0正常 1停用)',
create_by varchar(64) default '' comment '创建者',
create_time datetime comment '创建时间',
update_by varchar(64) default '' comment '更新者',
update_time datetime comment '更新时间',
remark varchar(500) default null comment '备注',
primary key (line_id),
constraint uq_biz_production_line_site_code unique (site_id, line_code),
constraint fk_biz_production_line_site foreign key (site_id) references biz_customer_site (site_id),
constraint ck_biz_production_line_status check (status in ('0', '1'))
) engine=innodb comment = '客户现场产线主数据表';
-- 托盘资产主数据表
create table biz_pallet (
pallet_id bigint(20) not null auto_increment comment '托盘ID',
pallet_code varchar(64) not null comment '托盘编号',
qr_token varchar(128) not null comment '二维码固定标识',
capacity_packages int not null comment '容量包数',
asset_status char(1) not null default '0' comment '资产状态(0正常 1停用)',
create_by varchar(64) default '' comment '创建者',
create_time datetime comment '创建时间',
update_by varchar(64) default '' comment '更新者',
update_time datetime comment '更新时间',
remark varchar(500) default null comment '备注',
primary key (pallet_id),
constraint uq_biz_pallet_code unique (pallet_code),
constraint uq_biz_pallet_qr_token unique (qr_token),
constraint ck_biz_pallet_capacity_packages check (capacity_packages > 0),
constraint ck_biz_pallet_asset_status check (asset_status in ('0', '1'))
) engine=innodb comment = '托盘资产主数据表';
-- 用户与仓库关联表
create table biz_user_warehouse (
user_id bigint(20) not null comment '用户ID',
warehouse_id bigint(20) not null comment '仓库ID',
create_by varchar(64) default '' comment '创建者',
create_time datetime comment '创建时间',
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)
) engine=innodb comment = '用户与仓库关联表';
-- 用户与客户现场关联表
create table biz_user_site (
user_id bigint(20) not null comment '用户ID',
site_id bigint(20) not null comment '现场ID',
create_by varchar(64) default '' comment '创建者',
create_time datetime comment '创建时间',
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)
) engine=innodb comment = '用户与客户现场关联表';
-- 首次入库单
create table biz_inbound_order (
inbound_id bigint(20) not null auto_increment comment '入库单ID',
inbound_no varchar(64) not null comment '入库单号',
warehouse_id bigint(20) not null comment '仓库ID',
status varchar(32) not null default 'DRAFT' comment '单据状态',
confirm_idempotency_key varchar(64) comment '确认幂等键',
confirm_request_hash varchar(64) comment '确认请求摘要',
confirmed_by bigint(20) comment '确认人ID',
confirmed_at datetime comment '确认时间',
create_by varchar(64) default '' comment '创建者',
create_time datetime comment '创建时间',
update_by varchar(64) default '' comment '更新者',
update_time datetime comment '更新时间',
remark varchar(500) default null comment '备注',
primary key (inbound_id),
constraint uq_biz_inbound_order_no unique (inbound_no),
constraint uq_biz_inbound_order_confirm_idempotency unique (confirm_idempotency_key),
constraint fk_biz_inbound_order_warehouse foreign key (warehouse_id) references biz_warehouse (warehouse_id),
constraint fk_biz_inbound_order_confirmed_by foreign key (confirmed_by) references sys_user (user_id),
constraint ck_biz_inbound_order_status check (status in ('DRAFT', 'CONFIRMED'))
) engine=innodb comment = '首次入库单';
-- 首次入库单托盘明细
create table biz_inbound_item (
inbound_item_id bigint(20) not null auto_increment comment '入库明细ID',
inbound_id bigint(20) not null comment '入库单ID',
pallet_id bigint(20) not null comment '托盘ID',
create_by varchar(64) default '' comment '创建者',
create_time datetime comment '创建时间',
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),
constraint fk_biz_inbound_item_pallet foreign key (pallet_id) references biz_pallet (pallet_id)
) engine=innodb comment = '首次入库单托盘明细';
-- 托盘装填记录
create table biz_filling_record (
filling_id bigint(20) not null auto_increment comment '装填记录ID',
filling_no varchar(64) not null comment '装填单号',
pallet_id bigint(20) not null comment '托盘ID',
warehouse_id bigint(20) not null comment '仓库ID',
strain_id bigint(20) not null comment '菌种ID',
strain_code_snapshot varchar(64) not null comment '菌种编码快照',
strain_name_snapshot varchar(100) not null comment '菌种名称快照',
batch_no varchar(64) not null comment '生产批次',
package_spec varchar(64) not null comment '包装规格',
initial_packages int not null comment '初始包数',
active_slot varchar(16) comment '活动装填槽',
closed_at datetime comment '装填周期关闭时间',
idempotency_key varchar(64) not null comment '幂等键',
request_hash varchar(64) not null comment '请求摘要',
operator_id bigint(20) not null comment '操作人ID',
operated_at datetime not null comment '操作时间',
create_by varchar(64) default '' comment '创建者',
create_time datetime comment '创建时间',
update_by varchar(64) default '' comment '更新者',
update_time datetime comment '更新时间',
remark varchar(500) default null comment '备注',
primary key (filling_id),
constraint uq_biz_filling_record_no unique (filling_no),
constraint uq_biz_filling_record_idempotency unique (idempotency_key),
constraint uq_biz_filling_record_pallet_active_slot unique (pallet_id, active_slot),
constraint fk_biz_filling_record_pallet foreign key (pallet_id) references biz_pallet (pallet_id),
constraint fk_biz_filling_record_warehouse foreign key (warehouse_id) references biz_warehouse (warehouse_id),
constraint fk_biz_filling_record_strain foreign key (strain_id) references biz_strain (strain_id),
constraint fk_biz_filling_record_operator foreign key (operator_id) references sys_user (user_id),
constraint ck_biz_filling_record_initial_packages check (initial_packages > 0),
constraint ck_biz_filling_record_active_slot check (active_slot is null or active_slot = 'ACTIVE')
) engine=innodb comment = '托盘装填记录';
-- 整托发货单
create table biz_shipment_order (
shipment_id bigint(20) not null auto_increment comment '发货单ID',
shipment_no varchar(64) not null comment '发货单号',
source_warehouse_id bigint(20) not null comment '来源仓库ID',
target_site_id bigint(20) not null comment '目标现场ID',
status varchar(32) not null default 'DRAFT' comment '单据状态',
confirm_idempotency_key varchar(64) comment '确认幂等键',
confirm_request_hash varchar(64) comment '确认请求摘要',
confirmed_by bigint(20) comment '确认人ID',
confirmed_at datetime comment '确认时间',
create_by varchar(64) default '' comment '创建者',
create_time datetime comment '创建时间',
update_by varchar(64) default '' comment '更新者',
update_time datetime comment '更新时间',
remark varchar(500) default null comment '备注',
primary key (shipment_id),
constraint uq_biz_shipment_order_no unique (shipment_no),
constraint uq_biz_shipment_order_confirm_idempotency unique (confirm_idempotency_key),
constraint fk_biz_shipment_order_warehouse foreign key (source_warehouse_id) references biz_warehouse (warehouse_id),
constraint fk_biz_shipment_order_site foreign key (target_site_id) references biz_customer_site (site_id),
constraint fk_biz_shipment_order_confirmed_by foreign key (confirmed_by) references sys_user (user_id),
constraint ck_biz_shipment_order_status check (status in ('DRAFT', 'CONFIRMED'))
) engine=innodb comment = '整托发货单';
-- 整托发货明细
create table biz_shipment_item (
shipment_item_id bigint(20) not null auto_increment comment '发货明细ID',
shipment_id bigint(20) not null comment '发货单ID',
pallet_id bigint(20) not null comment '托盘ID',
active_filling_id bigint(20) comment '确认时活动装填记录ID',
package_qty int comment '确认时包数快照',
create_by varchar(64) default '' comment '创建者',
create_time datetime comment '创建时间',
primary key (shipment_item_id),
constraint uq_biz_shipment_item_order_pallet unique (shipment_id, pallet_id),
constraint fk_biz_shipment_item_order foreign key (shipment_id) references biz_shipment_order (shipment_id),
constraint fk_biz_shipment_item_pallet foreign key (pallet_id) references biz_pallet (pallet_id),
constraint fk_biz_shipment_item_filling foreign key (active_filling_id) references biz_filling_record (filling_id),
constraint ck_biz_shipment_item_package_qty check (package_qty is null or package_qty > 0)
) engine=innodb comment = '整托发货明细';
-- 客户现场使用事实记录
create table biz_usage_record (
usage_id bigint(20) not null auto_increment comment '使用记录ID',
usage_no varchar(64) not null comment '使用记录号',
pallet_id bigint(20) not null comment '托盘ID',
active_filling_id bigint(20) not null comment '活动装填记录ID',
site_id bigint(20) not null comment '现场ID',
line_id bigint(20) not null comment '产线ID',
used_packages int not null comment '本次使用包数',
remaining_packages_after int not null comment '使用后剩余包数',
idempotency_key varchar(64) not null comment '幂等键',
request_hash varchar(64) not null comment '请求摘要',
operator_id bigint(20) not null comment '操作人ID',
occurred_at datetime not null comment '发生时间',
primary key (usage_id),
constraint uq_biz_usage_record_no unique (usage_no),
constraint uq_biz_usage_record_idempotency unique (idempotency_key),
constraint fk_biz_usage_record_pallet foreign key (pallet_id) references biz_pallet (pallet_id),
constraint fk_biz_usage_record_filling foreign key (active_filling_id) references biz_filling_record (filling_id),
constraint fk_biz_usage_record_site foreign key (site_id) references biz_customer_site (site_id),
constraint fk_biz_usage_record_line foreign key (line_id) references biz_production_line (line_id),
constraint fk_biz_usage_record_operator foreign key (operator_id) references sys_user (user_id),
constraint ck_biz_usage_record_used_packages check (used_packages > 0),
constraint ck_biz_usage_record_remaining_packages_after check (remaining_packages_after >= 0)
) engine=innodb comment = '客户现场使用事实记录';
-- 托盘退回单
create table biz_return_order (
return_id bigint(20) not null auto_increment comment '退回单ID',
return_no varchar(64) not null comment '退回单号',
source_site_id bigint(20) not null comment '来源现场ID',
target_warehouse_id bigint(20) not null comment '目标仓库ID',
status varchar(32) not null default 'REQUESTED' comment '退回状态',
request_idempotency_key varchar(64) not null comment '申请幂等键',
request_hash varchar(64) not null comment '申请请求摘要',
confirm_idempotency_key varchar(64) comment '确认幂等键',
confirm_request_hash varchar(64) comment '确认请求摘要',
requested_by bigint(20) not null comment '申请人ID',
requested_at datetime not null comment '申请时间',
confirmed_by bigint(20) comment '确认人ID',
confirmed_at datetime comment '确认时间',
create_by varchar(64) default '' comment '创建者',
create_time datetime comment '创建时间',
update_by varchar(64) default '' comment '更新者',
update_time datetime comment '更新时间',
remark varchar(500) default null comment '备注',
primary key (return_id),
constraint uq_biz_return_order_no unique (return_no),
constraint uq_biz_return_order_request_idempotency unique (request_idempotency_key),
constraint uq_biz_return_order_confirm_idempotency unique (confirm_idempotency_key),
constraint fk_biz_return_order_site foreign key (source_site_id) references biz_customer_site (site_id),
constraint fk_biz_return_order_warehouse foreign key (target_warehouse_id) references biz_warehouse (warehouse_id),
constraint fk_biz_return_order_requested_by foreign key (requested_by) references sys_user (user_id),
constraint fk_biz_return_order_confirmed_by foreign key (confirmed_by) references sys_user (user_id),
constraint ck_biz_return_order_status check (status in ('REQUESTED', 'CONFIRMED'))
) engine=innodb comment = '托盘退回单';
-- 托盘退回明细
create table biz_return_item (
return_item_id bigint(20) not null auto_increment comment '退回明细ID',
return_id bigint(20) not null comment '退回单ID',
pallet_id bigint(20) not null comment '托盘ID',
active_filling_id_snapshot bigint(20) comment '申请时活动装填记录快照',
remaining_packages_snapshot int not null comment '申请时剩余包数快照',
create_by varchar(64) default '' comment '创建者',
create_time datetime comment '创建时间',
primary key (return_item_id),
constraint uq_biz_return_item_order_pallet unique (return_id, pallet_id),
constraint fk_biz_return_item_order foreign key (return_id) references biz_return_order (return_id),
constraint fk_biz_return_item_pallet foreign key (pallet_id) references biz_pallet (pallet_id),
constraint fk_biz_return_item_filling foreign key (active_filling_id_snapshot) references biz_filling_record (filling_id),
constraint ck_biz_return_item_remaining_packages_snapshot check (remaining_packages_snapshot >= 0)
) engine=innodb comment = '托盘退回明细';
-- 托盘只追加流转事件;必须先于库存快照创建
create table biz_pallet_event (
event_id bigint(20) not null auto_increment comment '事件ID',
pallet_id bigint(20) not null comment '托盘ID',
event_seq int not null comment '托盘内事件序号',
event_type varchar(32) not null comment '事件类型',
source_type varchar(32) not null comment '来源类型',
source_id bigint(20) not null comment '来源记录ID',
before_state varchar(32) not null comment '变更前状态',
after_state varchar(32) not null comment '变更后状态',
before_warehouse_id bigint(20) comment '变更前仓库ID',
before_site_id bigint(20) comment '变更前现场ID',
before_line_id bigint(20) comment '变更前产线ID',
after_warehouse_id bigint(20) comment '变更后仓库ID',
after_site_id bigint(20) comment '变更后现场ID',
after_line_id bigint(20) comment '变更后产线ID',
before_remaining_packages int not null comment '变更前剩余包数',
after_remaining_packages int not null comment '变更后剩余包数',
operator_id bigint(20) not null comment '操作人ID',
occurred_at datetime not null comment '发生时间',
idempotency_key varchar(64) not null comment '幂等键',
request_hash varchar(64) not null comment '请求摘要',
primary key (event_id),
constraint uq_biz_pallet_event_pallet_seq unique (pallet_id, event_seq),
constraint uq_biz_pallet_event_pallet_idempotency unique (pallet_id, idempotency_key),
constraint fk_biz_pallet_event_pallet foreign key (pallet_id) references biz_pallet (pallet_id),
constraint fk_biz_pallet_event_before_warehouse foreign key (before_warehouse_id) references biz_warehouse (warehouse_id),
constraint fk_biz_pallet_event_before_site foreign key (before_site_id) references biz_customer_site (site_id),
constraint fk_biz_pallet_event_before_line foreign key (before_line_id) references biz_production_line (line_id),
constraint fk_biz_pallet_event_after_warehouse foreign key (after_warehouse_id) references biz_warehouse (warehouse_id),
constraint fk_biz_pallet_event_after_site foreign key (after_site_id) references biz_customer_site (site_id),
constraint fk_biz_pallet_event_after_line foreign key (after_line_id) references biz_production_line (line_id),
constraint fk_biz_pallet_event_operator foreign key (operator_id) references sys_user (user_id),
constraint ck_biz_pallet_event_event_type check (
event_type in (
'INBOUND_CONFIRMED',
'FILLING_CONFIRMED',
'SHIPMENT_CONFIRMED',
'USAGE_RECORDED',
'RETURN_REQUESTED',
'RETURN_CONFIRMED'
)
)
) engine=innodb comment = '托盘只追加流转事件';
-- 托盘当前库存、位置和流转状态
create table biz_pallet_inventory (
pallet_id bigint(20) not null comment '托盘ID',
active_filling_id bigint(20) comment '当前活动装填记录ID',
warehouse_id bigint(20) comment '当前仓库ID',
site_id bigint(20) comment '当前现场ID',
current_line_id bigint(20) comment '当前驻场周期绑定产线ID',
flow_state varchar(32) not null default 'UNSTOCKED' comment '托盘流转状态',
remaining_packages int not null default 0 comment '当前剩余包数',
last_event_id bigint(20) comment '最后事件ID',
version int not null default 0 comment '并发版本号',
updated_at datetime not null comment '更新时间',
primary key (pallet_id),
constraint fk_biz_pallet_inventory_pallet foreign key (pallet_id) references biz_pallet (pallet_id),
constraint fk_biz_pallet_inventory_filling foreign key (active_filling_id) references biz_filling_record (filling_id),
constraint fk_biz_pallet_inventory_warehouse foreign key (warehouse_id) references biz_warehouse (warehouse_id),
constraint fk_biz_pallet_inventory_site foreign key (site_id) references biz_customer_site (site_id),
constraint fk_biz_pallet_inventory_line foreign key (current_line_id) references biz_production_line (line_id),
constraint fk_biz_pallet_inventory_event foreign key (last_event_id) references biz_pallet_event (event_id),
constraint ck_biz_pallet_inventory_flow_state check (
flow_state in (
'UNSTOCKED',
'EMPTY_IN_WAREHOUSE',
'FILLED_IN_WAREHOUSE',
'AT_SITE_UNUSED',
'AT_SITE_PARTIAL',
'AT_SITE_EMPTY',
'RETURNING'
)
),
constraint ck_biz_pallet_inventory_remaining_packages check (remaining_packages >= 0),
constraint ck_biz_pallet_inventory_version check (version >= 0),
constraint ck_biz_pallet_inventory_location check (not (warehouse_id is not null and site_id is not null))
) engine=innodb comment = '托盘当前库存、位置和流转状态';
@@ -0,0 +1,841 @@
import re
import unittest
from pathlib import Path
SQL_ROOT = Path(__file__).resolve().parents[2] / 'sql'
INITIALIZERS = ('shierfeng-fastapi.sql', 'shierfeng-fastapi-pg.sql')
PATCH_PATH = SQL_ROOT / 'patches/20260724_pallet_workflow_mysql.sql'
SYS_MENU_VALUE_COUNT = 20
REMOVED_AI_DICT_CODES = set(range(33, 70))
EXPECTED_TABLES = {
'biz_strain',
'biz_warehouse',
'biz_customer',
'biz_customer_site',
'biz_production_line',
'biz_pallet',
'biz_user_warehouse',
'biz_user_site',
'biz_inbound_order',
'biz_inbound_item',
'biz_filling_record',
'biz_shipment_order',
'biz_shipment_item',
'biz_usage_record',
'biz_return_order',
'biz_return_item',
'biz_pallet_inventory',
'biz_pallet_event',
}
AUDIT_COLUMNS = {'create_by', 'create_time', 'update_by', 'update_time', 'remark'}
CREATE_AUDIT_COLUMNS = {'create_by', 'create_time'}
EXPECTED_COLUMNS = {
'biz_strain': {'strain_id', 'strain_code', 'strain_name', 'status'} | AUDIT_COLUMNS,
'biz_warehouse': {'warehouse_id', 'warehouse_code', 'warehouse_name', 'address', 'status'} | AUDIT_COLUMNS,
'biz_customer': {'customer_id', 'customer_code', 'customer_name', 'status'} | AUDIT_COLUMNS,
'biz_customer_site': {'site_id', 'customer_id', 'site_code', 'site_name', 'address', 'status'} | AUDIT_COLUMNS,
'biz_production_line': {'line_id', 'site_id', 'line_code', 'line_name', 'status'} | AUDIT_COLUMNS,
'biz_pallet': {'pallet_id', 'pallet_code', 'qr_token', 'capacity_packages', 'asset_status'} | AUDIT_COLUMNS,
'biz_user_warehouse': {'user_id', 'warehouse_id'} | CREATE_AUDIT_COLUMNS,
'biz_user_site': {'user_id', 'site_id'} | CREATE_AUDIT_COLUMNS,
'biz_inbound_order': {
'inbound_id',
'inbound_no',
'warehouse_id',
'status',
'confirm_idempotency_key',
'confirm_request_hash',
'confirmed_by',
'confirmed_at',
}
| AUDIT_COLUMNS,
'biz_inbound_item': {'inbound_item_id', 'inbound_id', 'pallet_id'} | CREATE_AUDIT_COLUMNS,
'biz_filling_record': {
'filling_id',
'filling_no',
'pallet_id',
'warehouse_id',
'strain_id',
'strain_code_snapshot',
'strain_name_snapshot',
'batch_no',
'package_spec',
'initial_packages',
'active_slot',
'closed_at',
'idempotency_key',
'request_hash',
'operator_id',
'operated_at',
}
| AUDIT_COLUMNS,
'biz_shipment_order': {
'shipment_id',
'shipment_no',
'source_warehouse_id',
'target_site_id',
'status',
'confirm_idempotency_key',
'confirm_request_hash',
'confirmed_by',
'confirmed_at',
}
| AUDIT_COLUMNS,
'biz_shipment_item': {
'shipment_item_id',
'shipment_id',
'pallet_id',
'active_filling_id',
'package_qty',
}
| CREATE_AUDIT_COLUMNS,
'biz_usage_record': {
'usage_id',
'usage_no',
'pallet_id',
'active_filling_id',
'site_id',
'line_id',
'used_packages',
'remaining_packages_after',
'idempotency_key',
'request_hash',
'operator_id',
'occurred_at',
},
'biz_return_order': {
'return_id',
'return_no',
'source_site_id',
'target_warehouse_id',
'status',
'request_idempotency_key',
'request_hash',
'confirm_idempotency_key',
'confirm_request_hash',
'requested_by',
'requested_at',
'confirmed_by',
'confirmed_at',
}
| AUDIT_COLUMNS,
'biz_return_item': {
'return_item_id',
'return_id',
'pallet_id',
'active_filling_id_snapshot',
'remaining_packages_snapshot',
}
| CREATE_AUDIT_COLUMNS,
'biz_pallet_inventory': {
'pallet_id',
'active_filling_id',
'warehouse_id',
'site_id',
'current_line_id',
'flow_state',
'remaining_packages',
'last_event_id',
'version',
'updated_at',
},
'biz_pallet_event': {
'event_id',
'pallet_id',
'event_seq',
'event_type',
'source_type',
'source_id',
'before_state',
'after_state',
'before_warehouse_id',
'before_site_id',
'before_line_id',
'after_warehouse_id',
'after_site_id',
'after_line_id',
'before_remaining_packages',
'after_remaining_packages',
'operator_id',
'occurred_at',
'idempotency_key',
'request_hash',
},
}
EXPECTED_PRIMARY_KEYS = {
'biz_strain': ('strain_id',),
'biz_warehouse': ('warehouse_id',),
'biz_customer': ('customer_id',),
'biz_customer_site': ('site_id',),
'biz_production_line': ('line_id',),
'biz_pallet': ('pallet_id',),
'biz_user_warehouse': ('user_id', 'warehouse_id'),
'biz_user_site': ('user_id', 'site_id'),
'biz_inbound_order': ('inbound_id',),
'biz_inbound_item': ('inbound_item_id',),
'biz_filling_record': ('filling_id',),
'biz_shipment_order': ('shipment_id',),
'biz_shipment_item': ('shipment_item_id',),
'biz_usage_record': ('usage_id',),
'biz_return_order': ('return_id',),
'biz_return_item': ('return_item_id',),
'biz_pallet_inventory': ('pallet_id',),
'biz_pallet_event': ('event_id',),
}
EXPECTED_UNIQUE_KEYS = {
'biz_strain': {('strain_code',)},
'biz_warehouse': {('warehouse_code',)},
'biz_customer': {('customer_code',)},
'biz_customer_site': {('customer_id', 'site_code')},
'biz_production_line': {('site_id', 'line_code')},
'biz_pallet': {('pallet_code',), ('qr_token',)},
'biz_user_warehouse': set(),
'biz_user_site': set(),
'biz_inbound_order': {('inbound_no',), ('confirm_idempotency_key',)},
'biz_inbound_item': {('inbound_id', 'pallet_id')},
'biz_filling_record': {('filling_no',), ('idempotency_key',), ('pallet_id', 'active_slot')},
'biz_shipment_order': {('shipment_no',), ('confirm_idempotency_key',)},
'biz_shipment_item': {('shipment_id', 'pallet_id')},
'biz_usage_record': {('usage_no',), ('idempotency_key',)},
'biz_return_order': {('return_no',), ('request_idempotency_key',), ('confirm_idempotency_key',)},
'biz_return_item': {('return_id', 'pallet_id')},
'biz_pallet_inventory': set(),
'biz_pallet_event': {('pallet_id', 'event_seq'), ('pallet_id', 'idempotency_key')},
}
EXPECTED_FOREIGN_KEYS = {
'biz_strain': set(),
'biz_warehouse': set(),
'biz_customer': set(),
'biz_customer_site': {('customer_id', 'biz_customer.customer_id')},
'biz_production_line': {('site_id', 'biz_customer_site.site_id')},
'biz_pallet': set(),
'biz_user_warehouse': {('user_id', 'sys_user.user_id'), ('warehouse_id', 'biz_warehouse.warehouse_id')},
'biz_user_site': {('user_id', 'sys_user.user_id'), ('site_id', 'biz_customer_site.site_id')},
'biz_inbound_order': {
('warehouse_id', 'biz_warehouse.warehouse_id'),
('confirmed_by', 'sys_user.user_id'),
},
'biz_inbound_item': {
('inbound_id', 'biz_inbound_order.inbound_id'),
('pallet_id', 'biz_pallet.pallet_id'),
},
'biz_filling_record': {
('pallet_id', 'biz_pallet.pallet_id'),
('warehouse_id', 'biz_warehouse.warehouse_id'),
('strain_id', 'biz_strain.strain_id'),
('operator_id', 'sys_user.user_id'),
},
'biz_shipment_order': {
('source_warehouse_id', 'biz_warehouse.warehouse_id'),
('target_site_id', 'biz_customer_site.site_id'),
('confirmed_by', 'sys_user.user_id'),
},
'biz_shipment_item': {
('shipment_id', 'biz_shipment_order.shipment_id'),
('pallet_id', 'biz_pallet.pallet_id'),
('active_filling_id', 'biz_filling_record.filling_id'),
},
'biz_usage_record': {
('pallet_id', 'biz_pallet.pallet_id'),
('active_filling_id', 'biz_filling_record.filling_id'),
('site_id', 'biz_customer_site.site_id'),
('line_id', 'biz_production_line.line_id'),
('operator_id', 'sys_user.user_id'),
},
'biz_return_order': {
('source_site_id', 'biz_customer_site.site_id'),
('target_warehouse_id', 'biz_warehouse.warehouse_id'),
('requested_by', 'sys_user.user_id'),
('confirmed_by', 'sys_user.user_id'),
},
'biz_return_item': {
('return_id', 'biz_return_order.return_id'),
('pallet_id', 'biz_pallet.pallet_id'),
('active_filling_id_snapshot', 'biz_filling_record.filling_id'),
},
'biz_pallet_inventory': {
('pallet_id', 'biz_pallet.pallet_id'),
('active_filling_id', 'biz_filling_record.filling_id'),
('warehouse_id', 'biz_warehouse.warehouse_id'),
('site_id', 'biz_customer_site.site_id'),
('current_line_id', 'biz_production_line.line_id'),
('last_event_id', 'biz_pallet_event.event_id'),
},
'biz_pallet_event': {
('pallet_id', 'biz_pallet.pallet_id'),
('before_warehouse_id', 'biz_warehouse.warehouse_id'),
('before_site_id', 'biz_customer_site.site_id'),
('before_line_id', 'biz_production_line.line_id'),
('after_warehouse_id', 'biz_warehouse.warehouse_id'),
('after_site_id', 'biz_customer_site.site_id'),
('after_line_id', 'biz_production_line.line_id'),
('operator_id', 'sys_user.user_id'),
},
}
EXPECTED_CHECKS = {
'biz_strain': {"statusin('0','1')"},
'biz_warehouse': {"statusin('0','1')"},
'biz_customer': {"statusin('0','1')"},
'biz_customer_site': {"statusin('0','1')"},
'biz_production_line': {"statusin('0','1')"},
'biz_pallet': {'capacity_packages>0', "asset_statusin('0','1')"},
'biz_user_warehouse': set(),
'biz_user_site': set(),
'biz_inbound_order': {"statusin('draft','confirmed')"},
'biz_inbound_item': set(),
'biz_filling_record': {'initial_packages>0', "active_slotisnulloractive_slot='active'"},
'biz_shipment_order': {"statusin('draft','confirmed')"},
'biz_shipment_item': {'package_qtyisnullorpackage_qty>0'},
'biz_usage_record': {'used_packages>0', 'remaining_packages_after>=0'},
'biz_return_order': {"statusin('requested','confirmed')"},
'biz_return_item': {'remaining_packages_snapshot>=0'},
'biz_pallet_inventory': {
"flow_statein('unstocked','empty_in_warehouse','filled_in_warehouse','at_site_unused','at_site_partial',"
"'at_site_empty','returning')",
'remaining_packages>=0',
'version>=0',
'not(warehouse_idisnotnullandsite_idisnotnull)',
},
'biz_pallet_event': {
"event_typein('inbound_confirmed','filling_confirmed','shipment_confirmed','usage_recorded',"
"'return_requested','return_confirmed')"
},
}
EXPECTED_PAGES = {
502: ('菌种资料', 121, 1, 'strain', 'master/strain/index', 'master:strain:list'),
503: ('客户资料', 121, 2, 'customer', 'master/customer/index', 'master:customer:list'),
504: ('客户现场', 121, 3, 'site', 'master/site/index', 'master:site:list'),
505: ('产线资料', 121, 4, 'line', 'master/line/index', 'master:line:list'),
506: ('仓库资料', 121, 5, 'warehouse', 'master/warehouse/index', 'master:warehouse:list'),
507: ('托盘资料', 121, 6, 'pallet', 'master/pallet/index', 'master:pallet:list'),
508: ('仓库入库', 122, 1, 'inbound', 'business/inbound/index', 'business:inbound:list'),
509: ('工厂装填', 122, 2, 'filling', 'business/filling/index', 'business:filling:list'),
510: ('发货出库', 122, 3, 'shipment', 'business/shipment/index', 'business:shipment:list'),
511: ('客户现场使用', 122, 4, 'usage', 'business/usage/index', 'business:usage:list'),
512: ('退回仓库', 122, 5, 'return', 'business/return/index', 'business:return:list'),
513: (
'托盘库存',
123,
1,
'palletInventory',
'report/palletInventory/index',
'report:palletinventory:list',
),
514: ('装填记录', 123, 2, 'reportFilling', 'report/filling/index', 'report:filling:list'),
515: ('发货出库', 123, 3, 'reportShipment', 'report/shipment/index', 'report:shipment:list'),
516: ('产线消耗', 123, 4, 'consumption', 'report/consumption/index', 'report:consumption:list'),
517: ('退回仓库', 123, 5, 'reportReturn', 'report/return/index', 'report:return:list'),
518: ('托盘追溯', 123, 6, 'palletTrace', 'report/palletTrace/index', 'report:pallettrace:list'),
}
BUTTON_SPECS = """
1065 502 1 菌种查询 master:strain:query
1066 502 2 菌种新增 master:strain:add
1067 502 3 菌种修改 master:strain:edit
1068 502 4 菌种删除 master:strain:remove
1069 503 1 客户查询 master:customer:query
1070 503 2 客户新增 master:customer:add
1071 503 3 客户修改 master:customer:edit
1072 503 4 客户删除 master:customer:remove
1073 504 1 现场查询 master:site:query
1074 504 2 现场新增 master:site:add
1075 504 3 现场修改 master:site:edit
1076 504 4 现场删除 master:site:remove
1077 504 5 分配用户 master:site:assign
1078 505 1 产线查询 master:line:query
1079 505 2 产线新增 master:line:add
1080 505 3 产线修改 master:line:edit
1081 505 4 产线删除 master:line:remove
1082 506 1 仓库查询 master:warehouse:query
1083 506 2 仓库新增 master:warehouse:add
1084 506 3 仓库修改 master:warehouse:edit
1085 506 4 仓库删除 master:warehouse:remove
1086 506 5 分配用户 master:warehouse:assign
1087 507 1 托盘查询 master:pallet:query
1088 507 2 托盘新增 master:pallet:add
1089 507 3 托盘修改 master:pallet:edit
1090 507 4 托盘删除 master:pallet:remove
1091 507 5 托盘打印 master:pallet:print
1092 508 1 入库查询 business:inbound:query
1093 508 2 入库新增 business:inbound:add
1094 508 3 入库修改 business:inbound:edit
1095 508 4 入库删除 business:inbound:remove
1096 508 5 入库确认 business:inbound:confirm
1097 509 1 装填查询 business:filling:query
1098 509 2 装填新增 business:filling:add
1099 510 1 发货查询 business:shipment:query
1100 510 2 发货新增 business:shipment:add
1101 510 3 发货修改 business:shipment:edit
1102 510 4 发货删除 business:shipment:remove
1103 510 5 发货确认 business:shipment:confirm
1104 511 1 使用查询 business:usage:query
1105 511 2 使用新增 business:usage:add
1106 512 1 退回查询 business:return:query
1107 512 2 退回申请 business:return:request
1108 512 3 退回确认 business:return:confirm
"""
EXPECTED_BUTTONS = {
int(menu_id): (name, int(parent_id), int(order_num), permission)
for menu_id, parent_id, order_num, name, permission in (
line.split() for line in BUTTON_SPECS.splitlines() if line.strip()
)
}
BUSINESS_MENU_IDS = set(range(121, 124)) | set(range(502, 519)) | set(range(1065, 1109))
def _strip_sql_comments(sql: str) -> str:
sql = re.sub(r'/\*.*?\*/', ' ', sql, flags=re.S)
return re.sub(r'(?m)^\s*(?:--|#).*$\n?', ' ', sql)
def _matching_parenthesis(text: str, opening: int) -> int:
depth = 0
quote = None
index = opening
while index < len(text):
char = text[index]
if quote:
if char == quote and index + 1 < len(text) and text[index + 1] == quote:
index += 2
continue
if char == quote:
quote = None
elif char in '\'"`':
quote = char
elif char == '(':
depth += 1
elif char == ')':
depth -= 1
if depth == 0:
return index
index += 1
raise AssertionError('unbalanced SQL parentheses')
def _split_values(values: str) -> list[str]:
parts = []
start = 0
depth = 0
quote = None
index = 0
while index < len(values):
char = values[index]
if quote:
if char == quote and index + 1 < len(values) and values[index + 1] == quote:
index += 2
continue
if char == quote:
quote = None
elif char in '\'"`':
quote = char
elif char == '(':
depth += 1
elif char == ')':
depth -= 1
elif char == ',' and depth == 0:
parts.append(values[start:index].strip())
start = index + 1
index += 1
parts.append(values[start:].strip())
return parts
def _decode_value(value: str) -> str | None:
value = value.strip()
if value.lower() == 'null':
return None
if value.startswith("'") and value.endswith("'"):
return value[1:-1].replace("''", "'")
return value
def _sql_statements(sql: str) -> list[str]:
return [statement.strip() for statement in _strip_sql_comments(sql).split(';') if statement.strip()]
def _insert_rows(sql: str, table: str) -> list[list[str | None]]:
rows = []
pattern = re.compile(
rf'\binsert(?:\s+ignore)?\s+into\s+(?:[`"]?[a-z0-9_]+[`"]?\.)?[`"]?{table}[`"]?'
r'(?:\s*\([^;]*?\))?\s+values\s*',
re.I | re.S,
)
for statement in _sql_statements(sql):
match = pattern.search(statement)
if not match:
continue
index = match.end()
while index < len(statement):
while index < len(statement) and (statement[index].isspace() or statement[index] == ','):
index += 1
if index >= len(statement) or statement[index] != '(':
break
closing = _matching_parenthesis(statement, index)
rows.append([_decode_value(value) for value in _split_values(statement[index + 1 : closing])])
index = closing + 1
return rows
def _create_table_bodies(sql: str) -> dict[str, str]:
clean_sql = _strip_sql_comments(sql)
pattern = re.compile(
r'\bcreate\s+table\s+(?:if\s+not\s+exists\s+)?'
r'(?:[`"]?[a-z0-9_]+[`"]?\.)?[`"]?([a-z_][a-z0-9_]*)[`"]?\s*\(',
re.I,
)
tables = {}
for match in pattern.finditer(clean_sql):
opening = match.end() - 1
closing = _matching_parenthesis(clean_sql, opening)
tables[match.group(1).lower()] = clean_sql[opening + 1 : closing]
return tables
def _identifiers(value: str) -> tuple[str, ...]:
return tuple(part.strip().strip('`"').lower() for part in value.split(','))
def _normalized_check(value: str) -> str:
normalized = re.sub(r'[\s`"]+', '', value).lower()
while normalized.startswith('(') and normalized.endswith(')'):
try:
if _matching_parenthesis(normalized, 0) != len(normalized) - 1:
break
except AssertionError:
break
normalized = normalized[1:-1]
return normalized
def _table_contract(
body: str,
) -> tuple[set[str], tuple[str, ...], set[tuple[str, ...]], set[tuple[str, str]], set[str]]:
columns = set()
primary_key = ()
unique_keys = set()
foreign_keys = set()
checks = set()
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)
first = identifier_match.group(1).lower() if identifier_match else ''
is_column = first and first not in reserved
if is_column:
columns.add(first)
if re.search(r'\bprimary\s+key\b', clean, flags=re.I):
primary_key = (first,)
if re.search(r'\bunique\b', clean, flags=re.I):
unique_keys.add((first,))
reference = re.search(
r'\breferences\s+[`"]?([a-z_][a-z0-9_]*)[`"]?\s*\(\s*[`"]?([a-z_][a-z0-9_]*)',
clean,
flags=re.I,
)
if reference:
foreign_keys.add((first, f'{reference.group(1).lower()}.{reference.group(2).lower()}'))
primary_match = re.search(r'\bprimary\s+key\s*\(', clean, flags=re.I)
if primary_match:
opening = primary_match.end() - 1
primary_key = _identifiers(clean[opening + 1 : _matching_parenthesis(clean, opening)])
unique_match = re.search(
r'\bunique(?:\s+(?:key|index))?(?:\s+[`"]?[a-z_][a-z0-9_]*[`"]?)?\s*\(',
clean,
flags=re.I,
)
if unique_match:
opening = unique_match.end() - 1
unique_keys.add(_identifiers(clean[opening + 1 : _matching_parenthesis(clean, opening)]))
foreign_match = re.search(r'\bforeign\s+key\s*\(', clean, flags=re.I)
if foreign_match:
opening = foreign_match.end() - 1
local_columns = _identifiers(clean[opening + 1 : _matching_parenthesis(clean, opening)])
reference = re.search(
r'\breferences\s+[`"]?([a-z_][a-z0-9_]*)[`"]?\s*\((.*?)\)',
clean,
flags=re.I | re.S,
)
if reference:
target_columns = _identifiers(reference.group(2))
for local_column, target_column in zip(local_columns, target_columns, strict=False):
foreign_keys.add((local_column, f'{reference.group(1).lower()}.{target_column}'))
check_match = re.search(r'\bcheck\s*\(', clean, flags=re.I)
if check_match:
opening = check_match.end() - 1
checks.add(_normalized_check(clean[opening + 1 : _matching_parenthesis(clean, opening)]))
return columns, primary_key, unique_keys, foreign_keys, checks
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]
counts = {menu_id: sum(int(row[0]) == menu_id for row in selected) for menu_id in BUSINESS_MENU_IDS}
if set(counts) != BUSINESS_MENU_IDS or any(count != 1 for count in counts.values()):
raise AssertionError(f'business menu row counts differ: {counts}')
return {int(row[0]): row for row in selected}
class SqlContractTest(unittest.TestCase):
def test_initializers_define_exact_business_table_set(self) -> None:
for filename in INITIALIZERS:
with self.subTest(filename=filename):
sql = (SQL_ROOT / filename).read_text(encoding='utf-8')
actual = {table for table in _create_table_bodies(sql) if table.startswith('biz_')}
self.assertEqual(EXPECTED_TABLES, actual)
def test_initializers_match_orm_table_contract(self) -> None:
for filename in INITIALIZERS:
tables = _create_table_bodies((SQL_ROOT / filename).read_text(encoding='utf-8'))
for table in sorted(EXPECTED_TABLES):
with self.subTest(filename=filename, table=table):
self.assertIn(table, tables)
columns, primary_key, unique_keys, foreign_keys, checks = _table_contract(tables[table])
self.assertEqual(EXPECTED_COLUMNS[table], columns)
self.assertEqual(EXPECTED_PRIMARY_KEYS[table], primary_key)
self.assertEqual(EXPECTED_UNIQUE_KEYS[table], unique_keys)
self.assertEqual(EXPECTED_FOREIGN_KEYS[table], foreign_keys)
self.assertEqual(EXPECTED_CHECKS[table], checks)
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()
}
self.assertEqual(61, len(expected_permissions))
for filename in INITIALIZERS:
with self.subTest(filename=filename):
rows = _business_menu_rows((SQL_ROOT / filename).read_text(encoding='utf-8'))
self.assertTrue(all(len(row) == SYS_MENU_VALUE_COUNT for row in rows.values()))
self.assertEqual(
('基础资料维护', '1', '10', 'businessMaster', 'M', ''),
tuple(rows[121][index] for index in (1, 2, 3, 4, 10, 13)),
)
self.assertEqual(
('业务管理', '1', '11', 'businessOperation', 'M', ''),
tuple(rows[122][index] for index in (1, 2, 3, 4, 10, 13)),
)
self.assertEqual(
('报表查询', '1', '12', 'businessReport', 'M', ''),
tuple(rows[123][index] for index in (1, 2, 3, 4, 10, 13)),
)
for menu_id in (121, 122, 123):
self.assertEqual(
(None, '', '', '1', '0', '0', '0'), tuple(rows[menu_id][i] for i in (5, 6, 7, 8, 9, 11, 12))
)
for menu_id, expected in EXPECTED_PAGES.items():
row = rows[menu_id]
actual = (row[1], int(row[2]), int(row[3]), row[4], row[5], row[13])
self.assertEqual(expected, actual, menu_id)
self.assertEqual(
('', '', '1', '0', 'C', '0', '0'), tuple(row[index] for index in (6, 7, 8, 9, 10, 11, 12))
)
for menu_id, expected in EXPECTED_BUTTONS.items():
row = rows[menu_id]
actual = (row[1], int(row[2]), int(row[3]), row[13])
self.assertEqual(expected, actual, menu_id)
self.assertEqual(
('', '', '', '', '1', '0', 'F', '0', '0'),
tuple(row[index] for index in (4, 5, 6, 7, 8, 9, 10, 11, 12)),
)
all_menu_rows = _insert_rows((SQL_ROOT / filename).read_text(encoding='utf-8'), 'sys_menu')
actual_permissions = {
row[13]
for row in all_menu_rows
if len(row) == SYS_MENU_VALUE_COUNT
and row[13]
and re.match(r'^(?:master|business|report):', row[13])
}
self.assertEqual(expected_permissions, actual_permissions)
def test_mysql_and_postgresql_business_menu_fields_are_identical(self) -> None:
mysql_rows = _business_menu_rows((SQL_ROOT / INITIALIZERS[0]).read_text(encoding='utf-8'))
postgres_rows = _business_menu_rows((SQL_ROOT / INITIALIZERS[1]).read_text(encoding='utf-8'))
compared_indexes = (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13)
mysql_contract = {
menu_id: tuple(row[index] for index in compared_indexes) for menu_id, row in mysql_rows.items()
}
postgres_contract = {
menu_id: tuple(row[index] for index in compared_indexes) for menu_id, row in postgres_rows.items()
}
self.assertEqual(mysql_contract, postgres_contract)
def test_initializers_do_not_grant_new_business_menus_to_roles(self) -> None:
for filename in INITIALIZERS:
with self.subTest(filename=filename):
rows = _insert_rows((SQL_ROOT / filename).read_text(encoding='utf-8'), 'sys_role_menu')
granted = {int(row[1]) for row in rows if len(row) > 1 and str(row[1]).isdigit()}
self.assertFalse(BUSINESS_MENU_IDS & granted)
self.assertFalse({4, 99, 118, 119, 1061, 1062, 1063, 1064} & granted)
def test_initializers_exclude_removed_ai_and_external_link_seeds(self) -> None:
removed_menu_ids = {4, 99, 118, 119, 1061, 1062, 1063, 1064}
for filename in INITIALIZERS:
with self.subTest(filename=filename):
sql = (SQL_ROOT / filename).read_text(encoding='utf-8')
menu_ids = {
int(row[0])
for row in _insert_rows(sql, 'sys_menu')
if row and row[0] is not None and str(row[0]).isdigit()
}
self.assertFalse(removed_menu_ids & menu_ids)
dict_types = _insert_rows(sql, 'sys_dict_type')
self.assertFalse(any(row[0] == '12' or row[2] == 'ai_provider_type' for row in dict_types))
dict_data = _insert_rows(sql, 'sys_dict_data')
self.assertFalse(
any(int(row[0]) in REMOVED_AI_DICT_CODES or row[4] == 'ai_provider_type' for row in dict_data)
)
def test_initializers_keep_menu_allocator_and_use_native_dialects(self) -> None:
mysql = (SQL_ROOT / 'shierfeng-fastapi.sql').read_text(encoding='utf-8').lower()
postgres = (SQL_ROOT / 'shierfeng-fastapi-pg.sql').read_text(encoding='utf-8').lower()
self.assertRegex(mysql, r'auto_increment\s*=\s*2000')
self.assertNotRegex(mysql, r'\bbigserial\b|\balter\s+sequence\b|\bcomment\s+on\b')
self.assertRegex(postgres, r'alter\s+sequence\s+sys_menu_menu_id_seq\s+restart\s+2000')
self.assertNotIn('`', postgres)
self.assertNotRegex(postgres, r'\bauto_increment\b|\bengine\s*=')
def test_initializers_create_event_before_inventory_foreign_key(self) -> None:
for filename in INITIALIZERS:
with self.subTest(filename=filename):
sql = _strip_sql_comments((SQL_ROOT / filename).read_text(encoding='utf-8')).lower()
self.assertIn('create table biz_pallet_event', sql)
self.assertIn('create table biz_pallet_inventory', sql)
self.assertLess(
sql.index('create table biz_pallet_event'), sql.index('create table biz_pallet_inventory')
)
def test_initializers_drop_business_tables_before_recreating_referenced_system_tables(self) -> None:
reverse_dependency_order = (
'biz_pallet_inventory',
'biz_pallet_event',
'biz_return_item',
'biz_return_order',
'biz_usage_record',
'biz_shipment_item',
'biz_shipment_order',
'biz_filling_record',
'biz_inbound_item',
'biz_inbound_order',
'biz_user_site',
'biz_user_warehouse',
'biz_pallet',
'biz_production_line',
'biz_customer_site',
'biz_customer',
'biz_warehouse',
'biz_strain',
)
for filename in INITIALIZERS:
with self.subTest(filename=filename):
sql = _strip_sql_comments((SQL_ROOT / filename).read_text(encoding='utf-8')).lower()
drop_positions = [sql.index(f'drop table if exists {table};') for table in reverse_dependency_order]
self.assertEqual(sorted(drop_positions), drop_positions)
self.assertLess(drop_positions[-1], sql.index('drop table if exists sys_user'))
def test_patch_exists_and_preserves_ai_business_tables(self) -> None:
self.assertTrue(PATCH_PATH.is_file(), f'missing patch: {PATCH_PATH}')
raw = PATCH_PATH.read_text(encoding='utf-8').lower()
sql = re.sub(r'/\*.*?\*/', '', raw, flags=re.S)
sql = re.sub(r'(?m)^\s*(?:--|#).*$\n?', '', sql).replace('`', '')
for table in (
'ai_models',
'ai_chat_config',
'ai_sessions',
'ai_memories',
'ai_metrics',
'ai_eval_runs',
'ai_knowledge',
'ai_culture',
'ai_traces',
'ai_spans',
'ai_schema_versions',
):
qualified = rf'(?:[a-z0-9_]+\.)?{table}'
patterns = (
rf'\bdrop\s+(?:temporary\s+)?table\s+(?:if\s+exists\s+)?{qualified}\b',
rf'\btruncate\s+(?:table\s+)?{qualified}\b',
rf'\bdelete\s+from\s+{qualified}\b',
rf'\balter\s+table\s+(?:if\s+exists\s+)?{qualified}\b',
)
for pattern in patterns:
self.assertIsNone(re.search(pattern, sql), f'dangerous AI table mutation: {table}')
self.assertNotRegex(sql, r'\b(?:drop\s+(?:temporary\s+)?table|truncate\s+(?:table\s+)?|delete\s+from)\s+biz_')
def test_patch_is_documented_idempotent_and_contains_full_contract(self) -> None:
self.assertTrue(PATCH_PATH.is_file(), f'missing patch: {PATCH_PATH}')
raw = PATCH_PATH.read_text(encoding='utf-8')
lower = raw.lower()
self.assertIn('隐式提交', raw)
self.assertIn('备份', raw)
self.assertIn('人工审查', raw)
self.assertIn('未自动执行', raw)
self.assertLess(lower.index('ddl'), lower.index('dml'))
self.assertEqual(18, len(re.findall(r'create\s+table\s+if\s+not\s+exists\s+`?biz_', lower)))
tables = _create_table_bodies(raw)
self.assertEqual(EXPECTED_TABLES, {table for table in tables if table.startswith('biz_')})
for table in EXPECTED_TABLES:
with self.subTest(table=table):
columns, primary_key, unique_keys, foreign_keys, checks = _table_contract(tables[table])
self.assertEqual(EXPECTED_COLUMNS[table], columns)
self.assertEqual(EXPECTED_PRIMARY_KEYS[table], primary_key)
self.assertEqual(EXPECTED_UNIQUE_KEYS[table], unique_keys)
self.assertEqual(EXPECTED_FOREIGN_KEYS[table], foreign_keys)
self.assertEqual(EXPECTED_CHECKS[table], checks)
patch_rows = _business_menu_rows(raw)
mysql_rows = _business_menu_rows((SQL_ROOT / 'shierfeng-fastapi.sql').read_text(encoding='utf-8'))
compared_indexes = (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13)
self.assertEqual(
{menu_id: tuple(row[index] for index in compared_indexes) for menu_id, row in mysql_rows.items()},
{menu_id: tuple(row[index] for index in compared_indexes) for menu_id, row in patch_rows.items()},
)
self.assertRegex(lower, r'\bon\s+duplicate\s+key\s+update\b|\binsert\s+ignore\s+into\s+sys_menu\b')
self.assertNotRegex(_strip_sql_comments(lower), r'\binsert(?:\s+ignore)?\s+into\s+`?sys_role_menu`?\b')
self.assertIn('/getRouters', raw)
self.assertIn('用户信息', raw)
self.assertIn('sys_dict:ai_provider_type', raw)
executable = _strip_sql_comments(raw)
self.assertNotIn('/getRouters', executable)
self.assertNotIn('sys_dict:ai_provider_type', executable)
self.assertNotRegex(lower, r'\bbigserial\b|\balter\s+sequence\b|\bcomment\s+on\b')
def test_patch_cleanup_is_explicit_and_ordered(self) -> None:
self.assertTrue(PATCH_PATH.is_file(), f'missing patch: {PATCH_PATH}')
sql = _strip_sql_comments(PATCH_PATH.read_text(encoding='utf-8')).replace('`', '').lower()
role_cleanup = re.search(r'delete\s+from\s+sys_role_menu\s+where\s+menu_id\s+in\s*\([^;]+\)', sql)
ai_menu_cleanup = re.search(r'delete\s+from\s+sys_menu\s+where\s+menu_id\s+in\s*\([^;]+\)', sql)
dict_data_cleanup = re.search(r'delete\s+from\s+sys_dict_data\s+where\s+[^;]+', sql)
dict_type_cleanup = re.search(r'delete\s+from\s+sys_dict_type\s+where\s+[^;]+', sql)
external_link_cleanup = re.search(r'delete\s+from\s+sys_menu\s+where\s+menu_id\s*=\s*99\b', sql)
for cleanup in (
role_cleanup,
ai_menu_cleanup,
dict_data_cleanup,
dict_type_cleanup,
external_link_cleanup,
):
self.assertIsNotNone(cleanup)
self.assertRegex(role_cleanup.group(), r'\b4\b.*\b99\b.*\b118\b.*\b119\b.*\b1061\b.*\b1064\b')
self.assertRegex(ai_menu_cleanup.group(), r'\b4\b.*\b118\b.*\b119\b.*\b1061\b.*\b1064\b')
self.assertRegex(dict_data_cleanup.group(), r'dict_code\s+between\s+33\s+and\s+69')
self.assertIn('ai_provider_type', dict_data_cleanup.group())
self.assertRegex(dict_type_cleanup.group(), r'dict_id\s*=\s*12')
self.assertIn('ai_provider_type', dict_type_cleanup.group())
self.assertLess(role_cleanup.start(), ai_menu_cleanup.start())
self.assertLess(ai_menu_cleanup.start(), dict_data_cleanup.start())
self.assertLess(dict_data_cleanup.start(), dict_type_cleanup.start())
self.assertLess(dict_type_cleanup.start(), external_link_cleanup.start())
self.assertNotRegex(sql, r'\bdelete\s+from\s+(?:[a-z0-9_]+\.)?(?:biz|ai)_[a-z0-9_]+\b')
if __name__ == '__main__':
unittest.main()