84e12ecd3923f28b62b8659b138a554b0f6e084d..15b4f0733767ae5d8a74f3302c21d13eb4697b4f
2026-02-02 vincentlu
#
15b4f0 对比 | 目录
2026-02-02 vincentlu
#
827e7d 对比 | 目录
2026-02-02 vincentlu
#
d125cd 对比 | 目录
2026-02-02 vincentlu
#
5c3f2c 对比 | 目录
2026-02-02 vincentlu
#
a493cc 对比 | 目录
2026-02-02 vincentlu
#
0267cc 对比 | 目录
2026-02-02 vincentlu
#
d67321 对比 | 目录
2026-02-02 vincentlu
#
558eac 对比 | 目录
2026-02-02 vincentlu
#
264c93 对比 | 目录
2026-02-02 vincentlu
Merge remote-tracking branch 'origin/rcs_master' into rcs_master
064337 对比 | 目录
2026-02-02 vincentlu
#
6ad413 对比 | 目录
2026-02-02 vincentlu
#
a476b3 对比 | 目录
2026-02-02 vincentlu
#
1c00a6 对比 | 目录
2026-02-02 vincentlu
#
ad04df 对比 | 目录
2026-02-02 vincentlu
#
f6aec4 对比 | 目录
2026-01-30 vincentlu
#
2ea9c3 对比 | 目录
2026-01-30 vincentlu
#
3f70b7 对比 | 目录
2026-01-30 vincentlu
#
7f4dd7 对比 | 目录
2026-01-30 vincentlu
#
08dcfb 对比 | 目录
2026-01-30 vincentlu
#
e9e8c6 对比 | 目录
2026-01-30 vincentlu
#
225130 对比 | 目录
2026-02-02 vincentlu
#
53c6a3 对比 | 目录
2026-02-02 vincentlu
#
067ea0 对比 | 目录
2026-02-02 vincentlu
#
020ed9 对比 | 目录
2026-02-02 vincentlu
#
027539 对比 | 目录
2026-01-30 vincentlu
#
6773ee 对比 | 目录
2026-01-30 vincentlu
#
a0916a 对比 | 目录
2026-01-30 vincentlu
#
88cce6 对比 | 目录
2026-01-30 vincentlu
#
1a1035 对比 | 目录
2026-01-30 vincentlu
#
fb9bc1 对比 | 目录
2026-01-30 vincentlu
#
bb71a2 对比 | 目录
5 文件已重命名
24个文件已添加
36个文件已修改
2876 ■■■■ 已修改文件
version/db/new.sql 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
version/db/sqlIndex 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
version/doc/RCS开发进度表.xlsx 补丁 | 查看 | 原始文档 | blame | 历史
zy-acs-flow/package-lock.json 106 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zy-acs-flow/package.json 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zy-acs-flow/public/imports/code_import_template.xlsx 补丁 | 查看 | 原始文档 | blame | 历史
zy-acs-flow/src/i18n/en.js 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zy-acs-flow/src/i18n/zh.js 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zy-acs-flow/src/page/ResourceContent.js 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zy-acs-flow/src/page/code/CodeList.jsx 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zy-acs-flow/src/page/components/ImportButton.jsx 17 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zy-acs-flow/src/page/components/ImportTxtModal.jsx 213 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zy-acs-flow/src/page/components/ImportXlsxModal.jsx 191 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zy-acs-flow/src/page/components/useExcelParse.js 77 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zy-acs-flow/src/page/integrationRecord/IntegrationRecordCreate.jsx 207 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zy-acs-flow/src/page/integrationRecord/IntegrationRecordEdit.jsx 180 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zy-acs-flow/src/page/integrationRecord/IntegrationRecordList.jsx 173 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zy-acs-flow/src/page/integrationRecord/IntegrationRecordPanel.jsx 129 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zy-acs-flow/src/page/integrationRecord/index.jsx 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zy-acs-flow/src/page/staReserve/StaReserveList.jsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zy-acs-flow/src/page/task/TaskList.jsx 301 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zy-acs-flow/src/page/task/TaskListAside.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
zy-acs-manager/src/main/java/com/zy/acs/manager/common/CodeBuilder.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zy-acs-manager/src/main/java/com/zy/acs/manager/common/config/ConveyorProperties.java 26 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zy-acs-manager/src/main/java/com/zy/acs/manager/common/config/UplinkProperties.java 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zy-acs-manager/src/main/java/com/zy/acs/manager/common/constant/Constants.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zy-acs-manager/src/main/java/com/zy/acs/manager/common/domain/TaskBoolDto.java 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zy-acs-manager/src/main/java/com/zy/acs/manager/common/domain/TaskDto.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
zy-acs-manager/src/main/java/com/zy/acs/manager/common/utils/ExcelUtil.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zy-acs-manager/src/main/java/com/zy/acs/manager/common/utils/HttpGo.java 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zy-acs-manager/src/main/java/com/zy/acs/manager/core/HandlerController.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zy-acs-manager/src/main/java/com/zy/acs/manager/core/integrate/conveyor/ConveyorController.java 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zy-acs-manager/src/main/java/com/zy/acs/manager/core/integrate/conveyor/SiemensConveyorStationService.java 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zy-acs-manager/src/main/java/com/zy/acs/manager/core/integrate/dto/OpenBusCancelParam.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zy-acs-manager/src/main/java/com/zy/acs/manager/core/integrate/dto/OpenBusCancelResult.java 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zy-acs-manager/src/main/java/com/zy/acs/manager/core/integrate/dto/OpenBusSubmitParam.java 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zy-acs-manager/src/main/java/com/zy/acs/manager/core/integrate/dto/TaskUplinkParam.java 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zy-acs-manager/src/main/java/com/zy/acs/manager/core/integrate/wms/OpenController.java 63 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zy-acs-manager/src/main/java/com/zy/acs/manager/core/integrate/wms/TaskReportService.java 141 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zy-acs-manager/src/main/java/com/zy/acs/manager/core/scheduler/MaintainScheduler.java 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zy-acs-manager/src/main/java/com/zy/acs/manager/core/scheduler/test/AutoRunRebootScheduler.java 31 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zy-acs-manager/src/main/java/com/zy/acs/manager/core/scheduler/test/AutoRunScheduler.java 28 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zy-acs-manager/src/main/java/com/zy/acs/manager/core/scheduler/test/AutoTestDeviationScheduler.java 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zy-acs-manager/src/main/java/com/zy/acs/manager/core/scheduler/test/ConveyorAutoRunScheduler.java 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zy-acs-manager/src/main/java/com/zy/acs/manager/core/service/MainService.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zy-acs-manager/src/main/java/com/zy/acs/manager/core/service/ValidService.java 13 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zy-acs-manager/src/main/java/com/zy/acs/manager/manager/controller/BusController.java 11 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zy-acs-manager/src/main/java/com/zy/acs/manager/manager/controller/CodeController.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zy-acs-manager/src/main/java/com/zy/acs/manager/manager/controller/IntegrationRecordController.java 108 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zy-acs-manager/src/main/java/com/zy/acs/manager/manager/controller/TaskController.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zy-acs-manager/src/main/java/com/zy/acs/manager/manager/entity/IntegrationRecord.java 196 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zy-acs-manager/src/main/java/com/zy/acs/manager/manager/entity/Task.java 102 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zy-acs-manager/src/main/java/com/zy/acs/manager/manager/enums/IntegrationDirectionType.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zy-acs-manager/src/main/java/com/zy/acs/manager/manager/enums/TaskUplinkStateType.java 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zy-acs-manager/src/main/java/com/zy/acs/manager/manager/mapper/IntegrationRecordMapper.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zy-acs-manager/src/main/java/com/zy/acs/manager/manager/service/BusService.java 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
zy-acs-manager/src/main/java/com/zy/acs/manager/manager/service/IntegrationRecordService.java 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zy-acs-manager/src/main/java/com/zy/acs/manager/manager/service/TaskService.java 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zy-acs-manager/src/main/java/com/zy/acs/manager/manager/service/impl/BusServiceImpl.java 21 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zy-acs-manager/src/main/java/com/zy/acs/manager/manager/service/impl/IntegrationRecordServiceImpl.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zy-acs-manager/src/main/java/com/zy/acs/manager/manager/service/impl/TaskServiceImpl.java 20 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zy-acs-manager/src/main/java/integrationRecord.sql 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zy-acs-manager/src/main/resources/application.yml 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zy-acs-manager/src/main/resources/mapper/manager/IntegrationRecordMapper.xml 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zy-acs-manager/src/main/resources/mapper/manager/TaskMapper.xml 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
version/db/new.sql
@@ -29,3 +29,32 @@
alter table man_sta
    add outbound_wait int null comment '连续出库等待' after inbound_wait;
alter table man_task
    add uplink_sts varchar(64) null comment '上行状态' after empty_mk;
alter table man_task_log
    add uplink_sts varchar(64) null comment '上行状态' after empty_mk;
CREATE TABLE `man_integration_record` (
    `id` BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT 'ID',
    `uuid` VARCHAR(255) DEFAULT NULL COMMENT '编号',
    `namespace` VARCHAR(255) DEFAULT NULL COMMENT '名称空间(*)',
    `url` VARCHAR(255) DEFAULT NULL COMMENT '接口地址',
    `appkey` VARCHAR(255) DEFAULT NULL COMMENT '平台密钥',
    `caller` VARCHAR(255) DEFAULT NULL COMMENT '调用方标识',
    `direction` int(1) NOT NULL DEFAULT 0 COMMENT '方向{1:被调用,2:调用外部}',
    `timestamp` VARCHAR(64) DEFAULT NULL COMMENT '时间戳',
    `client_ip` VARCHAR(64) DEFAULT NULL COMMENT '客户端IP',
    `request` TEXT COMMENT '请求内容',
    `response` TEXT COMMENT '响应内容',
    `err` TEXT COMMENT '异常内容',
    `result` int(1) DEFAULT NULL COMMENT '结果{1:成功,0:失败}',
    `cost_ms` INT(11) DEFAULT NULL COMMENT '耗时',
    `status` int(1) DEFAULT 1 COMMENT '状态{1:正常,0:冻结}',
    `create_time` timestamp DEFAULT CURRENT_TIMESTAMP COMMENT '添加时间',
    `update_time` timestamp DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间',
    `memo` VARCHAR(255) DEFAULT NULL COMMENT '备注',
    PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
version/db/sqlIndex
@@ -25,6 +25,7 @@
CREATE INDEX idx_task_seq_num ON man_task(seq_num);
CREATE INDEX idx_task_agv_id ON man_task(agv_id);
CREATE INDEX idx_task_agv_id_task_sts ON man_task(agv_id, task_sts);
CREATE INDEX idx_task_task_sts_uplink_sts ON man_task(task_sts, uplink_sts);
# man_zone
CREATE INDEX idx_zone_uuid ON man_zone(uuid);
# man_agv_detail
version/doc/RCS¿ª·¢½ø¶È±í.xlsx
Binary files differ
zy-acs-flow/package-lock.json
@@ -24,7 +24,8 @@
        "react-router-dom": "^6.26.1",
        "react-syntax-highlighter": "^15.5.0",
        "three": "^0.155.0",
        "tweedle.js": "^2.1.0"
        "tweedle.js": "^2.1.0",
        "xlsx": "^0.18.5"
      },
      "devDependencies": {
        "@types/node": "^20.10.7",
@@ -2339,6 +2340,15 @@
        "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
      }
    },
    "node_modules/adler-32": {
      "version": "1.3.1",
      "resolved": "https://registry.npmjs.org/adler-32/-/adler-32-1.3.1.tgz",
      "integrity": "sha512-ynZ4w/nUUv5rrsR8UUGoe1VC9hZj6V5hU9Qw1HlMDJGEJw5S7TfTErWTjMys6M7vr0YWcPqs3qAr4ss0nDfP+A==",
      "license": "Apache-2.0",
      "engines": {
        "node": ">=0.8"
      }
    },
    "node_modules/ajv": {
      "version": "6.12.6",
      "resolved": "https://registry.npmmirror.com/ajv/-/ajv-6.12.6.tgz",
@@ -2702,6 +2712,19 @@
        }
      ]
    },
    "node_modules/cfb": {
      "version": "1.2.2",
      "resolved": "https://registry.npmjs.org/cfb/-/cfb-1.2.2.tgz",
      "integrity": "sha512-KfdUZsSOw19/ObEWasvBP/Ac4reZvAGauZhs6S/gqNhXhI7cKwvlH7ulj+dOEYnca4bm4SGo8C1bTAQvnTjgQA==",
      "license": "Apache-2.0",
      "dependencies": {
        "adler-32": "~1.3.0",
        "crc-32": "~1.2.0"
      },
      "engines": {
        "node": ">=0.8"
      }
    },
    "node_modules/chalk": {
      "version": "2.4.2",
      "resolved": "https://registry.npmmirror.com/chalk/-/chalk-2.4.2.tgz",
@@ -2748,6 +2771,15 @@
      "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
      "engines": {
        "node": ">=6"
      }
    },
    "node_modules/codepage": {
      "version": "1.15.0",
      "resolved": "https://registry.npmjs.org/codepage/-/codepage-1.15.0.tgz",
      "integrity": "sha512-3g6NUTPd/YtuuGrhMnOMRjFc+LJw/bnMp3+0r/Wcz3IXUuCosKRJvMphm5+Q+bvTVGcJJuRvVLuYba+WojaFaA==",
      "license": "Apache-2.0",
      "engines": {
        "node": ">=0.8"
      }
    },
    "node_modules/color-convert": {
@@ -2808,6 +2840,18 @@
      },
      "engines": {
        "node": ">=10"
      }
    },
    "node_modules/crc-32": {
      "version": "1.2.2",
      "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz",
      "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==",
      "license": "Apache-2.0",
      "bin": {
        "crc32": "bin/crc32.njs"
      },
      "engines": {
        "node": ">=0.8"
      }
    },
    "node_modules/cross-spawn": {
@@ -3785,6 +3829,15 @@
      "integrity": "sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==",
      "engines": {
        "node": ">=0.4.x"
      }
    },
    "node_modules/frac": {
      "version": "1.1.2",
      "resolved": "https://registry.npmjs.org/frac/-/frac-1.1.2.tgz",
      "integrity": "sha512-w/XBfkibaTl3YDqASwfDUqkna4Z2p9cFSr1aHDt0WoMTECnRfBOv2WArlZILlqgWlmdIlALXGpM2AOhEk5W3IA==",
      "license": "Apache-2.0",
      "engines": {
        "node": ">=0.8"
      }
    },
    "node_modules/fs.realpath": {
@@ -5982,6 +6035,18 @@
        "node": ">=6"
      }
    },
    "node_modules/ssf": {
      "version": "0.11.2",
      "resolved": "https://registry.npmjs.org/ssf/-/ssf-0.11.2.tgz",
      "integrity": "sha512-+idbmIXoYET47hH+d7dfm2epdOMUDjqcB4648sTZ+t2JwoyBFL/insLfB/racrDmsKB3diwsDA696pZMieAC5g==",
      "license": "Apache-2.0",
      "dependencies": {
        "frac": "~1.1.2"
      },
      "engines": {
        "node": ">=0.8"
      }
    },
    "node_modules/strict-uri-encode": {
      "version": "2.0.0",
      "resolved": "https://registry.npmmirror.com/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz",
@@ -6552,6 +6617,24 @@
        "url": "https://github.com/sponsors/ljharb"
      }
    },
    "node_modules/wmf": {
      "version": "1.0.2",
      "resolved": "https://registry.npmjs.org/wmf/-/wmf-1.0.2.tgz",
      "integrity": "sha512-/p9K7bEh0Dj6WbXg4JG0xvLQmIadrner1bi45VMJTfnbVHsc7yIajZyoSoK60/dtVBs12Fm6WkUI5/3WAVsNMw==",
      "license": "Apache-2.0",
      "engines": {
        "node": ">=0.8"
      }
    },
    "node_modules/word": {
      "version": "0.3.0",
      "resolved": "https://registry.npmjs.org/word/-/word-0.3.0.tgz",
      "integrity": "sha512-OELeY0Q61OXpdUfTp+oweA/vtLVg5VDOXh+3he3PNzLGG/y0oylSOC1xRVj0+l4vQ3tj/bB1HVHv1ocXkQceFA==",
      "license": "Apache-2.0",
      "engines": {
        "node": ">=0.8"
      }
    },
    "node_modules/word-wrap": {
      "version": "1.2.5",
      "resolved": "https://registry.npmmirror.com/word-wrap/-/word-wrap-1.2.5.tgz",
@@ -6567,6 +6650,27 @@
      "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
      "dev": true
    },
    "node_modules/xlsx": {
      "version": "0.18.5",
      "resolved": "https://registry.npmjs.org/xlsx/-/xlsx-0.18.5.tgz",
      "integrity": "sha512-dmg3LCjBPHZnQp5/F/+nnTa+miPJxUXB6vtk42YjBBKayDNagxGEeIdWApkYPOf3Z3pm3k62Knjzp7lMeTEtFQ==",
      "license": "Apache-2.0",
      "dependencies": {
        "adler-32": "~1.3.0",
        "cfb": "~1.2.1",
        "codepage": "~1.15.0",
        "crc-32": "~1.2.1",
        "ssf": "~0.11.2",
        "wmf": "~1.0.1",
        "word": "~0.3.0"
      },
      "bin": {
        "xlsx": "bin/xlsx.njs"
      },
      "engines": {
        "node": ">=0.8"
      }
    },
    "node_modules/xtend": {
      "version": "4.0.2",
      "resolved": "https://registry.npmmirror.com/xtend/-/xtend-4.0.2.tgz",
zy-acs-flow/package.json
@@ -28,7 +28,8 @@
    "react-router-dom": "^6.26.1",
    "react-syntax-highlighter": "^15.5.0",
    "three": "^0.155.0",
    "tweedle.js": "^2.1.0"
    "tweedle.js": "^2.1.0",
    "xlsx": "^0.18.5"
  },
  "devDependencies": {
    "@types/node": "^20.10.7",
@@ -46,4 +47,4 @@
    "vite": "^5.3.5"
  },
  "name": "cool-admin-flow"
}
}
zy-acs-flow/public/imports/code_import_template.xlsx
Binary files differ
zy-acs-flow/src/i18n/en.js
@@ -62,9 +62,9 @@
            import: {
                title: 'Import',
                stop: 'Stop import',
                msg: 'Here is a sample CSV file you can use as a template',
                msg: 'Here is a sample file you can use as a template',
                tips: 'The import is running, please do not close this tab.',
                err: 'Failed to import this file, please make sure your provided a valid CSV file.',
                err: 'Failed to import this file, please make sure your provided a valid file.',
                download: 'Download Import Template',
                result: 'Contacts import complete. Imported %{success} success, with %{error} errors',
            },
