feat: 为现场日历增加单据类型并同步派车记录,减少重复录入。
按起止地关键词生成现场出入库计划,同步来源修改和删除,保留历史记录不补录。调整卡片及复制文本的单据类型格式,并记录测试库和生产库迁移结果。
This commit is contained in:
1 parent
1ae8603045
commit
fcbf8a9b65
17 files changed
+732
-42
No files matched your search
@@ -0,0 +1,19 @@
|
||||
-- 现场日历新增选填单据类型,历史记录保留 NULL。
|
||||
-- 先完成手工派车日历建表,再执行本迁移,最后部署后端和 Web。
|
||||
-- MySQL 5.7 / utf8mb4,可重复执行。
|
||||
SET NAMES utf8mb4;
|
||||
|
||||
SET @col_exists = (
|
||||
SELECT COUNT(*) FROM information_schema.COLUMNS
|
||||
WHERE TABLE_SCHEMA = DATABASE()
|
||||
AND TABLE_NAME = 'pl_dispatch_calendar'
|
||||
AND COLUMN_NAME = 'document_type'
|
||||
);
|
||||
SET @ddl = IF(
|
||||
@col_exists = 0,
|
||||
'ALTER TABLE `pl_dispatch_calendar` ADD COLUMN `document_type` varchar(50) DEFAULT NULL COMMENT ''单据类型(现场日历,支持自定义)'' AFTER `calendar_kind`',
|
||||
'SELECT 1'
|
||||
);
|
||||
PREPARE stmt FROM @ddl;
|
||||
EXECUTE stmt;
|
||||
DEALLOCATE PREPARE stmt;
|
||||
@@ -0,0 +1,41 @@
|
||||
-- 派车日历自动同步现场日历,MySQL 5.7 / utf8mb4,可重复执行。
|
||||
-- 前置:2026-10-30-dispatch-calendar-document-type.sql。先执行迁移再部署后端和 Web。
|
||||
-- 只改变结构:历史来源默认 site_sync_enabled=0,后续编辑也不补录。
|
||||
-- 上线后后端新建来源显式设置为1;手工现场记录仍为0。
|
||||
SET NAMES utf8mb4;
|
||||
|
||||
SET @col_exists = (
|
||||
SELECT COUNT(*) FROM information_schema.COLUMNS
|
||||
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'pl_dispatch_calendar'
|
||||
AND COLUMN_NAME = 'site_sync_enabled'
|
||||
);
|
||||
SET @ddl = IF(@col_exists = 0,
|
||||
'ALTER TABLE `pl_dispatch_calendar` ADD COLUMN `site_sync_enabled` smallint NOT NULL DEFAULT 0 COMMENT ''是否启用现场同步:仅新建来源置1,历史为0'' AFTER `document_type`',
|
||||
'SELECT 1');
|
||||
PREPARE stmt FROM @ddl;
|
||||
EXECUTE stmt;
|
||||
DEALLOCATE PREPARE stmt;
|
||||
|
||||
SET @col_exists = (
|
||||
SELECT COUNT(*) FROM information_schema.COLUMNS
|
||||
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'pl_dispatch_calendar'
|
||||
AND COLUMN_NAME = 'source_record_id'
|
||||
);
|
||||
SET @ddl = IF(@col_exists = 0,
|
||||
'ALTER TABLE `pl_dispatch_calendar` ADD COLUMN `source_record_id` varchar(64) DEFAULT NULL COMMENT ''自动现场记录的来源派车ID,手工记录为空'' AFTER `site_sync_enabled`',
|
||||
'SELECT 1');
|
||||
PREPARE stmt FROM @ddl;
|
||||
EXECUTE stmt;
|
||||
DEALLOCATE PREPARE stmt;
|
||||
|
||||
SET @idx_exists = (
|
||||
SELECT COUNT(*) FROM information_schema.STATISTICS
|
||||
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'pl_dispatch_calendar'
|
||||
AND INDEX_NAME = 'uk_dispatch_calendar_source'
|
||||
);
|
||||
SET @ddl = IF(@idx_exists = 0,
|
||||
'ALTER TABLE `pl_dispatch_calendar` ADD UNIQUE KEY `uk_dispatch_calendar_source` (`tenant_no`, `source_record_id`)',
|
||||
'SELECT 1');
|
||||
PREPARE stmt FROM @ddl;
|
||||
EXECUTE stmt;
|
||||
DEALLOCATE PREPARE stmt;
|
||||
@@ -36469,6 +36469,9 @@ CREATE TABLE IF NOT EXISTS `pl_dispatch_calendar` (
|
||||
`destination` varchar(255) NOT NULL COMMENT '目的地',
|
||||
`quantity` varchar(16) DEFAULT NULL COMMENT '数量文本(吨),最多两位小数,不参与汇总',
|
||||
`calendar_kind` varchar(16) NOT NULL DEFAULT 'acid' COMMENT '日历类型:acid酸派车 alkali碱派车 other其他 site现场',
|
||||
`document_type` varchar(50) DEFAULT NULL COMMENT '单据类型(现场日历,支持自定义)',
|
||||
`site_sync_enabled` smallint NOT NULL DEFAULT 0 COMMENT '是否启用现场同步:仅新建来源置1,历史为0',
|
||||
`source_record_id` varchar(64) DEFAULT NULL COMMENT '自动现场记录的来源派车ID,手工记录为空',
|
||||
`tenant_no` varchar(64) NOT NULL COMMENT '租户编号',
|
||||
`order_num` int DEFAULT NULL COMMENT '排序号',
|
||||
`remark` varchar(500) DEFAULT NULL COMMENT '备注',
|
||||
@@ -36480,7 +36483,8 @@ CREATE TABLE IF NOT EXISTS `pl_dispatch_calendar` (
|
||||
`del_flag` smallint NOT NULL DEFAULT 0 COMMENT '删除标记',
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `idx_dispatch_calendar_month` (`tenant_no`,`del_flag`,`dispatch_date`),
|
||||
KEY `idx_dispatch_calendar_vehicle` (`tenant_no`,`vehicle_id`,`dispatch_date`)
|
||||
KEY `idx_dispatch_calendar_vehicle` (`tenant_no`,`vehicle_id`,`dispatch_date`),
|
||||
UNIQUE KEY `uk_dispatch_calendar_source` (`tenant_no`,`source_record_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='手工派车日历';
|
||||
|
||||
-- 维护与查看分权;仅向已有日历入口且具有运输计划维护能力的角色授予维护按钮。
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
# 派车日历自动同步现场日历 Implementation Plan
|
||||
|
||||
> 使用 executing-plans 逐项执行;需求已确认,在当前已有单据类型改动上继续,不另行拆分工作区。
|
||||
|
||||
**Goal:** 新建酸、碱、其他派车记录按起止地生成并维护一条现场记录,历史记录不补录。
|
||||
|
||||
**Architecture:** 来源开关区分历史;唯一来源关联防重复;后端行锁与同事务保存;前端自动记录只读。
|
||||
|
||||
**Tech Stack:** Python 3.9、SQLAlchemy、Pydantic v2、MySQL 5.7、Vue 3。
|
||||
|
||||
## Task 1:先建立回归
|
||||
|
||||
- [x] 新建 `backend/tests/test_pl_dispatch_calendar_site_sync.py`,复用现有 SQLite fixture,通过公开 CRUD 验证匹配矩阵、生命周期、历史不补录、隔离、关联保护、唯一约束与失败回滚。
|
||||
- [x] 在 backend 执行 `ENV_FILE=/dev/null venv/bin/python -m pytest tests/test_pl_dispatch_calendar_site_sync.py -q`,确认自动生成断言失败。
|
||||
|
||||
## Task 2:模型、事务和迁移
|
||||
|
||||
- [x] 修改 `backend/app/models/pl_dispatch_calendar.py`,增加 `site_sync_enabled` smallint 默认 0、`source_record_id` varchar(64) 可空、租户来源唯一索引。
|
||||
- [x] 修改 `backend/app/services/pl_dispatch_calendar_service.py`:`get_record(..., for_update=True)` 锁定来源;创建时启用新来源;同步助手按四种匹配结果原地维护现场行;更新、删除拒绝自动现场行;每次 CRUD 只提交一次且失败 rollback。
|
||||
- [x] `sourceRecordId` 仅输出,不加入可写 schema。补充现有字段集合测试。
|
||||
- [x] 新建 `docs/sql/migrations/2026-10-30-dispatch-calendar-site-sync.sql` 并同步 `docs/sql/shanxia2.0.sql`;使用 information_schema 和 PREPARE 条件执行 ALTER,禁止历史数据 UPDATE/INSERT。
|
||||
- [x] 执行两个后端日历测试模块,确认通过。
|
||||
|
||||
## Task 3:界面保护与验证
|
||||
|
||||
- [x] 在 `web-admin/scripts/test-manual-dispatch-calendar.mjs` 加入自动现场 fixture,验证有维护权限时详情仍无编辑/删除且显示来源提示;先运行确认失败。
|
||||
- [x] 修改 `web-admin/src/api/pl/dispatch-calendar.ts` 的只读 Record 类型,增加 `sourceRecordId?: string | null`。
|
||||
- [x] 修改 `web-admin/src/views/pl/dispatch-calendar/ManualCalendar.vue`,详情提示来源维护,隐藏关联行编辑删除;共享 `useManualCalendar.ts` 防止直接打开编辑和删除。
|
||||
- [x] 执行已有浏览器脚本、前端模型测试、受影响文件 ESLint、`pnpm build`,完成代码审查。数据库迁移另按后续用户授权执行,见下方记录。
|
||||
|
||||
- [x] 按后续要求调整现场卡片:单据类型以普通文字追加在路线末尾的中文括号中;复制文本隐藏起止地和备注,类型追加在产品、数量末尾。单条和同车多条浏览器回归通过。
|
||||
|
||||
|
||||
## 验证结果
|
||||
|
||||
- 后端两个日历模块:98 项通过,包含事务故障注入、1213 有限重试及禁止提交后重放。
|
||||
- 前端日历模型:13 项通过;ESLint、Stylelint 通过。
|
||||
- 浏览器:Web 酸/碱/其他/现场和 H5 酸共 5 个场景通过,现场自动行的详情只读提示通过。
|
||||
- Web `pnpm build` 通过(41.95 秒),保留已有组件命名冲突和产物大小提示。
|
||||
- 代码审查已完成,死锁重试和提交后刷新边界问题已修复并回归。
|
||||
- SQL 迁移已按用户授权在测试库和生产库执行并核验(MySQL 5.7.36),详见下方执行记录。后端业务回归使用 SQLite;真实 MySQL 并发仍未实测。
|
||||
- 浏览器测试时钟改为固定起点后正常前进,避免冻结 Date.now 导致 Vue 嵌套点击时间戳过滤;业务页面未为测试增加特殊逻辑。
|
||||
|
||||
## 数据库执行记录(2026-10-30 北京时间 13:54)
|
||||
|
||||
用户明确授权在测试数据库及生产数据库执行两个 SQL,按测试后生产顺序完成。连接分别来自 `backend/.env` 和 `backend/.env.production`,未修改环境配置。
|
||||
|
||||
| 环境 | 数据库 | 执行前记录数 | 执行后记录数 | 原有字段及记录 | 历史同步开关 |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| 测试 | shanxia2.0 | 7 | 7 | 逐行比对未变 | 全部为 0 |
|
||||
| 生产 | shanxia2.0 | 31 | 31 | 逐行比对未变 | 全部为 0 |
|
||||
|
||||
两库均成功新增 `document_type`、`site_sync_enabled`、`source_record_id`,并核验唯一索引 `uk_dispatch_calendar_source (tenant_no, source_record_id)`。未补录历史数据,未部署应用或重启服务。
|
||||
|
||||
执行脚本 SHA-256:
|
||||
|
||||
- `2026-10-30-dispatch-calendar-document-type.sql`:`7bba5867852a45d43bded641ca29e0c4a6b654159c911352fc4440deeda8456b`
|
||||
- `2026-10-30-dispatch-calendar-site-sync.sql`:`0a6bd205d7a93253b0bae119b1ba7cbcf56c31fb3f2380a03bcdd94fa6fb5f04`
|
||||
@@ -0,0 +1,28 @@
|
||||
# 派车日历自动同步现场日历
|
||||
|
||||
用户已确认按起止地匹配同步,历史记录不补录,开始实施。
|
||||
|
||||
## 规则与边界
|
||||
|
||||
- 仅酸、碱、其他日历作为来源;始发地含“山峡”或“云栋”为出库,目的地含任一关键词为入库,两端命中为一条“出入库计划”,均不命中不生成。
|
||||
- 同步日期、车辆及车牌快照、产品及名称快照、起止地、数量和备注,不联动实际运输单或库存。
|
||||
- 来源更新时原地更新关联现场记录;失去匹配或来源删除时软删除;重新匹配时恢复原关联行,保持 ID 和创建信息。
|
||||
- 上线前记录不补录,即使后续编辑也不启用同步。上线后新建的来源即使初始不匹配,也支持后续修改命中。
|
||||
- 手工现场记录独立维护;自动现场记录只读,提示在来源派车日历维护,后端同时拒绝直接修改/删除。
|
||||
- 现场卡片将类型以普通文字放在路线末尾,如“山峡 → 芜湖(出库计划)”,不使用 Tag;复制文本保留车牌、产品和数量,隐藏起止地和备注,类型放在每条产品行末尾的中文括号中。
|
||||
|
||||
## 实现选择
|
||||
|
||||
采用后端同步事务,所有客户端统一生效。前端分步创建会产生部分成功;异步任务会延迟并增加重试成本,本需求无需引入。
|
||||
|
||||
`pl_dispatch_calendar` 增加内部 `site_sync_enabled`(默认 0,服务创建新来源显式设 1),以及只读 `source_record_id`(手工行 NULL)。唯一索引 `(tenant_no, source_record_id)` 限制每个来源最多一条关联行,包括软删除行。迁移只变更结构,不更新业务记录。
|
||||
|
||||
更新和删除先按租户、用户数据范围锁定来源行,再同步关联行并一次提交;任何失败回滚。关联行继承来源创建人和部门以保持数据范围一致。接口只读输出 `sourceRecordId`,输入禁止伪造关联和开关。
|
||||
|
||||
MySQL 死锁错误 1213 回滚后最多尝试三次完整事务,其他错误直接抛出。重试范围不含提交后的查询,避免重放已提交的新增操作。
|
||||
|
||||
## 验收
|
||||
|
||||
覆盖三个来源日历、两种关键词、四种匹配情况、重复保存、变更月份/车辆/产品、取消匹配/重新命中、来源删除、手工现场不递归、历史不补录、隔离和事务失败回滚。Web 现场详情显示维护提示并隐藏编辑删除;原手工 CRUD 保持不变。
|
||||
|
||||
上线顺序:单据类型迁移 → 同步关联迁移 → 后端和 Web 发布。无需 env 配置及小程序包变更。
|
||||
Reference in new issue
Block a user