@@ -158,6 +158,7 @@
        mission: 'Mission',
        staReserve: 'Sta Reserve',
        lane: 'Lane',
        integrationRecord: 'Integration',
    },
    table: {
        field: {
@@ -485,6 +486,7 @@
                destLoc: "dest loc",
                destCode: "dest code",
                emptyMk: "empty mk",
                uplinkSts: "uplink status",
                zpallet: "zpallet",
                phase: "ideal path",
                errDesc: "error",
@@ -603,6 +605,22 @@
                entryAngle: "entry angle",
                maximum: "maximum",
            },
            integrationRecord: {
                uuid: "uuid",
                namespace: "namespace",
                url: "url",
                appkey: "appkey",
                caller: "caller",
                direction: "direction",
                timestamp: "timestamp",
                clientIp: "clientIp",
                request: "request",
                response: "response",
                err: "error",
                result: "result",
                costMs: "costMs",
            },
        }
    },
    page: {
@@ -720,6 +738,18 @@
                reposition: 'clear path reposition',
            }
        },
        task: {
            enums: {
                uplinkSts: {
                    NONE: 'Unknown',
                    PENDING: 'Pending',
                    SENDING: 'Uploading',
                    SUCCESS: 'Success',
                    FAILED: 'Failed',
                    SKIPPED: 'Skipped',
                },
            },
        },
        segment: {
            enums: {
                state: {
zy-acs-flow/src/i18n/zh.js
@@ -62,9 +62,9 @@
            import: {
                title: '导入',
                stop: '停止导入',
                msg: '这是一个可以用作模板的示例 CSV æ–‡ä»¶',
                msg: '这是一个可以用作模板的示例文件',
                tips: '正在导入中,请不要关闭此窗口',
                err: '无法导入此文件,请确保您提供了有效的 CSV æ–‡ä»¶',
                err: '无法导入此文件,请确保您提供了有效的文件',
                download: '下载导入模板',
                result: '导入完成。已导入 %{success} æˆåŠŸ, å’Œ %{error} å¤±è´¥',
            },
@@ -158,6 +158,7 @@
        mission: '执行',
        staReserve: '站点预约',
        lane: '巷道管理',
        integrationRecord: '集成交互',
    },
    table: {
        field: {
@@ -485,6 +486,7 @@
                destLoc: "目标库位",
                destCode: "目标地面码",
                emptyMk: "空料箱",
                uplinkSts: "上行状态",
                zpallet: "料箱码",
                phase: "理想路径",
                errDesc: "异常描述",
@@ -603,6 +605,22 @@
                entryAngle: "车体角度",
                maximum: "承载量",
            },
            integrationRecord: {
                uuid: "编号",
                namespace: "命名空间",
                url: "接口地址",
                appkey: "密钥",
                caller: "调用方",
                direction: "方向",
                timestamp: "时间戳",
                clientIp: "客户端IP",
                request: "请求内容",
                response: "响应内容",
                err: "异常",
                result: "请求结果",
                costMs: "耗时(毫秒)",
            },
        }
    },
    page: {
@@ -720,6 +738,18 @@
                reposition: '清除路径重新定位',
            }
        },
        task: {
            enums: {
                uplinkSts: {
                    NONE: '未知',
                    PENDING: '待上报',
                    SENDING: '上报中',
                    SUCCESS: '成功',
                    FAILED: '失败',
                    SKIPPED: '跳过',
                },
            },
        },
        segment: {
            enums: {
                state: {
zy-acs-flow/src/page/ResourceContent.js
@@ -43,6 +43,7 @@
import mission from "./mission";
import staReserve from './staReserve';
import lane from './lane';
import integrationRecord from './integrationRecord';
const ResourceContent = (node) => {
    switch (node.component) {
@@ -124,6 +125,8 @@
            return staReserve;
        case 'lane':
            return lane;
        case 'integrationRecord':
            return integrationRecord;
        default:
            return {
                list: ListGuesser,
zy-acs-flow/src/page/code/CodeList.jsx
@@ -46,8 +46,10 @@
import ImportButton from '../components/ImportButton'
import { useCodeImport } from './useCodeImport';
import * as importTemp from './importTemp.csv?raw';
const IMPORT_TEMP_URL = `data:text/csv;name=crm_contacts_sample.csv;charset=utf-8,${encodeURIComponent(importTemp.default)}`;
// import * as importTemp from './importTemp.csv?raw';
// const IMPORT_TEMP_URL = `data:text/csv;name=crm_contacts_sample.csv;charset=utf-8,${encodeURIComponent(importTemp.default)}`;
const IMPORT_TEMP_URL = '/imports/code_import_template.xlsx';
const StyledDatagrid = styled(DatagridConfigurable)(({ theme }) => ({
    '& .css-1vooibu-MuiSvgIcon-root': {
@@ -126,7 +128,7 @@
                        <FilterButton />
                        <MyCreateButton onClick={() => { setCreateDialog(true) }} />
                        <SelectColumnsButton preferenceKey='code' />
                        <ImportButton importTemp={IMPORT_TEMP_URL} useCodeImport={useCodeImport} onceBatch={10} />
                        <ImportButton type="xlsx" importTemp={IMPORT_TEMP_URL} useImport={useCodeImport} onceBatch={10} />
                        <MyExportButton />
                    </TopToolbar>
                )}
zy-acs-flow/src/page/components/ImportButton.jsx
@@ -2,11 +2,18 @@
import { useState } from 'react';
import { Button } from 'react-admin';
import ImportModal from './ImportModal';
import ImportTxtModal from './ImportTxtModal';
import ImportXlsxModal from './ImportXlsxModal';
const ImportButton = (props) => {
const ImportButton = ({
    type = 'csv', // csv, txt, xlsx,
    variant = 'text',
    ...props
}) => {
    const [modalOpen, setModalOpen] = useState(false);
    const handleOpenModal = () => {
    const handleOpenModal = (e) => {
        e.stopPropagation();
        setModalOpen(true);
    };
@@ -17,12 +24,14 @@
    return (
        <>
            <Button
                variant={variant}
                startIcon={<UploadIcon />}
                label="common.action.import.title"
                onClick={handleOpenModal}
            />
            <ImportModal open={modalOpen} onClose={handleCloseModal} {...props} />
            {type === 'csv' && (<ImportModal open={modalOpen} onClose={handleCloseModal} {...props} />)}
            {type === 'txt' && (<ImportTxtModal open={modalOpen} onClose={handleCloseModal} {...props} />)}
            {type === 'xlsx' && (<ImportXlsxModal open={modalOpen} onClose={handleCloseModal} {...props} />)}
        </>
    );
};
zy-acs-flow/src/page/components/ImportTxtModal.jsx
New file
@@ -0,0 +1,213 @@
import { useEffect, useState } from 'react';
import { Box, CircularProgress, Stack, Typography } from '@mui/material';
import Alert from '@mui/material/Alert';
import Dialog from '@mui/material/Dialog';
import DialogActions from '@mui/material/DialogActions';
import DialogContent from '@mui/material/DialogContent';
import DialogTitle from '@mui/material/DialogTitle';
import MuiLink from '@mui/material/Link';
import {
    Button,
    FileField,
    FileInput,
    Form,
    Toolbar,
    useRefresh,
    useTranslate
} from 'react-admin';
import { Link } from 'react-router-dom';
import DialogCloseButton from './DialogCloseButton';
import { usePapaParse } from './usePapaParse';
const ImportTxtModal = ({ open, onClose, useImport, params, title = '', onceBatch = 10 }) => {
    const refresh = useRefresh();
    const translate = useTranslate();
    const { processBatch } = useImport();
    const { importer, parseCsv, reset } = usePapaParse({
        batchSize: onceBatch,
        processBatch,
        params,
    });
    const [file, setFile] = useState(null);
    useEffect(() => {
        if (importer.state === 'complete') {
            refresh();
        }
    }, [importer.state, refresh]);
    const handleFileChange = (file) => {
        setFile(file);
    };
    const startImport = async () => {
        if (!file) {
            return;
        }
        parseCsv(file);
    };
    const handleClose = () => {
        reset();
        onClose();
    };
    const handleReset = (e) => {
        e.preventDefault();
        reset();
    };
    return (
        <Dialog
            open={open}
            maxWidth="md"
            fullWidth
            onClick={(e) => e.stopPropagation()}
        >
            <DialogCloseButton onClose={handleClose} />
            <DialogTitle>{translate('common.action.import.title')} {title}</DialogTitle>
            <DialogContent>
                <Form>
                    <Stack spacing={2}>
                        {importer.state === 'running' && (
                            <Stack gap={2}>
                                <Alert
                                    severity="info"
                                    action={
                                        <Box
                                            sx={{
                                                display: 'flex',
                                                height: '100%',
                                                alignItems: 'center',
                                                padding: '0',
                                            }}
                                        >
                                            <CircularProgress size={20} />
                                        </Box>
                                    }
                                    sx={{
                                        alignItems: 'center',
                                        '& .MuiAlert-action': {
                                            padding: 0,
                                            marginRight: 0,
                                        },
                                    }}
                                >
                                    {translate('common.action.import.tips')}
                                </Alert>
                                <Typography variant="body2">
                                    Imported{' '}
                                    <strong>
                                        {importer.importCount} /{' '}
                                        {importer.rowCount}
                                    </strong>{' '}
                                    contacts, with{' '}
                                    <strong>{importer.errorCount}</strong>{' '}
                                    errors.
                                    {importer.remainingTime !== null && (
                                        <>
                                            {' '}
                                            Estimated remaining time:{' '}
                                            <strong>
                                                {millisecondsToTime(
                                                    importer.remainingTime
                                                )}
                                            </strong>
                                            .{' '}
                                            <MuiLink
                                                href="#"
                                                onClick={handleReset}
                                                color="error"
                                            >
                                                {translate('common.action.import.stop')}
                                            </MuiLink>
                                        </>
                                    )}
                                </Typography>
                            </Stack>
                        )}
                        {importer.state === 'error' && (
                            <Alert severity="error">
                                {translate('common.action.import.err')}
                            </Alert>
                        )}
                        {importer.state === 'complete' && (
                            <>
                                <Alert severity="success">
                                    {translate('common.action.import.result', {
                                        success: importer.importCount,
                                        error: importer.errorCount
                                    })}
                                </Alert>
                                {importer.errorMsg && (
                                    <Alert severity="error">
                                        {importer.errorMsg}
                                    </Alert>
                                )}
                            </>
                        )}
                        {importer.state === 'idle' && (
                            <>
                                <FileInput
                                    source="txt"
                                    label="Txt File"
                                    accept={{ 'text/plain': ['.txt'] }}
                                    onChange={handleFileChange}
                                >
                                    <FileField source="src" title="title" />
                                </FileInput>
                            </>
                        )}
                    </Stack>
                </Form>
            </DialogContent>
            <DialogActions
                sx={{
                    p: 0,
                    justifyContent: 'flex-start',
                }}
            >
                <Toolbar
                    sx={{
                        width: '100%',
                    }}
                >
                    {importer.state === 'idle' ? (
                        <>
                            <Button
                                label="common.action.import.title"
                                variant="contained"
                                onClick={startImport}
                                disabled={!file}
                            />
                        </>
                    ) : (
                        <Button
                            label="ra.action.close"
                            onClick={handleClose}
                            disabled={importer.state === 'running'}
                        />
                    )}
                </Toolbar>
            </DialogActions>
        </Dialog>
    );
}
function millisecondsToTime(ms) {
    var seconds = Math.floor((ms / 1000) % 60);
    var minutes = Math.floor((ms / (60 * 1000)) % 60);
    return `${minutes}m ${seconds}s`;
}
export default ImportTxtModal;
zy-acs-flow/src/page/components/ImportXlsxModal.jsx
New file
@@ -0,0 +1,191 @@
// ImportXlsxModal.jsx
import { useEffect, useState } from 'react';
import { Box, CircularProgress, Stack, Typography } from '@mui/material';
import Alert from '@mui/material/Alert';
import Dialog from '@mui/material/Dialog';
import DialogActions from '@mui/material/DialogActions';
import DialogContent from '@mui/material/DialogContent';
import DialogTitle from '@mui/material/DialogTitle';
import MuiLink from '@mui/material/Link';
import {
    Button,
    FileField,
    FileInput,
    Form,
    Toolbar,
    useRefresh,
    useTranslate,
} from 'react-admin';
import DialogCloseButton from './DialogCloseButton';
import { useExcelParse } from './useExcelParse';
const ImportXlsxModal = ({
    open,
    onClose,
    importTemp,
    useImport,
    params,
    title = '',
    onceBatch = 10,
}) => {
    const refresh = useRefresh();
    const t = useTranslate();
    const { processBatch } = useImport();
    const { importer, parseExcel, reset } = useExcelParse({
        batchSize: onceBatch,
        processBatch,
        params,
    });
    const [file, setFile] = useState(null);
    useEffect(() => {
        if (importer.state === 'complete') {
            refresh();
        }
    }, [importer.state, refresh]);
    const handleFileChange = (val) => {
        const f = Array.isArray(val) ? val[0]?.rawFile ?? val[0] : val?.rawFile ?? val;
        setFile(f || null);
    };
    const startImport = async () => {
        if (!file) return;
        parseExcel(file);
    };
    const handleClose = () => {
        reset();
        setFile(null);
        onClose();
    };
    const handleReset = (e) => {
        e.preventDefault();
        reset();
    };
    const nameMatch = importTemp?.split('/').pop() || 'import_template.xlsx';
    return (
        <Dialog open={open} maxWidth="md" fullWidth onClick={(e) => e.stopPropagation()}>
            <DialogCloseButton onClose={handleClose} />
            <DialogTitle>{t('common.action.import.title')} {title}</DialogTitle>
            <DialogContent>
                <Form>
                    <Stack spacing={2}>
                        <Alert
                            severity="info"
                            action={
                                importTemp ? (
                                    <Button
                                        component="a"
                                        label="common.action.import.download"
                                        color="info"
                                        href={importTemp}
                                        download={nameMatch}
                                    />
                                ) : null
                            }
                            sx={{
                                alignItems: 'center',
                                '& .MuiAlert-action': { p: 0, mr: 0 },
                            }}
                        >
                            {t('common.action.import.msg', { _: '这是一个可以用作模板的示例 Excel æ–‡ä»¶' })}
                        </Alert>
                        {importer.state === 'running' && (
                            <Stack gap={2}>
                                <Alert
                                    severity="info"
                                    action={
                                        <Box sx={{ display: 'flex', alignItems: 'center', p: 0 }}>
                                            <CircularProgress size={20} />
                                        </Box>
                                    }
                                    sx={{ alignItems: 'center', '& .MuiAlert-action': { p: 0, mr: 0 } }}
                                >
                                    {t('common.action.import.tips')}
                                </Alert>
                                <Typography variant="body2">
                                    Imported <strong>{importer.importCount}</strong> /{' '}
                                    <strong>{importer.rowCount}</strong> rows, with{' '}
                                    <strong>{importer.errorCount}</strong> errors.
                                    {importer.remainingTime !== null && (
                                        <>
                                            {' '}ETA: <strong>{millisecondsToTime(importer.remainingTime)}</strong>.{' '}
                                            <MuiLink href="#" onClick={handleReset} color="error">
                                                {t('common.action.import.stop')}
                                            </MuiLink>
                                        </>
                                    )}
                                </Typography>
                            </Stack>
                        )}
                        {importer.state === 'error' && (
                            <Alert severity="error">{t('common.action.import.err')}</Alert>
                        )}
                        {importer.state === 'complete' && (
                            <>
                                <Alert severity="success">
                                    {t('common.action.import.result', {
                                        success: importer.importCount,
                                        error: importer.errorCount,
                                    })}
                                </Alert>
                                {importer.errorMsg && <Alert severity="error">{importer.errorMsg}</Alert>}
                            </>
                        )}
                        {importer.state === 'idle' && (
                            <>
                                <FileInput
                                    source="xlsx"
                                    label="Excel File (.xlsx / .xls)"
                                    accept={{
                                        'application/vnd.ms-excel': ['.xls'],
                                        'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': ['.xlsx'],
                                    }}
                                    onChange={handleFileChange}
                                >
                                    <FileField source="src" title="title" />
                                </FileInput>
                            </>
                        )}
                    </Stack>
                </Form>
            </DialogContent>
            <DialogActions sx={{ p: 0, justifyContent: 'flex-start' }}>
                <Toolbar sx={{ width: '100%' }}>
                    {importer.state === 'idle' ? (
                        <Button
                            label="common.action.import.title"
                            variant="contained"
                            onClick={startImport}
                            disabled={!file}
                        />
                    ) : (
                        <Button
                            label="ra.action.close"
                            onClick={handleClose}
                            disabled={importer.state === 'running'}
                        />
                    )}
                </Toolbar>
            </DialogActions>
        </Dialog>
    );
};
function millisecondsToTime(ms) {
    const seconds = Math.floor((ms / 1000) % 60);
    const minutes = Math.floor((ms / (60 * 1000)) % 60);
    return `${minutes}m ${seconds}s`;
}
export default ImportXlsxModal;
zy-acs-flow/src/page/components/useExcelParse.js
New file
@@ -0,0 +1,77 @@
import * as XLSX from 'xlsx';
import { useCallback, useMemo, useRef, useState } from 'react';
export function useExcelParse({ batchSize = 10, processBatch, params }) {
    const importIdRef = useRef(0);
    const [importer, setImporter] = useState({ state: 'idle' });
    const reset = useCallback(() => {
        setImporter({ state: 'idle' });
        importIdRef.current += 1;
    }, []);
    const parseExcel = useCallback(async (file) => {
        if (!file) return;
        setImporter({ state: 'parsing' });
        const importId = importIdRef.current;
        try {
            const buf = await file.arrayBuffer();
            const wb = XLSX.read(buf, { type: 'array' });
            const sheet = wb.SheetNames[0];
            const rows = XLSX.utils.sheet_to_json(wb.Sheets[sheet], { defval: '' });
            setImporter({
                state: 'running',
                rowCount: rows.length,
                importCount: 0,
                errorCount: 0,
                remainingTime: null,
            });
            let totalTime = 0;
            for (let i = 0; i < rows.length; i += batchSize) {
                if (importIdRef.current !== importId) return;
                const batch = rows.slice(i, i + batchSize);
                try {
                    const start = Date.now();
                    await processBatch(batch, params);
                    totalTime += Date.now() - start;
                    const mean = totalTime / (i + batch.length);
                    setImporter((prev) =>
                        prev.state === 'running'
                            ? {
                                ...prev,
                                importCount: prev.importCount + batch.length,
                                remainingTime: mean * (rows.length - (prev.importCount + batch.length)),
                            }
                            : prev
                    );
                } catch (err) {
                    setImporter((prev) =>
                        prev.state === 'running'
                            ? {
                                ...prev,
                                errorCount: prev.errorCount + batch.length,
                                errorMsg: prev.errorMsg
                                    ? `${prev.errorMsg}\n${err?.message || String(err)}`
                                    : (err?.message || String(err)),
                            }
                            : prev
                    );
                }
            }
            setImporter((prev) =>
                prev.state === 'running' ? { ...prev, state: 'complete', remainingTime: null } : prev
            );
        } catch (error) {
            setImporter({ state: 'error', error });
        }
    }, [batchSize, processBatch, params]);
    return useMemo(() => ({ importer, parseExcel, reset }), [importer, parseExcel, reset]);
}
zy-acs-flow/src/page/integrationRecord/IntegrationRecordCreate.jsx
New file
@@ -0,0 +1,207 @@
import React, { useState, useRef, useEffect, useMemo } from "react";
import {
    CreateBase,
    useTranslate,
    TextInput,
    NumberInput,
    BooleanInput,
    DateInput,
    SaveButton,
    SelectInput,
    ReferenceInput,
    ReferenceArrayInput,
    AutocompleteInput,
    Toolbar,
    required,
    useDataProvider,
    useNotify,
    Form,
    useCreateController,
} from 'react-admin';
import {
    Dialog,
    DialogActions,
    DialogContent,
    DialogTitle,
    Stack,
    Grid,
    Box,
} from '@mui/material';
import DialogCloseButton from "../components/DialogCloseButton";
import StatusSelectInput from "../components/StatusSelectInput";
import MemoInput from "../components/MemoInput";
const IntegrationRecordCreate = (props) => {
    const { open, setOpen } = props;
    const translate = useTranslate();
    const notify = useNotify();
    const handleClose = (event, reason) => {
        if (reason !== "backdropClick") {
            setOpen(false);
        }
    };
    const handleSuccess = async (data) => {
        setOpen(false);
        notify('common.response.success');
    };
    const handleError = async (error) => {
        notify(error.message || 'common.response.fail', { type: 'error', messageArgs: { _: error.message } });
    };
    return (
        <>
            <CreateBase
                record={{}}
                transform={(data) => {
                    return data;
                }}
                mutationOptions={{ onSuccess: handleSuccess, onError: handleError }}
            >
                <Dialog
                    open={open}
                    onClose={handleClose}
                    aria-labelledby="form-dialog-title"
                    fullWidth
                    disableRestoreFocus
                    maxWidth="md"   // 'xs' | 'sm' | 'md' | 'lg' | 'xl'
                >
                    <Form>
                        <DialogTitle id="form-dialog-title" sx={{
                            position: 'sticky',
                            top: 0,
                            backgroundColor: 'background.paper',
                            zIndex: 1000
                        }}
                        >
                            {translate('create.title')}
                            <Box sx={{ position: 'absolute', top: 8, right: 8, zIndex: 1001 }}>
                                <DialogCloseButton onClose={handleClose} />
                            </Box>
                        </DialogTitle>
                        <DialogContent>
                            <Grid container rowSpacing={2} columnSpacing={2}>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <TextInput
                                        label="table.field.integrationRecord.uuid"
                                        source="uuid"
                                        parse={v => v}
                                        autoFocus
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <TextInput
                                        label="table.field.integrationRecord.namespace"
                                        source="namespace"
                                        parse={v => v}
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <TextInput
                                        label="table.field.integrationRecord.url"
                                        source="url"
                                        parse={v => v}
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <TextInput
                                        label="table.field.integrationRecord.appkey"
                                        source="appkey"
                                        parse={v => v}
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <TextInput
                                        label="table.field.integrationRecord.caller"
                                        source="caller"
                                        parse={v => v}
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <SelectInput
                                        label="table.field.integrationRecord.direction"
                                        source="direction"
                                        choices={[
                                            { id: 1, name: '被调用' },
                                            { id: 2, name: '调用外部' },
                                        ]}
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <TextInput
                                        label="table.field.integrationRecord.timestamp"
                                        source="timestamp"
                                        parse={v => v}
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <TextInput
                                        label="table.field.integrationRecord.clientIp"
                                        source="clientIp"
                                        parse={v => v}
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <TextInput
                                        label="table.field.integrationRecord.request"
                                        source="request"
                                        parse={v => v}
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <TextInput
                                        label="table.field.integrationRecord.response"
                                        source="response"
                                        parse={v => v}
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <TextInput
                                        label="table.field.integrationRecord.err"
                                        source="err"
                                        parse={v => v}
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <SelectInput
                                        label="table.field.integrationRecord.result"
                                        source="result"
                                        choices={[
                                            { id: 1, name: '成功' },
                                            { id: 0, name: '失败' },
                                        ]}
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <NumberInput
                                        label="table.field.integrationRecord.costMs"
                                        source="costMs"
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <StatusSelectInput />
                                </Grid>
                                <Grid item xs={12} display="flex" gap={1}>
                                    <Stack direction="column" spacing={1} width={'100%'}>
                                        <MemoInput />
                                    </Stack>
                                </Grid>
                            </Grid>
                        </DialogContent>
                        <DialogActions sx={{ position: 'sticky', bottom: 0, backgroundColor: 'background.paper', zIndex: 1000 }}>
                            <Toolbar sx={{ width: '100%', justifyContent: 'space-between' }}  >
                                <SaveButton />
                            </Toolbar>
                        </DialogActions>
                    </Form>
                </Dialog>
            </CreateBase>
        </>
    )
}
export default IntegrationRecordCreate;
zy-acs-flow/src/page/integrationRecord/IntegrationRecordEdit.jsx
New file
@@ -0,0 +1,180 @@
import React, { useState, useRef, useEffect, useMemo } from "react";
import {
    Edit,
    SimpleForm,
    FormDataConsumer,
    useTranslate,
    TextInput,
    NumberInput,
    BooleanInput,
    DateInput,
    SelectInput,
    ReferenceInput,
    ReferenceArrayInput,
    AutocompleteInput,
    SaveButton,
    Toolbar,
    Labeled,
    NumberField,
    required,
    useRecordContext,
    DeleteButton,
} from 'react-admin';
import { useWatch, useFormContext } from "react-hook-form";
import { Stack, Grid, Box, Typography } from '@mui/material';
import * as Common from '@/utils/common';
import { EDIT_MODE, REFERENCE_INPUT_PAGESIZE } from '@/config/setting';
import EditBaseAside from "../components/EditBaseAside";
import CustomerTopToolBar from "../components/EditTopToolBar";
import MemoInput from "../components/MemoInput";
import StatusSelectInput from "../components/StatusSelectInput";
const FormToolbar = () => {
    const { getValues } = useFormContext();
    return (
        <Toolbar sx={{ justifyContent: 'space-between' }}>
            <SaveButton />
            <DeleteButton mutationMode="optimistic" />
        </Toolbar>
    )
}
const IntegrationRecordEdit = () => {
    const translate = useTranslate();
    return (
        <Edit
            redirect="list"
            mutationMode={EDIT_MODE}
            actions={<CustomerTopToolBar />}
            aside={<EditBaseAside />}
        >
            <SimpleForm
                shouldUnregister
                warnWhenUnsavedChanges
                toolbar={<FormToolbar />}
                mode="onTouched"
                defaultValues={{}}
            // validate={(values) => { }}
            >
                <Grid container width={{ xs: '100%', xl: '80%' }} rowSpacing={3} columnSpacing={3}>
                    <Grid item xs={12} md={8}>
                        <Typography variant="h6" gutterBottom>
                            {translate('common.edit.title.main')}
                        </Typography>
                        <Stack direction='row' gap={2}>
                            <TextInput
                                label="table.field.integrationRecord.uuid"
                                source="uuid"
                                parse={v => v}
                                autoFocus
                            />
                        </Stack>
                        <Stack direction='row' gap={2}>
                            <TextInput
                                label="table.field.integrationRecord.namespace"
                                source="namespace"
                                parse={v => v}
                            />
                        </Stack>
                        <Stack direction='row' gap={2}>
                            <TextInput
                                label="table.field.integrationRecord.url"
                                source="url"
                                parse={v => v}
                            />
                        </Stack>
                        <Stack direction='row' gap={2}>
                            <TextInput
                                label="table.field.integrationRecord.appkey"
                                source="appkey"
                                parse={v => v}
                            />
                        </Stack>
                        <Stack direction='row' gap={2}>
                            <TextInput
                                label="table.field.integrationRecord.caller"
                                source="caller"
                                parse={v => v}
                            />
                        </Stack>
                        <Stack direction='row' gap={2}>
                            <SelectInput
                                label="table.field.integrationRecord.direction"
                                source="direction"
                                choices={[
                                    { id: 1, name: '被调用' },
                                    { id: 2, name: '调用外部' },
                                ]}
                                validate={required()}
                            />
                        </Stack>
                        <Stack direction='row' gap={2}>
                            <TextInput
                                label="table.field.integrationRecord.timestamp"
                                source="timestamp"
                                parse={v => v}
                            />
                        </Stack>
                        <Stack direction='row' gap={2}>
                            <TextInput
                                label="table.field.integrationRecord.clientIp"
                                source="clientIp"
                                parse={v => v}
                            />
                        </Stack>
                        <Stack direction='row' gap={2}>
                            <TextInput
                                label="table.field.integrationRecord.request"
                                source="request"
                                parse={v => v}
                            />
                        </Stack>
                        <Stack direction='row' gap={2}>
                            <TextInput
                                label="table.field.integrationRecord.response"
                                source="response"
                                parse={v => v}
                            />
                        </Stack>
                        <Stack direction='row' gap={2}>
                            <TextInput
                                label="table.field.integrationRecord.err"
                                source="err"
                                parse={v => v}
                            />
                        </Stack>
                        <Stack direction='row' gap={2}>
                            <SelectInput
                                label="table.field.integrationRecord.result"
                                source="result"
                                choices={[
                                    { id: 1, name: '成功' },
                                    { id: 0, name: '失败' },
                                ]}
                            />
                        </Stack>
                        <Stack direction='row' gap={2}>
                            <NumberInput
                                label="table.field.integrationRecord.costMs"
                                source="costMs"
                            />
                        </Stack>
                    </Grid>
                    <Grid item xs={12} md={4}>
                        <Typography variant="h6" gutterBottom>
                            {translate('common.edit.title.common')}
                        </Typography>
                        <StatusSelectInput />
                        <Box mt="2em" />
                        <MemoInput />
                    </Grid>
                </Grid>
            </SimpleForm>
        </Edit >
    )
}
export default IntegrationRecordEdit;
zy-acs-flow/src/page/integrationRecord/IntegrationRecordList.jsx
New file
@@ -0,0 +1,173 @@
import React, { useState, useRef, useEffect, useMemo, useCallback } from "react";
import { useNavigate } from 'react-router-dom';
import {
    List,
    DatagridConfigurable,
    SearchInput,
    TopToolbar,
    SelectColumnsButton,
    EditButton,
    FilterButton,
    CreateButton,
    ExportButton,
    BulkDeleteButton,
    WrapperField,
    useRecordContext,
    useTranslate,
    useNotify,
    useListContext,
    FunctionField,
    TextField,
    NumberField,
    DateField,
    BooleanField,
    ReferenceField,
    TextInput,
    DateTimeInput,
    DateInput,
    SelectInput,
    NumberInput,
    ReferenceInput,
    ReferenceArrayInput,
    AutocompleteInput,
    DeleteButton,
} from 'react-admin';
import { Box, Typography, Card, Stack } from '@mui/material';
import { styled } from '@mui/material/styles';
import IntegrationRecordCreate from "./IntegrationRecordCreate";
import IntegrationRecordPanel from "./IntegrationRecordPanel";
import EmptyDataLoader from "../components/EmptyDataLoader";
import MyCreateButton from "../components/MyCreateButton";
import MyExportButton from '../components/MyExportButton';
import PageDrawer from "../components/PageDrawer";
import MyField from "../components/MyField";
import { PAGE_DRAWER_WIDTH, OPERATE_MODE, DEFAULT_PAGE_SIZE } from '@/config/setting';
import * as Common from '@/utils/common';
const StyledDatagrid = styled(DatagridConfigurable)(({ theme }) => ({
    '& .css-1vooibu-MuiSvgIcon-root': {
        height: '.9em'
    },
    '& .RaDatagrid-row': {
        cursor: 'auto'
    },
    '& .column-name': {
    },
    '& .opt': {
        width: 200
    },
}));
const filters = [
    <SearchInput source="condition" alwaysOn />,
    <DateInput label='common.time.after' source="timeStart" alwaysOn />,
    <DateInput label='common.time.before' source="timeEnd" alwaysOn />,
    <TextInput source="uuid" label="table.field.integrationRecord.uuid" />,
    <TextInput source="namespace" label="table.field.integrationRecord.namespace" />,
    <TextInput source="url" label="table.field.integrationRecord.url" />,
    <TextInput source="appkey" label="table.field.integrationRecord.appkey" />,
    <TextInput source="caller" label="table.field.integrationRecord.caller" />,
    <SelectInput source="direction" label="table.field.integrationRecord.direction"
        choices={[
            { id: 1, name: '被调用' },
            { id: 2, name: '调用外部' },
        ]}
    />,
    <TextInput source="timestamp" label="table.field.integrationRecord.timestamp" />,
    <TextInput source="clientIp" label="table.field.integrationRecord.clientIp" />,
    <SelectInput source="result" label="table.field.integrationRecord.result"
        choices={[
            { id: 1, name: '成功' },
            { id: 0, name: '失败' },
        ]}
    />,
    <TextInput label="common.field.memo" source="memo" />,
]
const IntegrationRecordList = () => {
    const translate = useTranslate();
    const [createDialog, setCreateDialog] = useState(false);
    const [drawerVal, setDrawerVal] = useState(false);
    return (
        <Box display="flex">
            <List
                sx={{
                    flexGrow: 1,
                    transition: (theme) =>
                        theme.transitions.create(['all'], {
                            duration: theme.transitions.duration.enteringScreen,
                        }),
                    marginRight: !!drawerVal ? `${PAGE_DRAWER_WIDTH}px` : 0,
                }}
                title={"menu.integrationRecord"}
                empty={<EmptyDataLoader />}
                filters={filters}
                sort={{ field: "create_time", order: "desc" }}
                actions={(
                    <TopToolbar>
                        <FilterButton />
                        <MyCreateButton onClick={() => { setCreateDialog(true) }} />
                        <SelectColumnsButton preferenceKey='integrationRecord' />
                        <MyExportButton />
                    </TopToolbar>
                )}
                perPage={DEFAULT_PAGE_SIZE}
            >
                <StyledDatagrid
                    preferenceKey='integrationRecord'
                    bulkActionButtons={() => <BulkDeleteButton mutationMode={OPERATE_MODE} />}
                    rowClick={(id, resource, record) => false}
                    expand={() => <IntegrationRecordPanel />}
                    expandSingle={true}
                    omit={['id', 'createTime', 'createBy', 'memo']}
                >
                    <NumberField source="id" />
                    <TextField source="uuid" label="table.field.integrationRecord.uuid" />
                    <TextField source="namespace" label="table.field.integrationRecord.namespace" />
                    <TextField source="url" label="table.field.integrationRecord.url" />
                    <TextField source="appkey" label="table.field.integrationRecord.appkey" />
                    <TextField source="caller" label="table.field.integrationRecord.caller" />
                    <TextField source="direction$" label="table.field.integrationRecord.direction" sortable={false} />
                    <TextField source="timestamp" label="table.field.integrationRecord.timestamp" />
                    <TextField source="clientIp" label="table.field.integrationRecord.clientIp" />
                    <TextField source="request" label="table.field.integrationRecord.request" />
                    <TextField source="response" label="table.field.integrationRecord.response" />
                    <TextField source="err" label="table.field.integrationRecord.err" />
                    <TextField source="result$" label="table.field.integrationRecord.result" sortable={false} />
                    <NumberField source="costMs" label="table.field.integrationRecord.costMs" />
                    <ReferenceField source="updateBy" label="common.field.updateBy" reference="user" link={false} sortable={false}>
                        <TextField source="nickname" />
                    </ReferenceField>
                    <DateField source="updateTime" label="common.field.updateTime" showTime />
                    <ReferenceField source="createBy" label="common.field.createBy" reference="user" link={false} sortable={false}>
                        <TextField source="nickname" />
                    </ReferenceField>
                    <DateField source="createTime" label="common.field.createTime" showTime />
                    <BooleanField source="statusBool" label="common.field.status" sortable={false} />
                    <TextField source="memo" label="common.field.memo" sortable={false} />
                    <WrapperField cellClassName="opt" label="common.field.opt">
                        <EditButton sx={{ padding: '1px', fontSize: '.75rem' }} />
                        <DeleteButton sx={{ padding: '1px', fontSize: '.75rem' }} mutationMode={OPERATE_MODE} />
                    </WrapperField>
                </StyledDatagrid>
            </List>
            <IntegrationRecordCreate
                open={createDialog}
                setOpen={setCreateDialog}
            />
            <PageDrawer
                title='IntegrationRecord Detail'
                drawerVal={drawerVal}
                setDrawerVal={setDrawerVal}
            >
            </PageDrawer>
        </Box>
    )
}
export default IntegrationRecordList;
zy-acs-flow/src/page/integrationRecord/IntegrationRecordPanel.jsx
New file
@@ -0,0 +1,129 @@
import React, { useState, useRef, useEffect, useMemo } from "react";
import { Box, Card, CardContent, Grid, Typography, Tooltip } from '@mui/material';
import {
    useTranslate,
    useRecordContext,
} from 'react-admin';
import PanelTypography from "../components/PanelTypography";
import * as Common from '@/utils/common'
const IntegrationRecordPanel = () => {
    const record = useRecordContext();
    if (!record) return null;
    const translate = useTranslate();
    return (
        <>
            <Card sx={{ width: { xs: 300, sm: 500, md: 600, lg: 800 }, margin: 'auto' }}>
                <CardContent>
                    <Grid container spacing={2}>
                        <Grid item xs={12} sx={{ display: 'flex', justifyContent: 'space-between' }}>
                            <Typography variant="h6" gutterBottom align="left" sx={{
                                maxWidth: { xs: '100px', sm: '180px', md: '260px', lg: '360px' },
                                whiteSpace: 'nowrap',
                                overflow: 'hidden',
                                textOverflow: 'ellipsis',
                            }}>
                                {Common.camelToPascalWithSpaces(translate('table.field.integrationRecord.namespace'))}: {record.namespace}
                            </Typography>
                            {/*  inherit, primary, secondary, textPrimary, textSecondary, error */}
                            <Typography variant="h6" gutterBottom align="right" >
                                ID: {record.id}
                            </Typography>
                        </Grid>
                    </Grid>
                    <Grid container spacing={2}>
                        <Grid item xs={12} container alignContent="flex-end">
                            <Typography variant="caption" color="textSecondary" sx={{ wordWrap: 'break-word', wordBreak: 'break-all' }}>
                                {Common.camelToPascalWithSpaces(translate('common.field.memo'))}:{record.memo}
                            </Typography>
                        </Grid>
                    </Grid>
                    <Box height={20}>&nbsp;</Box>
                    <Grid container spacing={2}>
                        <Grid item xs={6}>
                            <PanelTypography
                                title="table.field.integrationRecord.uuid"
                                property={record.uuid}
                            />
                        </Grid>
                        <Grid item xs={6}>
                            <PanelTypography
                                title="table.field.integrationRecord.namespace"
                                property={record.namespace}
                            />
                        </Grid>
                        <Grid item xs={6}>
                            <PanelTypography
                                title="table.field.integrationRecord.url"
                                property={record.url}
                            />
                        </Grid>
                        <Grid item xs={6}>
                            <PanelTypography
                                title="table.field.integrationRecord.appkey"
                                property={record.appkey}
                            />
                        </Grid>
                        <Grid item xs={6}>
                            <PanelTypography
                                title="table.field.integrationRecord.caller"
                                property={record.caller}
                            />
                        </Grid>
                        <Grid item xs={6}>
                            <PanelTypography
                                title="table.field.integrationRecord.direction"
                                property={record.direction$}
                            />
                        </Grid>
                        <Grid item xs={6}>
                            <PanelTypography
                                title="table.field.integrationRecord.timestamp"
                                property={record.timestamp}
                            />
                        </Grid>
                        <Grid item xs={6}>
                            <PanelTypography
                                title="table.field.integrationRecord.clientIp"
                                property={record.clientIp}
                            />
                        </Grid>
                        <Grid item xs={6}>
                            <PanelTypography
                                title="table.field.integrationRecord.request"
                                property={record.request}
                            />
                        </Grid>
                        <Grid item xs={6}>
                            <PanelTypography
                                title="table.field.integrationRecord.response"
                                property={record.response}
                            />
                        </Grid>
                        <Grid item xs={6}>
                            <PanelTypography
                                title="table.field.integrationRecord.err"
                                property={record.err}
                            />
                        </Grid>
                        <Grid item xs={6}>
                            <PanelTypography
                                title="table.field.integrationRecord.result"
                                property={record.result$}
                            />
                        </Grid>
                        <Grid item xs={6}>
                            <PanelTypography
                                title="table.field.integrationRecord.costMs"
                                property={record.costMs}
                            />
                        </Grid>
                    </Grid>
                </CardContent>
            </Card >
        </>
    );
};
export default IntegrationRecordPanel;
zy-acs-flow/src/page/integrationRecord/index.jsx
New file
@@ -0,0 +1,18 @@
import React, { useState, useRef, useEffect, useMemo } from "react";
import {
    ListGuesser,
    EditGuesser,
    ShowGuesser,
} from "react-admin";
import IntegrationRecordList from "./IntegrationRecordList";
import IntegrationRecordEdit from "./IntegrationRecordEdit";
export default {
    list: IntegrationRecordList,
    edit: IntegrationRecordEdit,
    show: ShowGuesser,
    recordRepresentation: (record) => {
        return `${record.namespace}`
    }
};
zy-acs-flow/src/page/staReserve/StaReserveList.jsx
@@ -36,7 +36,7 @@
import { styled } from '@mui/material/styles';
import StaReserveCreate from "./StaReserveCreate";
import StaReservePanel from "./StaReservePanel";
import EmptyData from "../components/EmptyData";
import EmptyDataLoader from "../components/EmptyDataLoader";
import MyCreateButton from "../components/MyCreateButton";
import MyExportButton from '../components/MyExportButton';
import PageDrawer from "../components/PageDrawer";
@@ -131,7 +131,7 @@
                    marginRight: !!drawerVal ? `${PAGE_DRAWER_WIDTH}px` : 0,
                }}
                title={"menu.staReserve"}
                empty={<EmptyData onClick={() => { setCreateDialog(true) }} />}
                empty={<EmptyDataLoader />}
                filters={filters}
                sort={{ field: "create_time", order: "desc" }}
                actions={(
zy-acs-flow/src/page/task/TaskList.jsx
@@ -35,7 +35,7 @@
    useNotify,
    useRefresh,
} from 'react-admin';
import { Box, Chip, Card, Stack } from '@mui/material';
import { Box, Chip, Card, Stack, LinearProgress } from '@mui/material';
import { styled } from '@mui/material/styles';
import TaskCreate from "./TaskCreate";
import TaskPanel from "./TaskPanel";
@@ -70,6 +70,20 @@
        width: 200
    },
}));
const UPLINK_STATUS_CHOICES = [
    { id: 'NONE', name: 'page.task.enums.uplinkSts.NONE', color: 'default' },
    { id: 'PENDING', name: 'page.task.enums.uplinkSts.PENDING', color: 'warning' },
    { id: 'SENDING', name: 'page.task.enums.uplinkSts.SENDING', color: 'info' },
    { id: 'SUCCESS', name: 'page.task.enums.uplinkSts.SUCCESS', color: 'success' },
    { id: 'FAILED', name: 'page.task.enums.uplinkSts.FAILED', color: 'error' },
    { id: 'SKIPPED', name: 'page.task.enums.uplinkSts.SKIPPED', color: 'secondary' },
];
const UPLINK_STATUS_MAP = UPLINK_STATUS_CHOICES.reduce((acc, item) => {
    acc[item.id] = item;
    return acc;
}, {});
const filters = [
    // <SearchInput source="condition" alwaysOn />,
@@ -115,6 +129,7 @@
        <AutocompleteInput label="table.field.task.destCode" optionText="data" filterToQuery={(val) => ({ data: val })} />
    </ReferenceInput>,
    <TextInput source="emptyMk" label="table.field.task.emptyMk" />,
    <SelectInput source="uplinkSts" label="table.field.task.uplinkSts" choices={UPLINK_STATUS_CHOICES} />,
    <TextInput source="zpallet" label="table.field.task.zpallet" />,
    // <TextInput source="phase" label="table.field.task.phase" />,
    <TextInput source="errDesc" label="table.field.task.errDesc" />,
@@ -130,12 +145,12 @@
    />,
]
const TaskList = () => {
const TaskListContent = () => {
    const translate = useTranslate();
    const notify = useNotify();
    const refresh = useRefresh();
    const [createDialog, setCreateDialog] = useState(false);
    const [drawerVal, setDrawerVal] = useState(false);
    const { isLoading } = useListContext();
    const [taskStsByComplete, setTaskStsByComplete] = useState(null);
    const [taskStsByCancel, setTaskStsByCancel] = useState(null);
@@ -160,6 +175,7 @@
            setTaskStsByCancel(res.data.data[0]?.id);
        })
    }, []);
    const onComplete = (taskId) => {
        request.get("/task/complete/" + taskId).then(res => {
@@ -190,6 +206,163 @@
    }
    return (
        <Box sx={{ position: 'relative' }}>
            {isLoading && (
                <LinearProgress
                    sx={{
                        height: "2px",
                        position: 'absolute',
                        top: 0,
                        left: 0,
                        right: 0,
                    }}
                />
            )}
            <StyledDatagrid
                preferenceKey='task'
                bulkActionButtons={false}
                rowClick={(id, resource, record) => false}
                expand={() => <TaskPanel />}
                expandSingle={true}
                omit={['id', 'uuid', 'startTime', 'endTime', 'errTime', 'emptyMk', 'zpallet',
                    'oriSta', 'oriLoc', 'oriCode', 'destSta', 'destLoc', 'destCode', 'errDesc',
                    'updateTime', 'updateBy', 'ioTime', 'createBy', 'statusBool', 'memo']}
            >
                <NumberField source="id" />
                <TextField source="uuid" label="table.field.task.uuid" sortable={false} />
                <TextField source="seqNum" label="table.field.task.seqNum" sortable={false} />
                <ReferenceField source="busId" label="table.field.task.busId" reference="bus" link={false} sortable={false}>
                    <TextField source="busNo" />
                </ReferenceField>
                <ReferenceField source="agvId" label="table.field.task.agvId" reference="agv" link={false} sortable={false}>
                    <TextField source="uuid" />
                </ReferenceField>
                {/* <TextField source="name" label="table.field.task.name" /> */}
                <ReferenceField source="taskType" label="table.field.task.taskType" reference="taskType" link={false} sortable={false}>
                    <TextField source="name" />
                </ReferenceField>
                {/* <ReferenceField source="taskSts" label="table.field.task.taskSts" reference="taskSts" link={false} sortable={false}>
                        <TextField source="name" />
                    </ReferenceField> */}
                <ReferenceField source="taskSts" label="table.field.task.taskSts" reference="taskSts" link={false} sortable={true}>
                    <FunctionField render={record => (
                        <Chip
                            label={record.name}
                            variant="outlined"
                            size="small"
                            color={getTaskStsChipColor(record.name)}
                            sx={{ fontSize: '0.75rem' }}
                        />
                    )} />
                    {/* <TextField source="name" /> */}
                </ReferenceField>
                <DateField source="ioTime" label="table.field.task.ioTime" showTime sortable={false} />
                <DateField source="startTime" label="table.field.task.startTime" showTime sortable={false} />
                <DateField source="endTime" label="table.field.task.endTime" showTime sortable={false} />
                <DateField source="errTime" label="table.field.task.errTime" showTime sortable={false} />
                <ReferenceField source="oriSta" label="table.field.task.oriSta" reference="sta" link={false} sortable={false}>
                    <TextField source="staNo" />
                </ReferenceField>
                <ReferenceField source="oriLoc" label="table.field.task.oriLoc" reference="loc" link={false} sortable={false}>
                    <TextField source="locNo" />
                </ReferenceField>
                <ReferenceField source="oriCode" label="table.field.task.oriCode" reference="code" link={false} sortable={false}>
                    <TextField source="data" />
                </ReferenceField>
                <ReferenceField source="destSta" label="table.field.task.destSta" reference="sta" link={false} sortable={false}>
                    <TextField source="staNo" />
                </ReferenceField>
                <ReferenceField source="destLoc" label="table.field.task.destLoc" reference="loc" link={false} sortable={false}>
                    <TextField source="locNo" />
                </ReferenceField>
                <ReferenceField source="destCode" label="table.field.task.destCode" reference="code" link={false} sortable={false}>
                    <TextField source="data" />
                </ReferenceField>
                <TextField source="oriDesc" label="table.field.task.oriDesc" sortable={false} />
                <TextField source="destDesc" label="table.field.task.destDesc" sortable={false} />
                <NumberField source="priority" label="table.field.task.priority" sortable={false} />
                <FunctionField
                    label="table.field.task.uplinkSts"
                    sortable={false}
                    render={record => {
                        const meta = UPLINK_STATUS_MAP[record?.uplinkSts];
                        return (
                            <Chip
                                label={meta?.name ? translate(meta.name) : translate('common.enums.na')}
                                variant="outlined"
                                size="small"
                                color={meta?.color ?? 'default'}
                                sx={{ fontSize: '0.75rem' }}
                            />
                        );
                    }}
                />
                <TextField source="emptyMk" label="table.field.task.emptyMk" sortable={false} />
                <TextField source="zpallet" label="table.field.task.zpallet" sortable={false} />
                <TextField source="errDesc" label="table.field.task.errDesc" sortable={false} />
                <ReferenceField source="updateBy" label="common.field.updateBy" reference="user" link={false} sortable={false}>
                    <TextField source="nickname" />
                </ReferenceField>
                <DateField source="updateTime" label="common.field.updateTime" showTime />
                <ReferenceField source="createBy" label="common.field.createBy" reference="user" link={false} sortable={false}>
                    <TextField source="nickname" />
                </ReferenceField>
                <DateField source="createTime" label="common.field.createTime" showTime sortable={false} />
                <BooleanField source="statusBool" label="common.field.status" sortable={false} />
                <TextField source="memo" label="common.field.memo" sortable={false} />
                <FunctionField label="common.field.opt" cellClassName="opt" render={record => (
                    (record.taskSts !== taskStsByComplete && record.taskSts !== taskStsByCancel) && (
                        <>
                            <ConfirmButton
                                label="common.action.complete"
                                size="small"
                                color="primary"
                                startIcon={<CheckIcon />}
                                sx={{
                                    padding: '1px',
                                    fontSize: '.75rem',
                                    '& .MuiButton-startIcon': {
                                        marginRight: '2px'
                                    },
                                    mr: 1
                                }}
                                data={record.seqNum}
                                onConfirm={() => {
                                    onComplete(record.id);
                                }}
                            />
                            <ConfirmButton
                                label="ra.action.cancel"
                                size="small"
                                color="error"
                                startIcon={<ClearIcon />}
                                sx={{
                                    padding: '1px',
                                    fontSize: '.75rem',
                                    '& .MuiButton-startIcon': {
                                        marginRight: '1.5px'
                                    },
                                }}
                                data={record.seqNum}
                                onConfirm={() => {
                                    onCancel(record.id);
                                }}
                            />
                        </>
                    )
                )} />
            </StyledDatagrid>
        </Box>
    )
}
const TaskList = () => {
    const [createDialog, setCreateDialog] = useState(false);
    const [drawerVal, setDrawerVal] = useState(false);
    return (
        <Box display="flex">
            <List
                sx={{
@@ -216,125 +389,7 @@
                perPage={25}
                aside={<TaskListAside />}
            >
                <StyledDatagrid
                    preferenceKey='task'
                    bulkActionButtons={false}
                    rowClick={(id, resource, record) => false}
                    expand={() => <TaskPanel />}
                    expandSingle={true}
                    omit={['id', 'uuid', 'startTime', 'endTime', 'errTime', 'emptyMk', 'zpallet',
                        'oriSta', 'oriLoc', 'oriCode', 'destSta', 'destLoc', 'destCode', 'errDesc',
                        'updateTime', 'updateBy', 'ioTime', 'createBy', 'statusBool', 'memo']}
                >
                    <NumberField source="id" />
                    <TextField source="uuid" label="table.field.task.uuid" sortable={false} />
                    <TextField source="seqNum" label="table.field.task.seqNum" sortable={false} />
                    <ReferenceField source="busId" label="table.field.task.busId" reference="bus" link={false} sortable={false}>
                        <TextField source="busNo" />
                    </ReferenceField>
                    <ReferenceField source="agvId" label="table.field.task.agvId" reference="agv" link={false} sortable={false}>
                        <TextField source="uuid" />
                    </ReferenceField>
                    {/* <TextField source="name" label="table.field.task.name" /> */}
                    <ReferenceField source="taskType" label="table.field.task.taskType" reference="taskType" link={false} sortable={false}>
                        <TextField source="name" />
                    </ReferenceField>
                    {/* <ReferenceField source="taskSts" label="table.field.task.taskSts" reference="taskSts" link={false} sortable={false}>
                        <TextField source="name" />
                    </ReferenceField> */}
                    <ReferenceField source="taskSts" label="table.field.task.taskSts" reference="taskSts" link={false} sortable={true}>
                        <FunctionField render={record => (
                            <Chip
                                label={record.name}
                                variant="outlined"
                                size="small"
                                color={getTaskStsChipColor(record.name)}
                                sx={{ fontSize: '0.75rem' }}
                            />
                        )} />
                        {/* <TextField source="name" /> */}
                    </ReferenceField>
                    <DateField source="ioTime" label="table.field.task.ioTime" showTime sortable={false} />
                    <DateField source="startTime" label="table.field.task.startTime" showTime sortable={false} />
                    <DateField source="endTime" label="table.field.task.endTime" showTime sortable={false} />
                    <DateField source="errTime" label="table.field.task.errTime" showTime sortable={false} />
                    <ReferenceField source="oriSta" label="table.field.task.oriSta" reference="sta" link={false} sortable={false}>
                        <TextField source="staNo" />
                    </ReferenceField>
                    <ReferenceField source="oriLoc" label="table.field.task.oriLoc" reference="loc" link={false} sortable={false}>
                        <TextField source="locNo" />
                    </ReferenceField>
                    <ReferenceField source="oriCode" label="table.field.task.oriCode" reference="code" link={false} sortable={false}>
                        <TextField source="data" />
                    </ReferenceField>
                    <ReferenceField source="destSta" label="table.field.task.destSta" reference="sta" link={false} sortable={false}>
                        <TextField source="staNo" />
                    </ReferenceField>
                    <ReferenceField source="destLoc" label="table.field.task.destLoc" reference="loc" link={false} sortable={false}>
                        <TextField source="locNo" />
                    </ReferenceField>
                    <ReferenceField source="destCode" label="table.field.task.destCode" reference="code" link={false} sortable={false}>
                        <TextField source="data" />
                    </ReferenceField>
                    <TextField source="oriDesc" label="table.field.task.oriDesc" sortable={false} />
                    <TextField source="destDesc" label="table.field.task.destDesc" sortable={false} />
                    <NumberField source="priority" label="table.field.task.priority" sortable={false} />
                    <TextField source="emptyMk" label="table.field.task.emptyMk" sortable={false} />
                    <TextField source="zpallet" label="table.field.task.zpallet" sortable={false} />
                    <TextField source="errDesc" label="table.field.task.errDesc" sortable={false} />
                    <ReferenceField source="updateBy" label="common.field.updateBy" reference="user" link={false} sortable={false}>
                        <TextField source="nickname" />
                    </ReferenceField>
                    <DateField source="updateTime" label="common.field.updateTime" showTime />
                    <ReferenceField source="createBy" label="common.field.createBy" reference="user" link={false} sortable={false}>
                        <TextField source="nickname" />
                    </ReferenceField>
                    <DateField source="createTime" label="common.field.createTime" showTime sortable={false} />
                    <BooleanField source="statusBool" label="common.field.status" sortable={false} />
                    <TextField source="memo" label="common.field.memo" sortable={false} />
                    <FunctionField label="common.field.opt" cellClassName="opt" render={record => (
                        (record.taskSts !== taskStsByComplete && record.taskSts !== taskStsByCancel) && (
                            <>
                                <ConfirmButton
                                    label="common.action.complete"
                                    size="small"
                                    color="primary"
                                    startIcon={<CheckIcon />}
                                    sx={{
                                        padding: '1px',
                                        fontSize: '.75rem',
                                        '& .MuiButton-startIcon': {
                                            marginRight: '2px'
                                        },
                                        mr: 1
                                    }}
                                    data={record.seqNum}
                                    onConfirm={() => {
                                        onComplete(record.id);
                                    }}
                                />
                                <ConfirmButton
                                    label="ra.action.cancel"
                                    size="small"
                                    color="error"
                                    startIcon={<ClearIcon />}
                                    sx={{
                                        padding: '1px',
                                        fontSize: '.75rem',
                                        '& .MuiButton-startIcon': {
                                            marginRight: '1.5px'
                                        },
                                    }}
                                    data={record.seqNum}
                                    onConfirm={() => {
                                        onCancel(record.id);
                                    }}
                                />
                            </>
                        )
                    )} />
                </StyledDatagrid>
                <TaskListContent drawerVal={drawerVal} />
            </List>
            <TaskCreate
                open={createDialog}
zy-acs-flow/src/page/task/TaskListAside.jsx
@@ -51,7 +51,7 @@
                mt: 8,
                alignSelf: 'flex-start',
                border: theme[0] === 'light' && '1px solid #e0e0e3',
                width: 255
                width: 210
            }}
        >
            <CardContent>
zy-acs-manager/src/main/java/com/zy/acs/manager/common/CodeBuilder.java
@@ -22,8 +22,8 @@
//        generator.username="sa";priority
//        generator.password="Zoneyung@zy56$";
        generator.table="man_lane";
        generator.tableDesc="Lane";
        generator.table="man_integration_record";
        generator.tableDesc="Integration Record";
        generator.packagePath="com.zy.acs.manager.manager";
        generator.build();
zy-acs-manager/src/main/java/com/zy/acs/manager/common/config/ConveyorProperties.java
@@ -1,5 +1,6 @@
package com.zy.acs.manager.common.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
@@ -10,6 +11,7 @@
 * è¾“送线plc系统配置
 * Created by luxiaotao on 2018/10/15
 */
@Data
@Configuration
@ConfigurationProperties(prefix = "convey-plc")
public class ConveyorProperties {
@@ -28,27 +30,7 @@
    private Integer port;
    public static String getHostName() {
        return HOST_NAME;
    }
    // millisecond
    private Integer timeout = 15 * 1000;
    public static void setHostName(String hostName) {
        HOST_NAME = hostName;
    }
    public String getHost() {
        return host;
    }
    public void setHost(String host) {
        this.host = host;
    }
    public Integer getPort() {
        return port;
    }
    public void setPort(Integer port) {
        this.port = port;
    }
}
zy-acs-manager/src/main/java/com/zy/acs/manager/common/config/UplinkProperties.java
New file
@@ -0,0 +1,40 @@
package com.zy.acs.manager.common.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import java.net.InetAddress;
import java.net.UnknownHostException;
/**
 * ä¸Šæ¸¸æœåŠ¡ç³»ç»Ÿé…ç½®
 * Created by luxiaotao on 2018/10/15
 */
@Data
@Configuration
@ConfigurationProperties(prefix = "uplink")
public class UplinkProperties {
    public static String HOST_NAME;
    static {
        try {
            HOST_NAME = InetAddress.getLocalHost().getHostName();
        } catch (UnknownHostException e) {
            System.err.println("find hostname err");
        }
    }
    private Boolean enabled;
    private String host;
    private Integer port;
    private String url;
    // millisecond
    private Integer timeout = 60 * 1000;
}
zy-acs-manager/src/main/java/com/zy/acs/manager/common/constant/Constants.java
@@ -6,8 +6,12 @@
 */
public class Constants {
    public static final String UPLINK = "UPLINK";
    public static final String HANDLE = "HANDLE";
    public static final String LIMIT_ONE = "limit 1";
    /**
     * é»˜è®¤æˆåŠŸç 
     */
zy-acs-manager/src/main/java/com/zy/acs/manager/common/domain/TaskBoolDto.java
New file
@@ -0,0 +1,28 @@
package com.zy.acs.manager.common.domain;
import lombok.Data;
@Data
public class TaskBoolDto {
    private String taskNo;
    private Boolean success;
    private String msg;
    public TaskBoolDto() {
    }
    public TaskBoolDto(String taskNo, Boolean success) {
        this.taskNo = taskNo;
        this.success = success;
    }
    public TaskBoolDto(String taskNo, Boolean success, String msg) {
        this.taskNo = taskNo;
        this.success = success;
        this.msg = msg;
    }
}
zy-acs-manager/src/main/java/com/zy/acs/manager/common/domain/TaskDto.java
@@ -8,7 +8,7 @@
@Data
public class TaskDto {
    private String seqNum;
    private String taskNo;
    private String oriLoc;
zy-acs-manager/src/main/java/com/zy/acs/manager/common/utils/ExcelUtil.java
@@ -2,10 +2,10 @@
import com.zy.acs.framework.common.Cools;
import io.swagger.annotations.ApiModelProperty;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@@ -24,19 +24,24 @@
    public static void build(Workbook workbook, HttpServletResponse response) {
        response.reset();
        Http.cors(response);
        response.setContentType("application/octet-stream; charset=utf-8");
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet; charset=utf-8");
        try {
            response.setHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode("export", "UTF-8"));
            response.setHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode("export.xlsx", "UTF-8"));
            workbook.write(response.getOutputStream());
            workbook.close();
        } catch (IOException ignore) {}
    }
    public static <T> Workbook create(List<T> list, Class<T> clz) {
        HSSFWorkbook workbook = new HSSFWorkbook();
        XSSFWorkbook workbook = new XSSFWorkbook();
        Sheet sheet = workbook.createSheet(clz.getSimpleName());
        Row header = sheet.createRow(0);
        Field[] fields = Cools.getAllFields(clz);
        int headerIdx = 0;
        for (Field field : fields) {
            if (Modifier.isFinal(field.getModifiers())
@@ -44,6 +49,7 @@
                    || Modifier.isTransient(field.getModifiers())) {
                continue;
            }
            String memo = "Undefined";
            if (field.isAnnotationPresent(ApiModelProperty.class)) {
                memo = field.getAnnotation(ApiModelProperty.class).value();
@@ -83,7 +89,7 @@
            }
        }
        for (int i = 0; i <= fields.length; i++) {
        for (int i = 0; i <= headerIdx; i++) {
            sheet.autoSizeColumn(i);
        }
zy-acs-manager/src/main/java/com/zy/acs/manager/common/utils/HttpGo.java
@@ -264,6 +264,16 @@
        }
    }
    // ======================== tools ========================
    public String buildUrl(String host, Integer port, String path) {
        return buildUrl(host, port, path, false);
    }
    public String buildUrl(String host, Integer port, String path, boolean ssl) {
        String p = (path == null) ? "" : (path.startsWith("/") ? path : ("/" + path));
        return (ssl ? "https" : "http") + "://" + host + ":" + port + p;
    }
    // ===================== Demo (main) =====================
    public static void main(String[] args) throws Exception {
zy-acs-manager/src/main/java/com/zy/acs/manager/core/HandlerController.java
@@ -9,6 +9,7 @@
import com.zy.acs.framework.common.R;
import com.zy.acs.framework.common.SnowflakeIdWorker;
import com.zy.acs.manager.common.annotation.OperationLog;
import com.zy.acs.manager.common.config.UplinkProperties;
import com.zy.acs.manager.common.domain.param.HandlerPublishParam;
import com.zy.acs.manager.common.exception.BusinessException;
import com.zy.acs.manager.core.service.*;
@@ -64,7 +65,7 @@
    @Autowired
    private StaService staService;
    @Autowired
    private TrafficService trafficService;
    private UplinkProperties uplinkProperties;
    @Autowired
    private ThreadPoolRegulator threadPoolRegulator;
    @Autowired
@@ -164,13 +165,13 @@
        Task task = new Task();
        task.setAgvId(agv.getId());
        task.setUuid(String.valueOf(snowflakeIdWorker.nextId()).substring(3));
        List<Task> lastTasks = taskService.list(new LambdaQueryWrapper<Task>().orderByDesc(Task::getId));
//        List<Task> lastTasks = taskService.list(new LambdaQueryWrapper<Task>().orderByDesc(Task::getId));
//        task.setSeqNum(Utils.generateSeqNum(Cools.isEmpty(lastTasks)?null:lastTasks.get(0).getSeqNum()));
        task.setSeqNum(Base62.encode(snowflakeIdWorker.nextId()));
        task.setTaskType(param.getTaskMode().val());
        task.setTaskSts(TaskStsType.WAITING.val());
        task.setUplinkSts(uplinkProperties.getEnabled() ? TaskUplinkStateType.PENDING.toString() : TaskUplinkStateType.SKIPPED.toString());
        task.setPriority(999);
        task.setIoTime(now);
        task.setStartTime(now);
zy-acs-manager/src/main/java/com/zy/acs/manager/core/integrate/conveyor/ConveyorController.java
@@ -9,7 +9,6 @@
import com.zy.acs.manager.manager.entity.Task;
import com.zy.acs.manager.manager.enums.StatusType;
import com.zy.acs.manager.manager.service.StaService;
import com.zy.acs.manager.system.controller.BaseController;
import io.swagger.annotations.Api;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
@@ -23,7 +22,7 @@
@Api(tags = "Open Api")
@RestController
@RequestMapping("/api/open")
public class ConveyorController extends BaseController {
public class ConveyorController {
    @Autowired
    private StaService staService;
zy-acs-manager/src/main/java/com/zy/acs/manager/core/integrate/conveyor/SiemensConveyorStationService.java
@@ -33,20 +33,22 @@
    @PostConstruct
    public void init() {
        int timeoutSeconds = conveyorProperties.getTimeout() / 1000;
        this.http = HttpGo.builder()
                .connectTimeout(Duration.ofSeconds(8))
                .readTimeout(Duration.ofSeconds(15))
                .connectTimeout(Duration.ofSeconds(timeoutSeconds))
                .readTimeout(Duration.ofSeconds(timeoutSeconds))
//                .defaultHeader("User-Agent", "HttpGo/1.0")
                // .trustAllSsl(true) // ONLY if you really need it (self-signed internal)
//                .trustAllSsl(true) // ONLY if you really need it (self-signed internal)
                .build();
    }
    @Override
    public boolean allowAgvWork(Sta sta, Task task, Segment seg, StaReserveType type) {
        final String staNo = sta.getStaNo();
        // url
        String url = this.buildUrl("/cv/station/query");
        String url = this.http.buildUrl(conveyorProperties.getHost(), conveyorProperties.getPort(), "/cv/station/query");
        // headers
        Map<String, String> headers = new HashMap<>();
        // params
@@ -106,16 +108,7 @@
        return true;
    }
    private String buildUrl(String path) {
        String host = conveyorProperties.getHost();
        Integer port = conveyorProperties.getPort();
        String p = (path == null) ? "" : (path.startsWith("/") ? path : ("/" + path));
        return "http://" + host + ":" + port + p;
    }
    private HttpResult<List<ConveyorStaDto>> postForResult(String url
            , Map<String, String> headers, Map<String, Object> params) throws Exception {
    private HttpResult<List<ConveyorStaDto>> postForResult(String url, Map<String, String> headers, Map<String, Object> params) throws Exception {
        String json = JSON.toJSONString(params);
        HttpGo.HttpResponse response = this.http.postJson(url, headers, json);
zy-acs-manager/src/main/java/com/zy/acs/manager/core/integrate/dto/OpenBusCancelParam.java
New file
@@ -0,0 +1,18 @@
package com.zy.acs.manager.core.integrate.dto;
import lombok.Data;
import java.util.ArrayList;
import java.util.List;
/**
 * Created by vincent on 2023/6/12
 */
@Data
public class OpenBusCancelParam {
    private String batchNo;
    private List<String> tasks = new ArrayList<>();
}
zy-acs-manager/src/main/java/com/zy/acs/manager/core/integrate/dto/OpenBusCancelResult.java
New file
@@ -0,0 +1,19 @@
package com.zy.acs.manager.core.integrate.dto;
import com.zy.acs.manager.common.domain.TaskBoolDto;
import lombok.Data;
import java.util.ArrayList;
import java.util.List;
/**
 * Created by vincent on 2023/6/12
 */
@Data
public class OpenBusCancelResult {
    private String batchNo;
    private List<TaskBoolDto> tasks = new ArrayList<>();
}
zy-acs-manager/src/main/java/com/zy/acs/manager/core/integrate/dto/OpenBusSubmitParam.java
File was renamed from zy-acs-manager/src/main/java/com/zy/acs/manager/manager/controller/param/OpenBusSubmitParam.java
@@ -1,4 +1,4 @@
package com.zy.acs.manager.manager.controller.param;
package com.zy.acs.manager.core.integrate.dto;
import com.zy.acs.manager.common.domain.TaskDto;
import lombok.Data;
@@ -12,8 +12,8 @@
@Data
public class OpenBusSubmitParam {
    private String batch;
    private String batchNo;
    private List<TaskDto> taskList = new ArrayList<>();
    private List<TaskDto> tasks = new ArrayList<>();
}
zy-acs-manager/src/main/java/com/zy/acs/manager/core/integrate/dto/TaskUplinkParam.java
New file
@@ -0,0 +1,14 @@
package com.zy.acs.manager.core.integrate.dto;
import lombok.Data;
@Data
public class TaskUplinkParam {
    private String batchNo;
    private String taskNo;
    private Long timestamp;
}
zy-acs-manager/src/main/java/com/zy/acs/manager/core/integrate/wms/OpenController.java
@@ -1,13 +1,22 @@
package com.zy.acs.manager.core.integrate.wms;
import com.zy.acs.manager.common.annotation.OperationLog;
import com.zy.acs.framework.common.Cools;
import com.zy.acs.framework.common.R;
import com.zy.acs.manager.system.controller.BaseController;
import com.zy.acs.manager.common.annotation.OperationLog;
import com.zy.acs.manager.common.constant.Constants;
import com.zy.acs.manager.common.domain.TaskBoolDto;
import com.zy.acs.manager.core.integrate.dto.OpenBusCancelParam;
import com.zy.acs.manager.core.integrate.dto.OpenBusCancelResult;
import com.zy.acs.manager.core.integrate.dto.OpenBusSubmitParam;
import com.zy.acs.manager.core.service.MainService;
import com.zy.acs.manager.manager.controller.param.OpenBusSubmitParam;
import com.zy.acs.manager.manager.entity.Bus;
import com.zy.acs.manager.manager.entity.Task;
import com.zy.acs.manager.manager.enums.TaskStsType;
import com.zy.acs.manager.manager.service.BusService;
import com.zy.acs.manager.manager.service.TaskService;
import io.swagger.annotations.Api;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
@@ -16,20 +25,60 @@
/**
 * Created by vincent on 2023/6/12
 */
@Slf4j
@Api(tags = "Open Api")
@RestController
@RequestMapping("/api/open")
public class OpenController extends BaseController {
public class OpenController {
    @Autowired
    private MainService mainService;
    @Autowired
    private TaskService taskService;
    @Autowired
    private BusService busService;
//    @PreAuthorize("hasAuthority('open:bus:submit')")
    @PostMapping("/bus/submit")
    @OperationLog("generate task from open api")
    public R save(@RequestBody OpenBusSubmitParam param) {
    public R submit(@RequestBody OpenBusSubmitParam param) {
        mainService.generateBusAndTask(param, null);
        return R.ok("generate tasks success");
    }
    @PostMapping("/task/cancel")
    @OperationLog("cancel task from open api")
    public R cancel(@RequestBody OpenBusCancelParam param) {
        if (Cools.isEmpty(param.getBatchNo())) {
            return R.error("batchNo is empty");
        }
        if (Cools.isEmpty(param.getTasks())) {
            return R.error("tasks is empty");
        }
        Bus bus = busService.selectByBusNo(param.getBatchNo());
        if (null == bus) {
            return R.error("batchNo is not exist");
        }
        OpenBusCancelResult result = new OpenBusCancelResult();
        result.setBatchNo(param.getBatchNo());
        for (String taskNo : param.getTasks()) {
            Task task = taskService.selectBySeqNum(bus.getId(), taskNo);
            if (null == task) {
                result.getTasks().add(new TaskBoolDto(taskNo, Boolean.FALSE, "task " + taskNo + " is not exist"));
                continue;
            }
            if (!task.getTaskSts().equals(TaskStsType.INIT.val())) {
                result.getTasks().add(new TaskBoolDto(taskNo, Boolean.FALSE,  "task " + taskNo + " has already been assigned"));
                continue;
            }
            Boolean cancel = false;
            try {
                cancel = taskService.cancel(task.getId(), null, Constants.UPLINK);
            } catch (Exception e) {
                log.error("failed to cancel task {}", taskNo, e);
            }
            result.getTasks().add(new TaskBoolDto(taskNo, cancel, "failed to cancel task " + taskNo));
        }
        return R.ok("cancel tasks success").add(result);
    }
}
zy-acs-manager/src/main/java/com/zy/acs/manager/core/integrate/wms/TaskReportService.java
New file
@@ -0,0 +1,141 @@
package com.zy.acs.manager.core.integrate.wms;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.zy.acs.framework.common.Cools;
import com.zy.acs.framework.common.R;
import com.zy.acs.manager.common.config.UplinkProperties;
import com.zy.acs.manager.common.utils.HttpGo;
import com.zy.acs.manager.core.integrate.dto.HttpResult;
import com.zy.acs.manager.core.integrate.dto.TaskUplinkParam;
import com.zy.acs.manager.core.service.ThreadPoolRegulator;
import com.zy.acs.manager.manager.entity.Bus;
import com.zy.acs.manager.manager.entity.Task;
import com.zy.acs.manager.manager.enums.TaskStsType;
import com.zy.acs.manager.manager.enums.TaskUplinkStateType;
import com.zy.acs.manager.manager.service.BusService;
import com.zy.acs.manager.manager.service.TaskService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import java.time.Duration;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
@Slf4j
@Service
public class TaskReportService {
    @Autowired
    private UplinkProperties uplinkProperties;
    @Autowired
    private TaskService taskService;
    @Autowired
    private ThreadPoolRegulator threadPoolRegulator;
    @Autowired
    private BusService busService;
    private HttpGo http;
    @PostConstruct
    public void init() {
        int timeoutSeconds = uplinkProperties.getTimeout() / 1000;
        this.http = HttpGo.builder()
                .connectTimeout(Duration.ofSeconds(timeoutSeconds))
                .readTimeout(Duration.ofSeconds(timeoutSeconds))
//                .defaultHeader("User-Agent", "HttpGo/1.0")
//                .trustAllSsl(true) // ONLY if you really need it (self-signed internal)
                .build();
    }
    public boolean reportFinished(Task task) {
        if (Cools.isEmpty(task)) {
            return false;
        }
        if (!uplinkProperties.getEnabled()) {
            return false;
        }
        if (!task.getTaskSts().equals(TaskStsType.COMPLETE.val())) {
            return false;
        }
        TaskUplinkStateType uplinkStateType = TaskUplinkStateType.of(task.getUplinkSts());
        if (!uplinkStateType.equals(TaskUplinkStateType.PENDING)
                && !uplinkStateType.equals(TaskUplinkStateType.SENDING)
                && !uplinkStateType.equals(TaskUplinkStateType.FAILED)
        ) {
            return false;
        }
        // block
//        Future<R> future = threadPoolRegulator.getInstance().submit(() -> {
//            mapDataDispatcher.modifyDynamicMatrix(null, null, param.getAgvNo(), true);
//            return success();
//        });
//        System.out.println(JSON.toJSONString(future.get()));
        // non-block
        CompletableFuture<?> completableFuture = CompletableFuture.supplyAsync(() -> {
//            mapDataDispatcher.modifyDynamicMatrix(null, null, param.getAgvNo(), true);
//            avoidWaveCalculator.calcDynamicNodeByVehicle(agv, null);
            return R.ok();
        }, threadPoolRegulator.getInstance());
        // url
        String url = this.http.buildUrl(uplinkProperties.getHost(), uplinkProperties.getPort(), uplinkProperties.getUrl());
        // headers
        Map<String, String> headers = new HashMap<>();
        // params
        TaskUplinkParam param = new TaskUplinkParam();
        if (null != task.getBusId()) {
            Bus bus = busService.getById(task);
            param.setBatchNo(bus.getBusNo());
        }
        param.setTaskNo(task.getSeqNum());
        param.setTimestamp(null == task.getEndTime() ? System.currentTimeMillis() : task.getEndTime().getTime());
        HttpResult<?> result;
        try {
            result = postForResult(url, headers, param);
            Integer code = result.getCode();
            if (null == code || 200 != code) {
                return false;
            }
//            Object data = result.getData();
//            if (Cools.isEmpty(data)) {
//                return false;
//            }
        } catch (Exception e) {
            log.error("Uplink report failed, taskId={}",
                    JSON.toJSONString(task),
                    e);
            return false;
        }
        return true;
    }
    private HttpResult<?> postForResult(String url, Map<String, String> headers, TaskUplinkParam param) throws Exception {
        String json = JSON.toJSONString(param);
        HttpGo.HttpResponse response = this.http.postJson(url, headers, json);
        int status = response.statusCode();
        if (status != 200) {
            throw new RuntimeException("Uplink HTTP error: status=" + status + ", body=" + response.body());
        }
        String body = response.body();
        if (Cools.isEmpty(body)) {
            throw new RuntimeException("Uplink empty response body.");
        }
        HttpResult<?> result = JSON.parseObject(body, new TypeReference<HttpResult<?>>() {});
        if (result == null) {
            throw new RuntimeException("Uplink parse HttpResult failed: body=" + body);
        }
        return result;
    }
}
zy-acs-manager/src/main/java/com/zy/acs/manager/core/scheduler/MaintainScheduler.java
@@ -3,9 +3,11 @@
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.zy.acs.common.enums.AgvStatusType;
import com.zy.acs.common.utils.RedisSupport;
import com.zy.acs.framework.common.Cools;
import com.zy.acs.framework.common.DateUtils;
import com.zy.acs.manager.common.config.UplinkProperties;
import com.zy.acs.manager.core.integrate.wms.TaskReportService;
import com.zy.acs.manager.core.service.MainLockWrapService;
import com.zy.acs.manager.core.service.MainService;
import com.zy.acs.manager.manager.entity.*;
import com.zy.acs.manager.manager.enums.*;
import com.zy.acs.manager.manager.service.*;
@@ -36,7 +38,7 @@
    @Autowired
    private FuncStaService funcStaService;
    @Autowired
    private MainService mainService;
    private UplinkProperties uplinkProperties;
    @Autowired
    private MainLockWrapService mainLockWrapService;
    @Autowired
@@ -47,6 +49,8 @@
    private AgvModelService agvModelService;
    @Autowired
    private SegmentService segmentService;
    @Autowired
    private TaskReportService taskReportService;
    @Scheduled(cron = "0/5 * * * * ? ")
    private synchronized void autoCharge(){
@@ -148,8 +152,29 @@
        }
    }
    @Scheduled(cron = "0/3 * * * * ? ")
    private void reportTaskToUplink() {
        if (!uplinkProperties.getEnabled()) { return; }
        List<Task> taskList = taskService.list(new LambdaQueryWrapper<Task>()
                .in(Task::getUplinkSts, TaskUplinkStateType.PENDING.toString(), TaskUplinkStateType.FAILED.toString())
                .eq(Task::getTaskSts, TaskStsType.COMPLETE.val())
        );
        if (Cools.isEmpty(taskList)) { return; }
        for (Task task : taskList) {
            boolean finished = taskReportService.reportFinished(task);
            if (finished) {
                task.setUplinkSts(TaskUplinkStateType.SUCCESS.toString());
            } else {
                log.error("failed to report task to uplink: {}", task.getSeqNum());
                task.setUplinkSts(TaskUplinkStateType.FAILED.toString());
            }
            taskService.updateById(task);
        }
    }
    @Scheduled(cron = "0/5 * * * * ? ")
    private synchronized void releaseFuncSta(){
    private synchronized void releaseFuncSta() {
        List<FuncSta> funcStaList = funcStaService.list(new LambdaQueryWrapper<FuncSta>().eq(FuncSta::getState, FuncStaStateType.OCCUPIED.toString()));
        for (FuncSta funcSta : funcStaList) {
            boolean beIdle = funcStaService.isCanBeIdle(funcSta);
zy-acs-manager/src/main/java/com/zy/acs/manager/core/scheduler/test/AutoRunRebootScheduler.java
File was renamed from zy-acs-manager/src/main/java/com/zy/acs/manager/core/scheduler/AutoRunRebootScheduler.java
@@ -1,4 +1,4 @@
package com.zy.acs.manager.core.scheduler;
package com.zy.acs.manager.core.scheduler.test;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.zy.acs.common.constant.RedisConstant;
@@ -6,10 +6,10 @@
import com.zy.acs.framework.common.Cools;
import com.zy.acs.framework.common.SnowflakeIdWorker;
import com.zy.acs.manager.common.domain.TaskDto;
import com.zy.acs.manager.core.integrate.dto.OpenBusSubmitParam;
import com.zy.acs.manager.core.service.AreaGovernService;
import com.zy.acs.manager.core.service.MainLockWrapService;
import com.zy.acs.manager.core.service.MainService;
import com.zy.acs.manager.manager.controller.param.OpenBusSubmitParam;
import com.zy.acs.manager.manager.entity.*;
import com.zy.acs.manager.manager.enums.*;
import com.zy.acs.manager.manager.service.*;
@@ -18,12 +18,11 @@
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.util.*;
@Slf4j
@Component
//@Component
public class AutoRunRebootScheduler {
    private static final AgvModelType DEFAULT_AGV_MODEL = AgvModelType.HEAVY_LOAD_STACKING_ROBOT;
@@ -118,7 +117,7 @@
        Collections.shuffle(stockList);
        OpenBusSubmitParam param = new OpenBusSubmitParam();
        param.setBatch(String.valueOf(snowflakeIdWorker.nextId()).substring(13, 19));
        param.setBatchNo(String.valueOf(snowflakeIdWorker.nextId()).substring(13, 19));
        for (int i = 0; i < Math.min(agvModel.getBackpack(), stockList.size()) ; i++) {
            Sta stockSta = stockList.get(i);
            String staCode = codeService.getCacheById(stockSta.getCode()).getData();
@@ -156,11 +155,11 @@
            taskDto.setOriSta(stockSta.getStaNo());
            taskDto.setDestLoc(idleLoc.getLocNo());
            taskDto.setPriority(100);
            taskDto.setSeqNum(String.valueOf(snowflakeIdWorker.nextId()).substring(15, 19));
            taskDto.setTaskNo(String.valueOf(snowflakeIdWorker.nextId()).substring(15, 19));
            param.getTaskList().add(taskDto);
            param.getTasks().add(taskDto);
        }
        if (Cools.isEmpty(param.getTaskList())) { return; }
        if (Cools.isEmpty(param.getTasks())) { return; }
        mainService.generateBusAndTask(param, memo);
    }
@@ -174,7 +173,7 @@
        Collections.shuffle(idleList);
        OpenBusSubmitParam param = new OpenBusSubmitParam();
        param.setBatch(String.valueOf(snowflakeIdWorker.nextId()).substring(13, 19));
        param.setBatchNo(String.valueOf(snowflakeIdWorker.nextId()).substring(13, 19));
        for (int i = 0; i < Math.min(agvModel.getBackpack(), idleList.size()) ; i++) {
            Sta idleSta = idleList.get(i);
            String staCode = codeService.getCacheById(idleSta.getCode()).getData();
@@ -212,11 +211,11 @@
            taskDto.setOriLoc(stockLoc.getLocNo());
            taskDto.setDestSta(idleSta.getStaNo());
            taskDto.setPriority(100);
            taskDto.setSeqNum(String.valueOf(snowflakeIdWorker.nextId()).substring(15, 19));
            taskDto.setTaskNo(String.valueOf(snowflakeIdWorker.nextId()).substring(15, 19));
            param.getTaskList().add(taskDto);
            param.getTasks().add(taskDto);
        }
        if (Cools.isEmpty(param.getTaskList())) { return; }
        if (Cools.isEmpty(param.getTasks())) { return; }
        mainService.generateBusAndTask(param, memo);
    }
@@ -250,7 +249,7 @@
        Collections.shuffle(idleLocList);
        OpenBusSubmitParam param = new OpenBusSubmitParam();
        param.setBatch(String.valueOf(snowflakeIdWorker.nextId()).substring(13, 19));
        param.setBatchNo(String.valueOf(snowflakeIdWorker.nextId()).substring(13, 19));
        for (int i = 0; i < Math.min(maxCapacity, Math.min(stockLocList.size(), idleLocList.size())); i++) {
            Loc stockLoc = stockLocList.get(i);
            Loc idleLoc = idleLocList.get(i);
@@ -258,11 +257,11 @@
            TaskDto taskDto = new TaskDto();
            taskDto.setOriLoc(stockLoc.getLocNo());
            taskDto.setDestLoc(idleLoc.getLocNo());
            taskDto.setSeqNum(String.valueOf(snowflakeIdWorker.nextId()).substring(15, 19));
            taskDto.setTaskNo(String.valueOf(snowflakeIdWorker.nextId()).substring(15, 19));
            param.getTaskList().add(taskDto);
            param.getTasks().add(taskDto);
        }
        if (Cools.isEmpty(param.getTaskList())) { return; }
        if (Cools.isEmpty(param.getTasks())) { return; }
        mainService.generateBusAndTask(param, memo);
    }
zy-acs-manager/src/main/java/com/zy/acs/manager/core/scheduler/test/AutoRunScheduler.java
File was renamed from zy-acs-manager/src/main/java/com/zy/acs/manager/core/scheduler/AutoRunScheduler.java
@@ -1,4 +1,4 @@
package com.zy.acs.manager.core.scheduler;
package com.zy.acs.manager.core.scheduler.test;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.zy.acs.common.constant.RedisConstant;
@@ -7,7 +7,7 @@
import com.zy.acs.framework.common.SnowflakeIdWorker;
import com.zy.acs.manager.common.domain.TaskDto;
import com.zy.acs.manager.core.service.*;
import com.zy.acs.manager.manager.controller.param.OpenBusSubmitParam;
import com.zy.acs.manager.core.integrate.dto.OpenBusSubmitParam;
import com.zy.acs.manager.manager.entity.*;
import com.zy.acs.manager.manager.enums.*;
import com.zy.acs.manager.manager.service.*;
@@ -86,7 +86,7 @@
        Collections.shuffle(stockList);
        OpenBusSubmitParam param = new OpenBusSubmitParam();
        param.setBatch(String.valueOf(snowflakeIdWorker.nextId()).substring(13, 19));
        param.setBatchNo(String.valueOf(snowflakeIdWorker.nextId()).substring(13, 19));
        for (int i = 0; i < Math.min(agvModel.getBackpack(), stockList.size()) ; i++) {
            Sta stockSta = stockList.get(i);
            String staCode = codeService.getCacheById(stockSta.getCode()).getData();
@@ -124,11 +124,11 @@
            taskDto.setOriSta(stockSta.getStaNo());
            taskDto.setDestLoc(idleLoc.getLocNo());
            taskDto.setPriority(100);
            taskDto.setSeqNum(String.valueOf(snowflakeIdWorker.nextId()).substring(15, 19));
            taskDto.setTaskNo(String.valueOf(snowflakeIdWorker.nextId()).substring(15, 19));
            param.getTaskList().add(taskDto);
            param.getTasks().add(taskDto);
        }
        if (Cools.isEmpty(param.getTaskList())) { return; }
        if (Cools.isEmpty(param.getTasks())) { return; }
        mainService.generateBusAndTask(param, memo);
    }
@@ -142,7 +142,7 @@
        Collections.shuffle(idleList);
        OpenBusSubmitParam param = new OpenBusSubmitParam();
        param.setBatch(String.valueOf(snowflakeIdWorker.nextId()).substring(13, 19));
        param.setBatchNo(String.valueOf(snowflakeIdWorker.nextId()).substring(13, 19));
        for (int i = 0; i < Math.min(agvModel.getBackpack(), idleList.size()) ; i++) {
            Sta idleSta = idleList.get(i);
            String staCode = codeService.getCacheById(idleSta.getCode()).getData();
@@ -180,11 +180,11 @@
            taskDto.setOriLoc(stockLoc.getLocNo());
            taskDto.setDestSta(idleSta.getStaNo());
            taskDto.setPriority(100);
            taskDto.setSeqNum(String.valueOf(snowflakeIdWorker.nextId()).substring(15, 19));
            taskDto.setTaskNo(String.valueOf(snowflakeIdWorker.nextId()).substring(15, 19));
            param.getTaskList().add(taskDto);
            param.getTasks().add(taskDto);
        }
        if (Cools.isEmpty(param.getTaskList())) { return; }
        if (Cools.isEmpty(param.getTasks())) { return; }
        mainService.generateBusAndTask(param, memo);
    }
@@ -218,7 +218,7 @@
        Collections.shuffle(idleLocList);
        OpenBusSubmitParam param = new OpenBusSubmitParam();
        param.setBatch(String.valueOf(snowflakeIdWorker.nextId()).substring(13, 19));
        param.setBatchNo(String.valueOf(snowflakeIdWorker.nextId()).substring(13, 19));
        for (int i = 0; i < Math.min(maxCapacity, Math.min(stockLocList.size(), idleLocList.size())); i++) {
            Loc stockLoc = stockLocList.get(i);
            Loc idleLoc = idleLocList.get(i);
@@ -226,11 +226,11 @@
            TaskDto taskDto = new TaskDto();
            taskDto.setOriLoc(stockLoc.getLocNo());
            taskDto.setDestLoc(idleLoc.getLocNo());
            taskDto.setSeqNum(String.valueOf(snowflakeIdWorker.nextId()).substring(15, 19));
            taskDto.setTaskNo(String.valueOf(snowflakeIdWorker.nextId()).substring(15, 19));
            param.getTaskList().add(taskDto);
            param.getTasks().add(taskDto);
        }
        if (Cools.isEmpty(param.getTaskList())) { return; }
        if (Cools.isEmpty(param.getTasks())) { return; }
        mainService.generateBusAndTask(param, memo);
    }
zy-acs-manager/src/main/java/com/zy/acs/manager/core/scheduler/test/AutoTestDeviationScheduler.java
File was renamed from zy-acs-manager/src/main/java/com/zy/acs/manager/core/scheduler/AutoTestDeviationScheduler.java
@@ -1,4 +1,4 @@
package com.zy.acs.manager.core.scheduler;
package com.zy.acs.manager.core.scheduler.test;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.zy.acs.common.constant.RedisConstant;
@@ -6,10 +6,13 @@
import com.zy.acs.framework.common.Cools;
import com.zy.acs.framework.common.SnowflakeIdWorker;
import com.zy.acs.manager.common.domain.TaskDto;
import com.zy.acs.manager.core.integrate.dto.OpenBusSubmitParam;
import com.zy.acs.manager.core.service.AreaGovernService;
import com.zy.acs.manager.core.service.MainService;
import com.zy.acs.manager.manager.controller.param.OpenBusSubmitParam;
import com.zy.acs.manager.manager.entity.*;
import com.zy.acs.manager.manager.entity.Agv;
import com.zy.acs.manager.manager.entity.AgvModel;
import com.zy.acs.manager.manager.entity.Bus;
import com.zy.acs.manager.manager.entity.Loc;
import com.zy.acs.manager.manager.enums.AgvModelType;
import com.zy.acs.manager.manager.enums.BusStsType;
import com.zy.acs.manager.manager.enums.LocStsType;
@@ -19,10 +22,11 @@
import com.zy.acs.manager.system.service.ConfigService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.util.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@Slf4j
@Component
@@ -108,7 +112,7 @@
        }
        OpenBusSubmitParam param = new OpenBusSubmitParam();
        param.setBatch(String.valueOf(snowflakeIdWorker.nextId()).substring(13, 19));
        param.setBatchNo(String.valueOf(snowflakeIdWorker.nextId()).substring(13, 19));
        for (int i = 0; i < Math.min(maxCapacity, Math.min(stockLocList.size(), idleLocList.size())); i++) {
            Loc stockLoc = stockLocList.get(i);
            Loc idleLoc = idleLocList.get(i);
@@ -117,11 +121,11 @@
            TaskDto taskDto = new TaskDto();
            taskDto.setOriLoc(stockLoc.getLocNo());
            taskDto.setDestLoc(idleLoc.getLocNo());
            taskDto.setSeqNum(String.valueOf(snowflakeIdWorker.nextId()).substring(15, 19));
            taskDto.setTaskNo(String.valueOf(snowflakeIdWorker.nextId()).substring(15, 19));
            param.getTaskList().add(taskDto);
            param.getTasks().add(taskDto);
        }
        if (Cools.isEmpty(param.getTaskList())) { return; }
        if (Cools.isEmpty(param.getTasks())) { return; }
        mainService.generateBusAndTask(param, memo);
    }
zy-acs-manager/src/main/java/com/zy/acs/manager/core/scheduler/test/ConveyorAutoRunScheduler.java
File was renamed from zy-acs-manager/src/main/java/com/zy/acs/manager/core/scheduler/ConveyorAutoRunScheduler.java
@@ -1,4 +1,4 @@
package com.zy.acs.manager.core.scheduler;
package com.zy.acs.manager.core.scheduler.test;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.zy.acs.common.constant.RedisConstant;
@@ -8,7 +8,7 @@
import com.zy.acs.manager.common.domain.TaskDto;
import com.zy.acs.manager.core.service.AreaGovernService;
import com.zy.acs.manager.core.service.MainService;
import com.zy.acs.manager.manager.controller.param.OpenBusSubmitParam;
import com.zy.acs.manager.core.integrate.dto.OpenBusSubmitParam;
import com.zy.acs.manager.manager.entity.*;
import com.zy.acs.manager.manager.enums.AgvModelType;
import com.zy.acs.manager.manager.enums.BusStsType;
@@ -97,7 +97,7 @@
        Collections.shuffle(idleList);
        OpenBusSubmitParam param = new OpenBusSubmitParam();
        param.setBatch(String.valueOf(snowflakeIdWorker.nextId()).substring(13, 19));
        param.setBatchNo(String.valueOf(snowflakeIdWorker.nextId()).substring(13, 19));
        for (int i = 0; i < agvModel.getBackpack() ; i++) {
            Sta idleSta = idleList.get(i);
            String staCode = codeService.getCacheById(idleSta.getCode()).getData();
@@ -135,11 +135,11 @@
            taskDto.setOriLoc(stockLoc.getLocNo());
            taskDto.setDestSta(idleSta.getStaNo());
            taskDto.setPriority(100);
            taskDto.setSeqNum(String.valueOf(snowflakeIdWorker.nextId()).substring(15, 19));
            taskDto.setTaskNo(String.valueOf(snowflakeIdWorker.nextId()).substring(15, 19));
            param.getTaskList().add(taskDto);
            param.getTasks().add(taskDto);
        }
        if (Cools.isEmpty(param.getTaskList())) { return; }
        if (Cools.isEmpty(param.getTasks())) { return; }
        mainService.generateBusAndTask(param, memo);
    }
zy-acs-manager/src/main/java/com/zy/acs/manager/core/service/MainService.java
@@ -23,7 +23,7 @@
import com.zy.acs.manager.core.domain.TaskPosDto;
import com.zy.acs.manager.core.integrate.conveyor.ConveyorStationService;
import com.zy.acs.manager.core.service.astart.MapDataDispatcher;
import com.zy.acs.manager.manager.controller.param.OpenBusSubmitParam;
import com.zy.acs.manager.core.integrate.dto.OpenBusSubmitParam;
import com.zy.acs.manager.manager.entity.*;
import com.zy.acs.manager.manager.enums.*;
import com.zy.acs.manager.manager.service.*;
@@ -107,8 +107,8 @@
        if (!Cools.isEmpty(errorMsg)) {
            throw new BusinessException(errorMsg);
        }
        String batch = busSubmitParam.getBatch();
        List<TaskDto> taskDtoList = busSubmitParam.getTaskList();
        String batchNo = busSubmitParam.getBatchNo();
        List<TaskDto> taskDtoList = busSubmitParam.getTasks();
        if (Cools.isEmpty(taskDtoList)) {
            throw new BusinessException("taskList can't be empty!");
        }
@@ -123,7 +123,7 @@
        Date now = new Date();
        Bus bus = new Bus();
        bus.setUuid(String.valueOf(snowflakeIdWorker.nextId()).substring(3));
        bus.setBusNo(batch);
        bus.setBusNo(batchNo);
        bus.setStartTime(now);
        bus.setBusSts(BusStsType.RECEIVE.val());
        bus.setMemo(memo);
@@ -791,6 +791,7 @@
            task.setPriority(taskType.equals(TaskTypeType.TO_CHARGE)?2:1);
            task.setTaskSts(TaskStsType.ASSIGN.val());
            task.setTaskType(taskType.val());
            task.setUplinkSts(TaskUplinkStateType.SKIPPED.toString());
            task.setIoTime(now);
            task.setStartTime(now);
            if (!taskService.save(task)) {
zy-acs-manager/src/main/java/com/zy/acs/manager/core/service/ValidService.java
@@ -3,6 +3,7 @@
import com.alibaba.fastjson.JSON;
import com.zy.acs.framework.common.Cools;
import com.zy.acs.framework.common.SnowflakeIdWorker;
import com.zy.acs.manager.common.config.UplinkProperties;
import com.zy.acs.manager.common.domain.TaskDto;
import com.zy.acs.manager.common.exception.BusinessException;
import com.zy.acs.manager.manager.entity.Code;
@@ -10,6 +11,7 @@
import com.zy.acs.manager.manager.entity.Sta;
import com.zy.acs.manager.manager.entity.Task;
import com.zy.acs.manager.manager.enums.TaskTypeType;
import com.zy.acs.manager.manager.enums.TaskUplinkStateType;
import com.zy.acs.manager.manager.service.CodeService;
import com.zy.acs.manager.manager.service.LocService;
import com.zy.acs.manager.manager.service.StaService;
@@ -36,17 +38,20 @@
    private MapService mapService;
    @Autowired
    private SnowflakeIdWorker snowflakeIdWorker;
    @Autowired
    private UplinkProperties uplinkProperties;
    public List<Task> validTaskDtoList(List<TaskDto> taskDtoList) {
        List<Task> taskList = new ArrayList<>();
        String uplinkSts = uplinkProperties.getEnabled() ? TaskUplinkStateType.PENDING.toString() : TaskUplinkStateType.SKIPPED.toString();
        for (TaskDto taskDto : taskDtoList) {
            if (Cools.isEmpty(taskDto.getSeqNum())) {
            if (Cools.isEmpty(taskDto.getTaskNo())) {
                throw new BusinessException("Task seqNum can't be empty!");
            }
            Task task = new Task();
            taskList.add(task);
            task.setUuid(String.valueOf(snowflakeIdWorker.nextId()).substring(3));
            task.setSeqNum(taskDto.getSeqNum());
            task.setSeqNum(taskDto.getTaskNo());
            task.setPriority(taskDto.getPriority());
            // ori --------------------------
@@ -140,6 +145,7 @@
                        throw new BusinessException("seqNum:" + task.getSeqNum() + " is wrong,oriLoc:" + task.getOriLoc() + " is unable to reach destLoc" + task.getDestLoc());
                    }
                    task.setPhase(JSON.toJSONString(pathList));
                    task.setUplinkSts(uplinkSts);
                    break;
                case LOC_TO_STA:
                    oriLoc = locService.getById(task.getOriLoc());
@@ -158,6 +164,7 @@
                        throw new BusinessException("seqNum:" + task.getSeqNum() + " is wrong,oriLoc:" + task.getOriLoc() + " can't move to destSta" + task.getDestSta());
                    }
                    task.setPhase(JSON.toJSONString(pathList));
                    task.setUplinkSts(uplinkSts);
                    break;
                case STA_TO_LOC:
                    oriSta = staService.getById(task.getOriSta());
@@ -176,6 +183,7 @@
                        throw new BusinessException("seqNum:" + task.getSeqNum() + " is wrong,oriSta:" + task.getOriSta() + " is unable to react destLoc" + task.getDestLoc());
                    }
                    task.setPhase(JSON.toJSONString(pathList));
                    task.setUplinkSts(uplinkSts);
                    break;
                case STA_TO_STA:
                    oriSta = staService.getById(task.getOriSta());
@@ -194,6 +202,7 @@
                        throw new BusinessException("seqNum:" + task.getSeqNum() + " is wrong,oriSta:" + task.getOriSta() + " can't move to destSta" + task.getDestSta());
                    }
                    task.setPhase(JSON.toJSONString(pathList));
                    task.setUplinkSts(uplinkSts);
                    break;
                default:
                    throw new BusinessException("seqNum:" + task.getSeqNum() + " is wrong, cause this type not exist");
zy-acs-manager/src/main/java/com/zy/acs/manager/manager/controller/BusController.java
@@ -6,12 +6,13 @@
import com.zy.acs.framework.common.Cools;
import com.zy.acs.framework.common.R;
import com.zy.acs.manager.common.annotation.OperationLog;
import com.zy.acs.manager.common.constant.Constants;
import com.zy.acs.manager.common.domain.*;
import com.zy.acs.manager.common.utils.BusinessSortService;
import com.zy.acs.manager.common.utils.ExcelUtil;
import com.zy.acs.manager.core.service.MainService;
import com.zy.acs.manager.manager.controller.param.BusCreateParam;
import com.zy.acs.manager.manager.controller.param.OpenBusSubmitParam;
import com.zy.acs.manager.core.integrate.dto.OpenBusSubmitParam;
import com.zy.acs.manager.manager.entity.Bus;
import com.zy.acs.manager.manager.entity.Task;
import com.zy.acs.manager.manager.enums.BusStsType;
@@ -94,10 +95,10 @@
    @PostMapping("/bus/save")
    public R save(@RequestBody BusCreateParam param) {
        OpenBusSubmitParam submitParam = new OpenBusSubmitParam();
        submitParam.setBatch(param.getBusNo());
        submitParam.setBatchNo(param.getBusNo());
        for (TaskIdByLongDto dto : param.getTaskList()) {
            TaskDto taskDto = new TaskDto();
            taskDto.setSeqNum(dto.getSeqNum());
            taskDto.setTaskNo(dto.getSeqNum());
            taskDto.setPriority(dto.getPriority());
            if (!Cools.isEmpty(dto.getOriSta())) {
                taskDto.setOriSta(staService.getById(dto.getOriSta()).getStaNo());
@@ -111,7 +112,7 @@
            if (!Cools.isEmpty(dto.getDestLoc())) {
                taskDto.setDestLoc(locService.getById(dto.getDestLoc()).getLocNo());
            }
            submitParam.getTaskList().add(taskDto);
            submitParam.getTasks().add(taskDto);
        }
        Bus bus = mainService.generateBusAndTask(submitParam, param.getMemo());
        return R.ok("Save Success").add(bus);
@@ -176,7 +177,7 @@
                .in(Task::getTaskSts, TaskStsType.INIT.val(), TaskStsType.WAITING.val()));
        if (!Cools.isEmpty(taskList)) {
            for (Task task : taskList) {
                taskService.cancel(task.getId(), getLoginUserId());
                taskService.cancel(task.getId(), getLoginUserId(), Constants.HANDLE);
            }
        }
        if (0 == taskService.count(new LambdaQueryWrapper<Task>().eq(Task::getBusId, bus.getId()).eq(Task::getTaskSts, TaskStsType.COMPLETE.val()))) {
zy-acs-manager/src/main/java/com/zy/acs/manager/manager/controller/CodeController.java
@@ -17,6 +17,7 @@
import com.zy.acs.manager.manager.entity.Code;
import com.zy.acs.manager.manager.entity.CodeGap;
import com.zy.acs.manager.manager.entity.Route;
import com.zy.acs.manager.manager.enums.StatusType;
import com.zy.acs.manager.manager.service.CodeGapService;
import com.zy.acs.manager.manager.service.CodeService;
import com.zy.acs.manager.manager.service.RouteService;
@@ -159,6 +160,7 @@
            code.setUuid("code".concat(code.getData()));
//            code.setCorner(0);
            code.setScale(GsonUtils.toJson(Cools.add("x", 1).add("y", 1)));
            code.setStatus(StatusType.ENABLE.val);
            code.setCreateBy(userId);
            code.setCreateTime(now);
            code.setUpdateBy(userId);
zy-acs-manager/src/main/java/com/zy/acs/manager/manager/controller/IntegrationRecordController.java
New file
@@ -0,0 +1,108 @@
package com.zy.acs.manager.manager.controller;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.zy.acs.framework.common.Cools;
import com.zy.acs.framework.common.R;
import com.zy.acs.manager.common.utils.ExcelUtil;
import com.zy.acs.manager.common.annotation.OperationLog;
import com.zy.acs.manager.common.domain.BaseParam;
import com.zy.acs.manager.common.domain.KeyValVo;
import com.zy.acs.manager.common.domain.PageParam;
import com.zy.acs.manager.manager.entity.IntegrationRecord;
import com.zy.acs.manager.manager.service.IntegrationRecordService;
import com.zy.acs.manager.system.controller.BaseController;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletResponse;
import java.util.*;
@RestController
@RequestMapping("/api")
public class IntegrationRecordController extends BaseController {
    @Autowired
    private IntegrationRecordService integrationRecordService;
    @PreAuthorize("hasAuthority('manager:integrationRecord:list')")
    @PostMapping("/integrationRecord/page")
    public R page(@RequestBody Map<String, Object> map) {
        BaseParam baseParam = buildParam(map, BaseParam.class);
        PageParam<IntegrationRecord, BaseParam> pageParam = new PageParam<>(baseParam, IntegrationRecord.class);
        return R.ok().add(integrationRecordService.page(pageParam, pageParam.buildWrapper(true)));
    }
    @PreAuthorize("hasAuthority('manager:integrationRecord:list')")
    @PostMapping("/integrationRecord/list")
    public R list(@RequestBody Map<String, Object> map) {
        return R.ok().add(integrationRecordService.list());
    }
    @PreAuthorize("hasAuthority('manager:integrationRecord:list')")
    @PostMapping({"/integrationRecord/many/{ids}", "/integrationRecords/many/{ids}"})
    public R many(@PathVariable Long[] ids) {
        return R.ok().add(integrationRecordService.listByIds(Arrays.asList(ids)));
    }
    @PreAuthorize("hasAuthority('manager:integrationRecord:list')")
    @GetMapping("/integrationRecord/{id}")
    public R get(@PathVariable("id") Long id) {
        return R.ok().add(integrationRecordService.getById(id));
    }
    @PreAuthorize("hasAuthority('manager:integrationRecord:save')")
    @OperationLog("Create Integration Record")
    @PostMapping("/integrationRecord/save")
    public R save(@RequestBody IntegrationRecord integrationRecord) {
        integrationRecord.setCreateTime(new Date());
        integrationRecord.setUpdateTime(new Date());
        if (!integrationRecordService.save(integrationRecord)) {
            return R.error("Save Fail");
        }
        return R.ok("Save Success").add(integrationRecord);
    }
    @PreAuthorize("hasAuthority('manager:integrationRecord:update')")
    @OperationLog("Update Integration Record")
    @PostMapping("/integrationRecord/update")
    public R update(@RequestBody IntegrationRecord integrationRecord) {
        integrationRecord.setUpdateTime(new Date());
        if (!integrationRecordService.updateById(integrationRecord)) {
            return R.error("Update Fail");
        }
        return R.ok("Update Success").add(integrationRecord);
    }
    @PreAuthorize("hasAuthority('manager:integrationRecord:remove')")
    @OperationLog("Delete Integration Record")
    @PostMapping("/integrationRecord/remove/{ids}")
    public R remove(@PathVariable Long[] ids) {
        if (!integrationRecordService.removeByIds(Arrays.asList(ids))) {
            return R.error("Delete Fail");
        }
        return R.ok("Delete Success").add(ids);
    }
    @PreAuthorize("hasAuthority('manager:integrationRecord:list')")
    @PostMapping("/integrationRecord/query")
    public R query(@RequestParam(required = false) String condition) {
        List<KeyValVo> vos = new ArrayList<>();
        LambdaQueryWrapper<IntegrationRecord> wrapper = new LambdaQueryWrapper<>();
        if (!Cools.isEmpty(condition)) {
            wrapper.like(IntegrationRecord::getNamespace, condition);
        }
        integrationRecordService.page(new Page<>(1, 30), wrapper).getRecords().forEach(
                item -> vos.add(new KeyValVo(item.getId(), item.getNamespace()))
        );
        return R.ok().add(vos);
    }
    @PreAuthorize("hasAuthority('manager:integrationRecord:list')")
    @PostMapping("/integrationRecord/export")
    public void export(@RequestBody Map<String, Object> map, HttpServletResponse response) throws Exception {
        ExcelUtil.build(ExcelUtil.create(integrationRecordService.list(), IntegrationRecord.class), response);
    }
}
zy-acs-manager/src/main/java/com/zy/acs/manager/manager/controller/TaskController.java
@@ -1,11 +1,11 @@
package com.zy.acs.manager.manager.controller;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.zy.acs.framework.common.Cools;
import com.zy.acs.framework.common.R;
import com.zy.acs.manager.common.annotation.OperationLog;
import com.zy.acs.manager.common.constant.Constants;
import com.zy.acs.manager.common.domain.BaseParam;
import com.zy.acs.manager.common.domain.KeyValVo;
import com.zy.acs.manager.common.domain.PageParam;
@@ -15,7 +15,6 @@
import com.zy.acs.manager.manager.entity.Task;
import com.zy.acs.manager.manager.service.TaskService;
import com.zy.acs.manager.system.controller.BaseController;
import com.zy.acs.manager.system.entity.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
@@ -119,14 +118,14 @@
    @OperationLog("Complete Task")
    @GetMapping("/task/complete/{id}")
    public R complete(@PathVariable Long id) {
        return taskService.complete(id, getLoginUserId()) ? R.ok("Complete Success") : R.error("Complete Fail");
        return taskService.complete(id, getLoginUserId(), Constants.HANDLE) ? R.ok("Complete Success") : R.error("Complete Fail");
    }
    @PreAuthorize("hasAuthority('manager:task:update')")
    @OperationLog("Cancel Task")
    @GetMapping("/task/cancel/{id}")
    public R cancel(@PathVariable Long id) {
        return taskService.cancel(id, getLoginUserId()) ? R.ok("Cancel Success") : R.error("Cancel Fail");
        return taskService.cancel(id, getLoginUserId(), Constants.HANDLE) ? R.ok("Cancel Success") : R.error("Cancel Fail");
    }
}
zy-acs-manager/src/main/java/com/zy/acs/manager/manager/entity/IntegrationRecord.java
New file
@@ -0,0 +1,196 @@
package com.zy.acs.manager.manager.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.io.Serializable;
import java.util.Date;
@Data
@TableName("man_integration_record")
public class IntegrationRecord implements Serializable {
    private static final long serialVersionUID = 1L;
    /**
     * ID
     */
    @ApiModelProperty(value= "ID")
    @TableId(value = "id", type = IdType.AUTO)
    private Long id;
    /**
     * ç¼–号
     */
    @ApiModelProperty(value= "编号")
    private String uuid;
    /**
     * åç§°ç©ºé—´
     */
    @ApiModelProperty(value= "名称空间")
    private String namespace;
    /**
     * æŽ¥å£åœ°å€
     */
    @ApiModelProperty(value= "接口地址")
    private String url;
    /**
     * å¹³å°å¯†é’¥
     */
    @ApiModelProperty(value= "平台密钥")
    private String appkey;
    /**
     * è°ƒç”¨æ–¹æ ‡è¯†
     */
    @ApiModelProperty(value= "调用方标识")
    private String caller;
    /**
     * æ–¹å‘ 1: è¢«è°ƒç”¨  2: è°ƒç”¨å¤–部
     */
    @ApiModelProperty(value= "方向 1: è¢«è°ƒç”¨  2: è°ƒç”¨å¤–部  ")
    private Integer direction;
    /**
     * æ—¶é—´æˆ³
     */
    @ApiModelProperty(value= "时间戳")
    private String timestamp;
    /**
     * å®¢æˆ·ç«¯IP
     */
    @ApiModelProperty(value= "客户端IP")
    private String clientIp;
    /**
     * è¯·æ±‚内容
     */
    @ApiModelProperty(value= "请求内容")
    private String request;
    /**
     * å“åº”内容
     */
    @ApiModelProperty(value= "响应内容")
    private String response;
    /**
     * å¼‚常内容
     */
    @ApiModelProperty(value= "异常内容")
    private String err;
    /**
     * ç»“æžœ 1: æˆåŠŸ  0: å¤±è´¥
     */
    @ApiModelProperty(value= "结果 1: æˆåŠŸ  0: å¤±è´¥  ")
    private Integer result;
    /**
     * è€—æ—¶
     */
    @ApiModelProperty(value= "耗时")
    private Integer costMs;
    /**
     * çŠ¶æ€ 1: æ­£å¸¸  0: å†»ç»“
     */
    @ApiModelProperty(value= "状态 1: æ­£å¸¸  0: å†»ç»“  ")
    private Integer status;
    /**
     * æ·»åŠ æ—¶é—´
     */
    @ApiModelProperty(value= "添加时间")
    @DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
    private Date createTime;
    /**
     * ä¿®æ”¹æ—¶é—´
     */
    @ApiModelProperty(value= "修改时间")
    @DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
    private Date updateTime;
    /**
     * å¤‡æ³¨
     */
    @ApiModelProperty(value= "备注")
    private String memo;
    public IntegrationRecord() {}
    public IntegrationRecord(String uuid,String namespace,String url,String appkey,String caller,Integer direction,String timestamp,String clientIp,String request,String response,String err,Integer result,Integer costMs,Integer status,Date createTime,Date updateTime,String memo) {
        this.uuid = uuid;
        this.namespace = namespace;
        this.url = url;
        this.appkey = appkey;
        this.caller = caller;
        this.direction = direction;
        this.timestamp = timestamp;
        this.clientIp = clientIp;
        this.request = request;
        this.response = response;
        this.err = err;
        this.result = result;
        this.costMs = costMs;
        this.status = status;
        this.createTime = createTime;
        this.updateTime = updateTime;
        this.memo = memo;
    }
//    IntegrationRecord integrationRecord = new IntegrationRecord(
//            null,    // ç¼–号
//            null,    // åç§°ç©ºé—´
//            null,    // æŽ¥å£åœ°å€
//            null,    // å¹³å°å¯†é’¥
//            null,    // è°ƒç”¨æ–¹æ ‡è¯†
//            null,    // æ–¹å‘[非空]
//            null,    // æ—¶é—´æˆ³
//            null,    // å®¢æˆ·ç«¯IP
//            null,    // è¯·æ±‚内容
//            null,    // å“åº”内容
//            null,    // å¼‚常内容
//            null,    // ç»“æžœ
//            null,    // è€—æ—¶
//            null,    // çŠ¶æ€
//            null,    // æ·»åŠ æ—¶é—´[非空]
//            null,    // ä¿®æ”¹æ—¶é—´[非空]
//            null    // å¤‡æ³¨
//    );
    public Boolean getResultBool(){
        if (null == this.result){ return null; }
        switch (this.result){
            case 1:
                return true;
            case 0:
                return false;
            default:
                return null;
        }
    }
    public Boolean getStatusBool(){
        if (null == this.status){ return null; }
        switch (this.status){
            case 1:
                return true;
            case 0:
                return false;
            default:
                return null;
        }
    }
}
zy-acs-manager/src/main/java/com/zy/acs/manager/manager/entity/Task.java
@@ -18,202 +18,106 @@
    private static final long serialVersionUID = 1L;
    /**
     * ID
     */
    @ApiModelProperty(value= "ID")
    @TableId(value = "id", type = IdType.AUTO)
    private Long id;
    /**
     * ç¼–号
     */
    @ApiModelProperty(value= "编号")
    private String uuid;
    /**
     * æ€»çº¿
     */
    @ApiModelProperty(value= "总线")
    private Long busId;
    /**
     * ä»»åŠ¡å·
     */
    @ApiModelProperty(value= "任务号")
    private String seqNum;
    /**
     * ä¼˜å…ˆçº§
     */
    @ApiModelProperty(value= "优先级")
    private Integer priority;
    /**
     * åç§°
     */
    @ApiModelProperty(value= "名称")
    private String name;
    /**
     * ä»»åŠ¡è¿›åº¦
     */
    @ApiModelProperty(value= "任务进度")
    private Long taskSts;
    /**
     * ä»»åŠ¡ç±»åž‹
     */
    @ApiModelProperty(value= "任务类型")
    private Long taskType;
    /**
     * AGV
     */
    @ApiModelProperty(value= "AGV")
    private Long agvId;
    /**
     * å·¥ä½œæ—¶é—´
     */
    @ApiModelProperty(value= "工作时间")
    private Date ioTime;
    /**
     * å¼€å§‹æ—¶é—´
     */
    @ApiModelProperty(value= "开始时间")
    private Date startTime;
    /**
     * ç»“束时间
     */
    @ApiModelProperty(value= "结束时间")
    private Date endTime;
    /**
     * å¼‚常时间
     */
    @ApiModelProperty(value= "异常时间")
    private Date errTime;
    /**
     * èµ·å§‹ç«™
     */
    @ApiModelProperty(value= "起始站")
    private Long oriSta;
    /**
     * èµ·å§‹åº“位
     */
    @ApiModelProperty(value= "起始库位")
    private Long oriLoc;
    /**
     * èµ·å§‹ç 
     */
    @ApiModelProperty(value= "起始码")
    private Long oriCode;
    /**
     * èµ·å§‹å··é“哈希
     */
    @ApiModelProperty(value= "起始巷道哈希")
    private String oriLaneHash;
    /**
     * ç›®æ ‡ç«™
     */
    @ApiModelProperty(value= "目标站")
    private Long destSta;
    /**
     * ç›®æ ‡åº“位
     */
    @ApiModelProperty(value= "目标库位")
    private Long destLoc;
    /**
     * ç›®æ ‡ç 
     */
    @ApiModelProperty(value= "目标码")
    private Long destCode;
    /**
     * ç›®æ ‡å··é“哈希
     */
    @ApiModelProperty(value= "目标巷道哈希")
    private String destLaneHash;
    /**
     * ç©ºæ¿
     */
    @ApiModelProperty(value= "空板")
    private String emptyMk;
    /**
     * æ‰˜ç›˜ç 
     */
    @ApiModelProperty(value= "上行状态")
    private String uplinkSts;
    @ApiModelProperty(value= "托盘码")
    private String zpallet;
    /**
     * çŽ¯èŠ‚
     */
    @ApiModelProperty(value= "环节")
    private String phase;
    /**
     * å¼‚常描述
     */
    @ApiModelProperty(value= "异常描述")
    private String errDesc;
    /**
     * çŠ¶æ€ 1: æ­£å¸¸  0: å†»ç»“
     */
    @ApiModelProperty(value= "状态 1: æ­£å¸¸  0: å†»ç»“  ")
    private Integer status;
    /**
     * æ˜¯å¦åˆ é™¤ 1: æ˜¯  0: å¦
     */
    @ApiModelProperty(value= "是否删除 1: æ˜¯  0: å¦  ")
    private Integer deleted;
    /**
     * ç§Ÿæˆ·
     */
    @ApiModelProperty(value= "租户")
    private Long tenantId;
    /**
     * æ·»åŠ äººå‘˜
     */
    @ApiModelProperty(value= "添加人员")
    private Long createBy;
    /**
     * æ·»åŠ æ—¶é—´
     */
    @ApiModelProperty(value= "添加时间")
    private Date createTime;
    /**
     * ä¿®æ”¹äººå‘˜
     */
    @ApiModelProperty(value= "修改人员")
    private Long updateBy;
    /**
     * ä¿®æ”¹æ—¶é—´
     */
    @ApiModelProperty(value= "修改时间")
    private Date updateTime;
    /**
     * å¤‡æ³¨
     */
    @ApiModelProperty(value= "备注")
    private String memo;
zy-acs-manager/src/main/java/com/zy/acs/manager/manager/enums/IntegrationDirectionType.java
New file
@@ -0,0 +1,16 @@
package com.zy.acs.manager.manager.enums;
public enum IntegrationDirectionType {
    NONE(0),
    INBOUND(1),
    OUTBOUND(2),
    ;
    public int value;
    IntegrationDirectionType(int value) {
        this.value = value;
    }
}
zy-acs-manager/src/main/java/com/zy/acs/manager/manager/enums/TaskUplinkStateType.java
New file
@@ -0,0 +1,28 @@
package com.zy.acs.manager.manager.enums;
import com.zy.acs.framework.common.Cools;
public enum TaskUplinkStateType {
    NONE,       // æœªçŸ¥
    PENDING,    // å¾…上报
    SENDING,    // ä¸ŠæŠ¥ä¸­
    SUCCESS,    // æˆåŠŸ
    FAILED,     // å¤±è´¥
    SKIPPED,    // è·³è¿‡
    ;
    public static TaskUplinkStateType of(String state) {
        if (Cools.isEmpty(state)) {
            return NONE;
        }
        for (TaskUplinkStateType type : TaskUplinkStateType.values()) {
            if (type.toString().equals(state)) {
                return type;
            }
        }
        return NONE;
    }
}
zy-acs-manager/src/main/java/com/zy/acs/manager/manager/mapper/IntegrationRecordMapper.java
New file
@@ -0,0 +1,12 @@
package com.zy.acs.manager.manager.mapper;
import com.zy.acs.manager.manager.entity.IntegrationRecord;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;
@Mapper
@Repository
public interface IntegrationRecordMapper extends BaseMapper<IntegrationRecord> {
}
zy-acs-manager/src/main/java/com/zy/acs/manager/manager/service/BusService.java
@@ -1,7 +1,7 @@
package com.zy.acs.manager.manager.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.zy.acs.manager.manager.controller.param.OpenBusSubmitParam;
import com.zy.acs.manager.core.integrate.dto.OpenBusSubmitParam;
import com.zy.acs.manager.manager.entity.Bus;
import com.zy.acs.manager.manager.enums.BusStsType;
@@ -9,6 +9,8 @@
public interface BusService extends IService<Bus> {
    Bus selectByBusNo(String busNo);
    Bus selectByUuid(String uuid);
    String checkoutValid(OpenBusSubmitParam param);
zy-acs-manager/src/main/java/com/zy/acs/manager/manager/service/IntegrationRecordService.java
New file
@@ -0,0 +1,8 @@
package com.zy.acs.manager.manager.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.zy.acs.manager.manager.entity.IntegrationRecord;
public interface IntegrationRecordService extends IService<IntegrationRecord> {
}
zy-acs-manager/src/main/java/com/zy/acs/manager/manager/service/TaskService.java
@@ -19,7 +19,7 @@
    Task pick(TaskStsType taskStsType);
    Task selectByUuid(String uuid);
    Task selectBySeqNum(Long busId, String seqNum);
    List<Task> selectBySts(TaskStsType taskStsType);
@@ -27,9 +27,9 @@
    List<Map<String, Object>> selectStatByLastSevenDays();
    Boolean complete(Long taskId, Long userId);
    Boolean complete(Long taskId, Long userId, String from);
    Boolean cancel(Long taskId, Long userId);
    Boolean cancel(Long taskId, Long userId, String from);
    LaneDto checkoutOriginLane(Task task);
zy-acs-manager/src/main/java/com/zy/acs/manager/manager/service/impl/BusServiceImpl.java
@@ -5,8 +5,9 @@
import com.zy.acs.framework.common.BaseRes;
import com.zy.acs.framework.common.Cools;
import com.zy.acs.framework.exception.CoolException;
import com.zy.acs.manager.common.constant.Constants;
import com.zy.acs.manager.common.domain.TaskDto;
import com.zy.acs.manager.manager.controller.param.OpenBusSubmitParam;
import com.zy.acs.manager.core.integrate.dto.OpenBusSubmitParam;
import com.zy.acs.manager.manager.entity.Bus;
import com.zy.acs.manager.manager.entity.Loc;
import com.zy.acs.manager.manager.entity.Task;
@@ -33,6 +34,14 @@
    private LocService locService;
    @Override
    public Bus selectByBusNo(String busNo) {
        if (Cools.isEmpty(busNo)) {
            return null;
        }
        return this.getOne(new LambdaQueryWrapper<Bus>().eq(Bus::getBusNo, busNo).last(Constants.LIMIT_ONE));
    }
    @Override
    public Bus selectByUuid(String uuid) {
        return this.getOne(new LambdaQueryWrapper<Bus>().eq(Bus::getUuid, uuid));
    }
@@ -41,7 +50,7 @@
        if (Cools.isEmpty(param)) {
            return;
        }
        List<TaskDto> taskList = param.getTaskList();
        List<TaskDto> taskList = param.getTasks();
        if (Cools.isEmpty(taskList)) {
            return;
        }
@@ -67,7 +76,7 @@
        if (null == param) {
            return BaseRes.PARAM;
        }
        if (Cools.isEmpty(param.getBatch())) {
        if (Cools.isEmpty(param.getBatchNo())) {
            return "Batch cannot be empty!";
        }
        this.test(param);
@@ -75,7 +84,7 @@
        Set<String> oriLocNoSet = new HashSet<>();
        Set<String> destStaNoSet = new HashSet<>();
        Set<String> destLocNoSet = new HashSet<>();
        for (TaskDto dto : param.getTaskList()) {
        for (TaskDto dto : param.getTasks()) {
            if (!Cools.isEmpty(dto.getOriSta()) && !Cools.isEmpty(dto.getOriLoc())) {
                return "OriSta and OriLoc cannot exist at the same time!";
            }
@@ -119,8 +128,8 @@
                    destLocNoSet.add(dto.getDestLoc());
                }
            }
            if (Cools.isEmpty(dto.getSeqNum())) {
                dto.setSeqNum(taskService.generateSeqNum());
            if (Cools.isEmpty(dto.getTaskNo())) {
                dto.setTaskNo(taskService.generateSeqNum());
            }
        }
zy-acs-manager/src/main/java/com/zy/acs/manager/manager/service/impl/IntegrationRecordServiceImpl.java
New file
@@ -0,0 +1,12 @@
package com.zy.acs.manager.manager.service.impl;
import com.zy.acs.manager.manager.mapper.IntegrationRecordMapper;
import com.zy.acs.manager.manager.entity.IntegrationRecord;
import com.zy.acs.manager.manager.service.IntegrationRecordService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
@Service("integrationRecordService")
public class IntegrationRecordServiceImpl extends ServiceImpl<IntegrationRecordMapper, IntegrationRecord> implements IntegrationRecordService {
}
zy-acs-manager/src/main/java/com/zy/acs/manager/manager/service/impl/TaskServiceImpl.java
@@ -68,8 +68,16 @@
    }
    @Override
    public Task selectByUuid(String uuid) {
        return this.getOne(new LambdaQueryWrapper<Task>().eq(Task::getUuid, uuid));
    public Task selectBySeqNum(Long busId, String seqNum) {
        if (Cools.isEmpty(seqNum)) {
            return null;
        }
        LambdaQueryWrapper<Task> wrapper = new LambdaQueryWrapper<>();
        if (null != busId) {
            wrapper.eq(Task::getBusId, busId);
        }
        wrapper.eq(Task::getSeqNum, seqNum);
        return this.getOne(wrapper.last(Constants.LIMIT_ONE));
    }
    @Override
@@ -100,7 +108,7 @@
    @Override
    @Transactional
    public Boolean complete(Long taskId, Long userId) {
    public Boolean complete(Long taskId, Long userId, String from) {
        Task task = this.getById(taskId);
        if (null == task) {
            return Boolean.FALSE;
@@ -112,7 +120,7 @@
        task.setTaskSts(TaskStsType.COMPLETE.val());
        task.setUpdateTime(now);
        task.setUpdateBy(userId);
        task.setMemo(Constants.HANDLE + " " + TaskStsType.COMPLETE);
        task.setMemo(from + " " + TaskStsType.COMPLETE);
        if (!this.updateById(task)) {
            throw new CoolException(BaseRes.ERROR);
        }
@@ -141,7 +149,7 @@
    @Override
    @Transactional
    public Boolean cancel(Long taskId, Long userId) {
    public Boolean cancel(Long taskId, Long userId, String from) {
        Task task = this.getById(taskId);
        if (null == task) {
            return Boolean.FALSE;
@@ -153,7 +161,7 @@
        task.setTaskSts(TaskStsType.CANCEL.val());
        task.setUpdateTime(now);
        task.setUpdateBy(userId);
        task.setMemo(Constants.HANDLE + " " + TaskStsType.CANCEL);
        task.setMemo(from + " " + TaskStsType.CANCEL);
        if (!this.updateById(task)) {
            throw new CoolException(BaseRes.ERROR);
        }
zy-acs-manager/src/main/java/integrationRecord.sql
New file
@@ -0,0 +1,7 @@
-- save integrationRecord record
-- mysql
-- SwapHorizontalCircle
insert into `sys_menu` ( `name`, `parent_id`, `route`, `component`, `type`, `sort`, `tenant_id`, `status`) values ( 'menu.integrationRecord', '0', '/manager/integrationRecord', 'integrationRecord', '0' , '5', '1' , '1');
insert into `sys_menu` ( `name`, `parent_id`, `type`, `authority`, `sort`, `tenant_id`, `status`) values ( 'Query Integration Record', '', '1', 'manager:integrationRecord:list', '0', '1', '1');
insert into `sys_menu` ( `name`, `parent_id`, `type`, `authority`, `sort`, `tenant_id`, `status`) values ( 'Delete Integration Record', '', '1', 'manager:integrationRecord:remove', '3', '1', '1');
zy-acs-manager/src/main/resources/application.yml
@@ -53,6 +53,14 @@
convey-plc:
  host: 10.10.10.222
  port: 9090
  url: /api/open/task/report
  timeout: 15000
uplink:
  enabled: true
  host: 10.10.10.222
  port: 9090
  timeout: 30000
floyd:
  enable: false
zy-acs-manager/src/main/resources/mapper/manager/IntegrationRecordMapper.xml
New file
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.zy.acs.manager.manager.mapper.IntegrationRecordMapper">
</mapper>
zy-acs-manager/src/main/resources/mapper/manager/TaskMapper.xml
@@ -50,6 +50,9 @@
            <if test="param.timeEnd != null">
                and a.create_time &lt; #{param.timeEnd}
            </if>
            <if test="param.uplinkSts != null">
                AND a.uplink_sts = #{param.uplinkSts}
            </if>
            <if test="param.keywords != null">
                AND (
                a.memo LIKE CONCAT('%', #{param.keywords}, '%')