已添加18个文件
已重命名46个文件
已修改10个文件
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import java.nio.charset.StandardCharsets; |
| | | import java.nio.file.Files; |
| | | import java.nio.file.Paths; |
| | | import java.sql.Connection; |
| | | import java.sql.DriverManager; |
| | | import java.sql.Statement; |
| | | import java.util.ArrayList; |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * æ 人å¼å®åçº§èæ¬æ§è¡å¨ãç´æ¥è¯»å application-tide.yml ä¸ç主åºé
ç½®ï¼ |
| | | * é¿å
å¨å½ä»¤è¡ãæ¥å¿æä¸´æ¶æä»¶ä¸å¤å¶æ°æ®åºå£ä»¤ã |
| | | */ |
| | | public class DatabaseUpgrade { |
| | | public static void main(String[] args) throws Exception { |
| | | String config = args.length > 0 ? args[0] : "src/main/resources/application-tide.yml"; |
| | | String script = args.length > 1 ? args[1] : "doc/unattended_upgrade.sql"; |
| | | List<String> yaml = Files.readAllLines(Paths.get(config), StandardCharsets.UTF_8); |
| | | String url = firstValue(yaml, "url: jdbc:mysql:"); |
| | | String username = valueAfter(yaml, "url: jdbc:mysql:", "username:"); |
| | | String password = valueAfter(yaml, "url: jdbc:mysql:", "password:"); |
| | | if (url == null || username == null) throw new IllegalStateException("æªæ¾å°ä¸»æ°æ®åºé
ç½®"); |
| | | |
| | | Class.forName("com.mysql.cj.jdbc.Driver"); |
| | | try (Connection connection = DriverManager.getConnection(url, username, password); |
| | | Statement statement = connection.createStatement()) { |
| | | int count = 0; |
| | | for (String sql : splitSql(Files.readAllLines(Paths.get(script), StandardCharsets.UTF_8))) { |
| | | if (!sql.trim().isEmpty()) { |
| | | statement.execute(sql); |
| | | count++; |
| | | } |
| | | } |
| | | System.out.println("æ 人å¼å®æ°æ®åºåçº§å®æï¼å
±æ§è¡ " + count + " æ¡è¯å¥ã"); |
| | | } |
| | | } |
| | | |
| | | private static String firstValue(List<String> lines, String marker) { |
| | | for (String line : lines) if (line.trim().startsWith(marker)) return line.trim().substring(5).trim(); |
| | | return null; |
| | | } |
| | | |
| | | private static String valueAfter(List<String> lines, String anchor, String key) { |
| | | boolean found = false; |
| | | for (String line : lines) { |
| | | String value = line.trim(); |
| | | if (value.startsWith(anchor)) found = true; |
| | | else if (found && value.startsWith(key)) return value.substring(key.length()).trim(); |
| | | } |
| | | return null; |
| | | } |
| | | |
| | | private static List<String> splitSql(List<String> lines) { |
| | | List<String> result = new ArrayList<>(); |
| | | String delimiter = ";"; |
| | | StringBuilder current = new StringBuilder(); |
| | | for (String line : lines) { |
| | | String trimmed = line.trim(); |
| | | if (trimmed.startsWith("--") || trimmed.isEmpty()) continue; |
| | | if (trimmed.toUpperCase().startsWith("DELIMITER ")) { |
| | | delimiter = trimmed.substring("DELIMITER ".length()).trim(); |
| | | continue; |
| | | } |
| | | current.append(line).append('\n'); |
| | | if (trimmed.endsWith(delimiter)) { |
| | | int end = current.length() - delimiter.length() - 1; |
| | | result.add(current.substring(0, Math.max(0, end)).trim()); |
| | | current.setLength(0); |
| | | } |
| | | } |
| | | if (current.length() > 0) result.add(current.toString().trim()); |
| | | return result; |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | -- ============================================================ |
| | | -- æ 人å¼å®ç³»ç» åè½è¡¥å
¨åçº§èæ¬ |
| | | -- æ°æ®åº: wrzsxt-ruoyi-xinlan |
| | | -- 说æ: å
¨é¨ä½¿ç¨ IF NOT EXISTS / 卿夿ï¼å¯é夿§è¡ |
| | | -- ============================================================ |
| | | |
| | | USE `wrzsxt-ruoyi-xinlan`; |
| | | |
| | | -- ------------------------------------------------------------ |
| | | -- 1. 设å¤è¿ç¨çæ§ï¼é¥æµæ°æ®è¡¨ |
| | | -- ------------------------------------------------------------ |
| | | CREATE TABLE IF NOT EXISTS `device_telemetry` ( |
| | | `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主é®', |
| | | `device_ledger_id` bigint NOT NULL COMMENT '设å¤å°è´¦id', |
| | | `device_name` varchar(255) DEFAULT NULL COMMENT '设å¤åç§°åä½', |
| | | `temperature` decimal(10,2) DEFAULT NULL COMMENT '温度(â)', |
| | | `pressure` decimal(10,2) DEFAULT NULL COMMENT 'åå(MPa)', |
| | | `vibration` decimal(10,2) DEFAULT NULL COMMENT 'æ¯å¨(mm/s)', |
| | | `energy` decimal(12,2) DEFAULT NULL COMMENT 'è½è(kWh)', |
| | | `status` varchar(20) DEFAULT 'normal' COMMENT 'ç¶æ normal/warning/alarm', |
| | | `alarm_content` varchar(500) DEFAULT NULL COMMENT 'æ¥è¦å
容', |
| | | `collect_time` datetime NOT NULL COMMENT 'ééæ¶é´', |
| | | `create_time` datetime DEFAULT NULL, |
| | | `tenant_id` bigint DEFAULT NULL, |
| | | PRIMARY KEY (`id`), |
| | | KEY `idx_telemetry_device_time` (`device_ledger_id`, `collect_time`), |
| | | KEY `idx_telemetry_time` (`collect_time`) |
| | | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='设å¤é¥æµå®æ¶æ°æ®'; |
| | | |
| | | -- 设å¤éå¼/è¿ç¨æ§å¶é
ç½®ï¼å¤ç¨ device_ledgerï¼ç¼ºåæ¶å¨ææ·»å ï¼ |
| | | DROP PROCEDURE IF EXISTS `sp_add_column_if_absent`; |
| | | DELIMITER $$ |
| | | CREATE PROCEDURE `sp_add_column_if_absent`( |
| | | IN p_table VARCHAR(64), IN p_column VARCHAR(64), IN p_ddl VARCHAR(500)) |
| | | BEGIN |
| | | IF NOT EXISTS ( |
| | | SELECT 1 FROM information_schema.columns |
| | | WHERE table_schema = DATABASE() AND table_name = p_table AND column_name = p_column |
| | | ) THEN |
| | | SET @s = CONCAT('ALTER TABLE `', p_table, '` ADD COLUMN ', p_ddl); |
| | | PREPARE stmt FROM @s; EXECUTE stmt; DEALLOCATE PREPARE stmt; |
| | | END IF; |
| | | END$$ |
| | | DELIMITER ; |
| | | |
| | | CALL sp_add_column_if_absent('device_ledger', 'temp_min', '`temp_min` decimal(10,2) DEFAULT 20 COMMENT ''温度ä¸é'''); |
| | | CALL sp_add_column_if_absent('device_ledger', 'temp_max', '`temp_max` decimal(10,2) DEFAULT 80 COMMENT ''温度ä¸é'''); |
| | | CALL sp_add_column_if_absent('device_ledger', 'pressure_min', '`pressure_min` decimal(10,2) DEFAULT 0.20 COMMENT ''ååä¸é'''); |
| | | CALL sp_add_column_if_absent('device_ledger', 'pressure_max', '`pressure_max` decimal(10,2) DEFAULT 1.50 COMMENT ''ååä¸é'''); |
| | | CALL sp_add_column_if_absent('device_ledger', 'vibration_max', '`vibration_max` decimal(10,2) DEFAULT 5.00 COMMENT ''æ¯å¨ä¸é'''); |
| | | CALL sp_add_column_if_absent('device_ledger', 'control_status', '`control_status` varchar(20) DEFAULT ''stopped'' COMMENT ''è¿ç¨æ§å¶ç¶æ running/stopped'''); |
| | | CALL sp_add_column_if_absent('device_ledger', 'telemetry_enabled', '`telemetry_enabled` tinyint DEFAULT 1 COMMENT ''æ¯å¦å¯ç¨é¥æµ'''); |
| | | |
| | | -- ------------------------------------------------------------ |
| | | -- 2. é¨ç¦ç®¡ç |
| | | -- ------------------------------------------------------------ |
| | | CREATE TABLE IF NOT EXISTS `access_gate` ( |
| | | `id` bigint NOT NULL AUTO_INCREMENT, |
| | | `gate_name` varchar(120) NOT NULL COMMENT 'é¨ç¦åç§°', |
| | | `area` varchar(120) DEFAULT NULL COMMENT 'åºå', |
| | | `location` varchar(255) DEFAULT NULL COMMENT 'ä½ç½®', |
| | | `weighbridge_number` varchar(64) DEFAULT NULL COMMENT 'å
³èå°ç£
ç¼å·', |
| | | `gate_type` varchar(20) DEFAULT 'vehicle' COMMENT 'ç±»å vehicle-è½¦è¾ person-人å all-æ··å', |
| | | `device_status` varchar(20) DEFAULT 'online' COMMENT '设å¤ç¶æ online/offline', |
| | | `maintainer` varchar(64) DEFAULT NULL COMMENT 'ç»´æ¤äºº', |
| | | `maintenance_time` date DEFAULT NULL COMMENT 'ç»´æ¤æ¶é´', |
| | | `remark` varchar(500) DEFAULT NULL, |
| | | `create_user` bigint DEFAULT NULL, |
| | | `create_time` datetime DEFAULT NULL, |
| | | `update_user` bigint DEFAULT NULL, |
| | | `update_time` datetime DEFAULT NULL, |
| | | `tenant_id` bigint DEFAULT NULL, |
| | | PRIMARY KEY (`id`) |
| | | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='é¨ç¦è®¾å¤'; |
| | | |
| | | CREATE TABLE IF NOT EXISTS `access_record` ( |
| | | `id` bigint NOT NULL AUTO_INCREMENT, |
| | | `gate_id` bigint DEFAULT NULL COMMENT 'é¨ç¦id', |
| | | `gate_name` varchar(120) DEFAULT NULL COMMENT 'é¨ç¦åç§°åä½', |
| | | `access_type` varchar(20) NOT NULL COMMENT 'ç±»å vehicle/person', |
| | | `plate_number` varchar(32) DEFAULT NULL COMMENT '车çå·', |
| | | `rfid_tag` varchar(64) DEFAULT NULL COMMENT 'RFIDæ ç¾', |
| | | `carrier_name` varchar(120) DEFAULT NULL COMMENT 'æ¿è¿å', |
| | | `person_name` varchar(64) DEFAULT NULL COMMENT '人åå§å', |
| | | `id_card_face` varchar(64) DEFAULT NULL COMMENT '人è¸è¯å«ç»æ', |
| | | `work_card_no` varchar(64) DEFAULT NULL COMMENT 'å·¥å¡å·', |
| | | `direction` varchar(10) DEFAULT 'in' COMMENT 'æ¹å in/out', |
| | | `verify_result` varchar(20) DEFAULT 'pass' COMMENT 'æ ¸éªç»æ pass/reject', |
| | | `verify_method` varchar(64) DEFAULT NULL COMMENT 'æ ¸éªæ¹å¼ 车çè¯å«/RFID/人è¸è¯å«/å·¥å¡', |
| | | `release_status` varchar(20) DEFAULT 'released' COMMENT 'æ¾è¡ç¶æ released/blocked', |
| | | `weighbridge_linked` tinyint DEFAULT 0 COMMENT 'æ¯å¦å°ç£
è卿¾è¡', |
| | | `weighbridge_record_id` bigint DEFAULT NULL COMMENT 'å
³èå°ç£
è®°å½id', |
| | | `reject_reason` varchar(255) DEFAULT NULL COMMENT 'æç»åå ', |
| | | `access_time` datetime NOT NULL COMMENT 'éè¡æ¶é´', |
| | | `create_time` datetime DEFAULT NULL, |
| | | `tenant_id` bigint DEFAULT NULL, |
| | | PRIMARY KEY (`id`), |
| | | KEY `idx_access_time` (`access_time`), |
| | | KEY `idx_access_plate` (`plate_number`) |
| | | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='é¨ç¦è¿åºè®°å½'; |
| | | |
| | | -- ------------------------------------------------------------ |
| | | -- 3. æ 人å¼å®å°ç£
ï¼æ©å±å段 |
| | | -- ------------------------------------------------------------ |
| | | CALL sp_add_column_if_absent('unattended_operation', 'net_weight', '`net_weight` decimal(12,2) DEFAULT NULL COMMENT ''åé(kg)'''); |
| | | CALL sp_add_column_if_absent('unattended_operation', 'carrier_name', '`carrier_name` varchar(120) DEFAULT NULL COMMENT ''æ¿è¿å'''); |
| | | CALL sp_add_column_if_absent('unattended_operation', 'order_no', '`order_no` varchar(120) DEFAULT NULL COMMENT ''å¹é
ç订åå·'''); |
| | | CALL sp_add_column_if_absent('unattended_operation', 'order_type', '`order_type` varchar(20) DEFAULT NULL COMMENT ''purchase/sales'''); |
| | | CALL sp_add_column_if_absent('unattended_operation', 'order_matched', '`order_matched` tinyint DEFAULT 0 COMMENT ''æ¯å¦å¹é
订å'''); |
| | | CALL sp_add_column_if_absent('unattended_operation', 'cheat_flag', '`cheat_flag` tinyint DEFAULT 0 COMMENT ''æ¯å¦ä½å¼/å¼å¸¸'''); |
| | | CALL sp_add_column_if_absent('unattended_operation', 'cheat_reason', '`cheat_reason` varchar(255) DEFAULT NULL COMMENT ''å¼å¸¸åå '''); |
| | | CALL sp_add_column_if_absent('unattended_operation', 'infrared_status', '`infrared_status` varchar(20) DEFAULT ''normal'' COMMENT ''çº¢å¤æ£æµ normal/offset/alarm'''); |
| | | CALL sp_add_column_if_absent('unattended_operation', 'erp_push_status', '`erp_push_status` varchar(20) DEFAULT ''pending'' COMMENT ''ERPæ¨é pending/success/failed'''); |
| | | CALL sp_add_column_if_absent('unattended_operation', 'erp_push_time', '`erp_push_time` datetime DEFAULT NULL COMMENT ''ERPæ¨éæ¶é´'''); |
| | | CALL sp_add_column_if_absent('unattended_operation', 'qr_code', '`qr_code` varchar(255) DEFAULT NULL COMMENT ''ç£
åäºç»´ç å
容'''); |
| | | CALL sp_add_column_if_absent('unattended_operation', 'weigh_no', '`weigh_no` varchar(64) DEFAULT NULL COMMENT ''ç£
åå·ï¼å¯ä¸'''); |
| | | CALL sp_add_column_if_absent('unattended_operation', 'weigh_status', '`weigh_status` varchar(20) DEFAULT ''done'' COMMENT ''ç§°éç¶æ weighing/done/abnormal'''); |
| | | |
| | | -- ------------------------------------------------------------ |
| | | -- 4. æºè½å·¡æ£ï¼è¡¥é½è·¯å¾è§å/æ¥ååæ®µ |
| | | -- ------------------------------------------------------------ |
| | | CALL sp_add_column_if_absent('inspection_task', 'inspection_route', '`inspection_route` text COMMENT ''å·¡æ£è·¯å¾(JSONç¹ä½)'''); |
| | | CALL sp_add_column_if_absent('inspection_task', 'task_status', '`task_status` varchar(20) DEFAULT ''pending'' COMMENT ''pending/running/finished'''); |
| | | CALL sp_add_column_if_absent('inspection_task', 'report_content', '`report_content` text COMMENT ''èªå¨çæçå·¡æ£æ¥å'''); |
| | | CALL sp_add_column_if_absent('inspection_task', 'report_time', '`report_time` datetime DEFAULT NULL COMMENT ''æ¥åçææ¶é´'''); |
| | | CALL sp_add_column_if_absent('inspection_task', 'device_source', '`device_source` varchar(20) DEFAULT ''manual'' COMMENT ''manual/video/drone/robot'''); |
| | | CALL sp_add_column_if_absent('inspection_task', 'actual_start_time', '`actual_start_time` datetime DEFAULT NULL'); |
| | | CALL sp_add_column_if_absent('inspection_task', 'actual_end_time', '`actual_end_time` datetime DEFAULT NULL'); |
| | | |
| | | -- ------------------------------------------------------------ |
| | | -- 5. æ¿è¿å绩æï¼ç±å°ç£
æ°æ®èåï¼è¡¥å
åºç¡ä¿¡æ¯è¡¨ï¼ |
| | | -- ------------------------------------------------------------ |
| | | CREATE TABLE IF NOT EXISTS `carrier_info` ( |
| | | `id` bigint NOT NULL AUTO_INCREMENT, |
| | | `carrier_name` varchar(120) NOT NULL COMMENT 'æ¿è¿ååç§°', |
| | | `contact_person` varchar(64) DEFAULT NULL, |
| | | `contact_phone` varchar(32) DEFAULT NULL, |
| | | `status` varchar(20) DEFAULT 'normal', |
| | | `create_time` datetime DEFAULT NULL, |
| | | `update_time` datetime DEFAULT NULL, |
| | | `tenant_id` bigint DEFAULT NULL, |
| | | PRIMARY KEY (`id`), |
| | | UNIQUE KEY `uk_carrier_name` (`carrier_name`) |
| | | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='æ¿è¿ååºç¡ä¿¡æ¯'; |
| | | |
| | | -- ------------------------------------------------------------ |
| | | -- 6. èåï¼è¡¥é½äºä¸ªä¸å¡æ¨¡åå
¥å£ï¼å¹çï¼ |
| | | -- ------------------------------------------------------------ |
| | | INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, remark) |
| | | SELECT 'é¨ç¦è®¾å¤ç®¡ç', 2077, 6, 'accessGate', 'environmentAccess/accessManagement/index', 1, 0, 'C', '0', '0', '', 'lock', 'admin', NOW(), 'é¨ç¦è®¾å¤' |
| | | FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM sys_menu WHERE parent_id=2077 AND path='accessGate'); |
| | | |
| | | INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, remark) |
| | | SELECT '设å¤è¿ç¨çæ§', 2077, 1, 'deviceRemoteMonitoring', 'environmentAccess/deviceRemoteMonitoring/index', 1, 0, 'C', '0', '0', '', 'monitor', 'admin', NOW(), '温度ãååãè½èçæµä¸è¿ç¨å¯å' |
| | | FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM sys_menu WHERE parent_id=2077 AND path='deviceRemoteMonitoring'); |
| | | |
| | | INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, remark) |
| | | SELECT 'æºè½å·¡æ£ç®¡ç', 2077, 2, 'intelligentInspectionManagement', 'environmentAccess/intelligentInspectionManagement/index', 1, 0, 'C', '0', '0', '', 'guide', 'admin', NOW(), 'è·¯å¾è§åãè§é¢åæãæ 人æº/æºå¨äººèå¨ä¸èªå¨æ¥å' |
| | | FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM sys_menu WHERE parent_id=2077 AND path='intelligentInspectionManagement'); |
| | | |
| | | INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, remark) |
| | | SELECT 'æ 人å¼å®å°ç£
', 2077, 3, 'unattendedWeighbridgeSystem', 'environmentAccess/unattendedWeighbridgeSystem/index', 1, 0, 'C', '0', '0', '', 'form', 'admin', NOW(), 'èªå¨ç§°éã红å¤é²ä½å¼ãERPä¸äºç»´ç ç£
å' |
| | | FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM sys_menu WHERE parent_id=2077 AND path='unattendedWeighbridgeSystem'); |
| | | |
| | | INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, remark) |
| | | SELECT 'æ¥è¡¨ä¸å¿', 2077, 7, 'unattendedReportCenter', 'environmentAccess/reportCenter/index', 1, 0, 'C', '0', '0', '', 'chart', 'admin', NOW(), 'æ¯æ¥è¿ç£
䏿¿è¿å绩æ' |
| | | FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM sys_menu WHERE parent_id=2077 AND path='unattendedReportCenter'); |
| | | |
| | | -- ------------------------------------------------------------ |
| | | -- 7. åå²å°ç£
è®°å½åå¡«ï¼é¿å
çµåç£
åäºç»´ç åºç° null |
| | | -- ------------------------------------------------------------ |
| | | UPDATE unattended_operation |
| | | SET net_weight = ROUND(gross_weight - tare_weight, 2) |
| | | WHERE net_weight IS NULL AND gross_weight IS NOT NULL AND tare_weight IS NOT NULL; |
| | | |
| | | UPDATE unattended_operation |
| | | SET weigh_no = CONCAT('WB', DATE_FORMAT(COALESCE(weight_time, create_time, NOW()), '%Y%m%d%H%i%s'), LPAD(MOD(id, 1000), 3, '0')) |
| | | WHERE weigh_no IS NULL OR weigh_no = '' OR LOWER(weigh_no) = 'null'; |
| | | |
| | | UPDATE unattended_operation |
| | | SET qr_code = CONCAT_WS('|', |
| | | COALESCE(NULLIF(weigh_no, 'null'), ''), |
| | | COALESCE(NULLIF(plate_number, 'null'), ''), |
| | | COALESCE(CAST(net_weight AS CHAR), ''), |
| | | COALESCE(DATE_FORMAT(weight_time, '%Y-%m-%d %H:%i:%s'), ''), |
| | | COALESCE(NULLIF(carrier_name, 'null'), ''), |
| | | COALESCE(NULLIF(weighbridge_number, 'null'), '') |
| | | ); |
| | | |
| | | DROP PROCEDURE IF EXISTS `sp_add_column_if_absent`; |
| | |
| | | |
| | | @ApiModelProperty("è¿è¡æ¶é¿") |
| | | private String runtimeDuration; |
| | | |
| | | /* *************************** è¿ç¨çæ§ *************************** */ |
| | | |
| | | @ApiModelProperty("温度ä¸é") |
| | | private BigDecimal tempMin; |
| | | |
| | | @ApiModelProperty("温度ä¸é") |
| | | private BigDecimal tempMax; |
| | | |
| | | @ApiModelProperty("ååä¸é") |
| | | private BigDecimal pressureMin; |
| | | |
| | | @ApiModelProperty("ååä¸é") |
| | | private BigDecimal pressureMax; |
| | | |
| | | @ApiModelProperty("æ¯å¨ä¸é") |
| | | private BigDecimal vibrationMax; |
| | | |
| | | @ApiModelProperty("è¿ç¨æ§å¶ç¶æ running/stopped") |
| | | private String controlStatus; |
| | | |
| | | @ApiModelProperty("æ¯å¦å¯ç¨é¥æµ") |
| | | private Integer telemetryEnabled; |
| | | } |
| | |
| | | import org.springframework.web.bind.annotation.*; |
| | | |
| | | import javax.servlet.http.HttpServletResponse; |
| | | import java.time.LocalDateTime; |
| | | |
| | | /** |
| | | * @author :yys |
| | |
| | | return R.ok(inspectionTaskService.delByIds(ids)); |
| | | } |
| | | |
| | | @PostMapping("/start/{id}") |
| | | @ApiOperation("å¯å¨å·¡æ£å¹¶èå¨éé设å¤") |
| | | public R start(@PathVariable Long id) { |
| | | InspectionTask task = inspectionTaskService.getById(id); |
| | | if (task == null) return R.fail("å·¡æ£ä»»å¡ä¸åå¨"); |
| | | if ("finished".equals(task.getTaskStatus())) return R.fail("ä»»å¡å·²å®æï¼æ ééå¤å¯å¨"); |
| | | if ("running".equals(task.getTaskStatus())) return R.fail("ä»»å¡å·²å¨æ§è¡ä¸"); |
| | | task.setTaskStatus("running"); |
| | | task.setActualStartTime(LocalDateTime.now()); |
| | | task.setReportContent(null); |
| | | task.setReportTime(null); |
| | | inspectionTaskService.updateById(task); |
| | | return R.ok(task); |
| | | } |
| | | |
| | | @PostMapping("/finish/{id}") |
| | | @ApiOperation("宿巡æ£å¹¶èªå¨çææ¥å") |
| | | public R finish(@PathVariable Long id) { |
| | | InspectionTask task = inspectionTaskService.getById(id); |
| | | if (task == null) return R.fail("å·¡æ£ä»»å¡ä¸åå¨"); |
| | | if (!"running".equals(task.getTaskStatus())) return R.fail("请å
å¼å§å·¡æ£å宿"); |
| | | LocalDateTime now = LocalDateTime.now(); |
| | | task.setTaskStatus("finished"); |
| | | task.setActualEndTime(now); |
| | | task.setReportTime(now); |
| | | |
| | | String sourceLabel = sourceLabel(task.getDeviceSource()); |
| | | String duration = "æªè®°å½"; |
| | | if (task.getActualStartTime() != null) { |
| | | long minutes = java.time.Duration.between(task.getActualStartTime(), now).toMinutes(); |
| | | duration = minutes + " åé"; |
| | | } |
| | | StringBuilder report = new StringBuilder(); |
| | | report.append("å·¡æ£æ¥å\n") |
| | | .append("ä»»å¡åç§°ï¼").append(nvl(task.getTaskName())).append("\n") |
| | | .append("å·¡æ£åºåï¼").append(nvl(task.getInspectionLocation())).append("\n") |
| | | .append("è§åè·¯å¾ï¼").append(nvl(task.getInspectionRoute())).append("\n") |
| | | .append("ééç»ç«¯ï¼").append(sourceLabel).append("\n") |
| | | .append("å·¡æ£äººåï¼").append(nvl(task.getInspector())).append("\n") |
| | | .append("å¼å§æ¶é´ï¼").append(nvl(task.getActualStartTime())).append("\n") |
| | | .append("ç»ææ¶é´ï¼").append(now).append("\n") |
| | | .append("æ§è¡æ¶é¿ï¼").append(duration).append("\n\n") |
| | | .append("å·¡æ£ç»è®ºï¼æè§åè·¯å¾å®æå
¨é¨ç¹ä½å·¡æ£ï¼æªåç°éç«å³å¤ç½®çé大å¼å¸¸ï¼") |
| | | .append("ç°åºå½±åã设å¤åä¼ ä¸ç¹ä½è®°å½å·²å½æ¡£ã\n") |
| | | .append("çææ¶é´ï¼").append(now); |
| | | task.setReportContent(report.toString()); |
| | | inspectionTaskService.updateById(task); |
| | | return R.ok(task); |
| | | } |
| | | |
| | | private static String sourceLabel(String source) { |
| | | if (source == null) return "人工ç»ç«¯"; |
| | | switch (source) { |
| | | case "video": return "è§é¢åæ"; |
| | | case "drone": return "æ 人æº"; |
| | | case "robot": return "å·¡æ£æºå¨äºº"; |
| | | case "manual": return "人工ç»ç«¯"; |
| | | default: return source; |
| | | } |
| | | } |
| | | |
| | | private static String nvl(Object v) { |
| | | return v == null ? "â" : v.toString(); |
| | | } |
| | | |
| | | } |
| | |
| | | @ApiModelProperty(value = "å·¡æ£å°ç¹è¯¦ç»æè¿°") |
| | | private String inspectionLocation; |
| | | |
| | | @ApiModelProperty(value = "å·¡æ£è·¯å¾(JSONç¹ä½)") |
| | | private String inspectionRoute; |
| | | |
| | | @ApiModelProperty(value = "ä»»å¡ç¶æ pending/running/finished") |
| | | private String taskStatus; |
| | | |
| | | @ApiModelProperty(value = "èªå¨çæçå·¡æ£æ¥å") |
| | | private String reportContent; |
| | | |
| | | @ApiModelProperty(value = "æ¥åçææ¶é´") |
| | | @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") |
| | | private LocalDateTime reportTime; |
| | | |
| | | @ApiModelProperty(value = "ä¿¡æ¯æ¥æº manual/video/drone/robot") |
| | | private String deviceSource; |
| | | |
| | | @ApiModelProperty(value = "å®é
å¼å§æ¶é´") |
| | | @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") |
| | | private LocalDateTime actualStartTime; |
| | | |
| | | @ApiModelProperty(value = "å®é
ç»ææ¶é´") |
| | | @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") |
| | | private LocalDateTime actualEndTime; |
| | | |
| | | @ApiModelProperty(value = "软å 餿 å¿ï¼0=æªå é¤ï¼1=å·²å é¤") |
| | | private Integer deleted; |
| | | |
| | |
| | | @Override |
| | | public IPage<InspectionTaskDto> selectInspectionTaskList(Page<InspectionTask> page, InspectionTaskDto inspectionTaskDto) { |
| | | LambdaQueryWrapper<InspectionTask> queryWrapper = new LambdaQueryWrapper<>(); |
| | | queryWrapper.orderByDesc(InspectionTask::getCreateTime); |
| | | queryWrapper.and(w -> w.isNull(InspectionTask::getDeleted).or().eq(InspectionTask::getDeleted, 0)) |
| | | .like(StringUtils.isNotBlank(inspectionTaskDto.getTaskName()), |
| | | InspectionTask::getTaskName, inspectionTaskDto.getTaskName()) |
| | | .like(StringUtils.isNotBlank(inspectionTaskDto.getInspectionLocation()), |
| | | InspectionTask::getInspectionLocation, inspectionTaskDto.getInspectionLocation()) |
| | | .eq(StringUtils.isNotBlank(inspectionTaskDto.getTaskStatus()), |
| | | InspectionTask::getTaskStatus, inspectionTaskDto.getTaskStatus()) |
| | | .eq(StringUtils.isNotBlank(inspectionTaskDto.getDeviceSource()), |
| | | InspectionTask::getDeviceSource, inspectionTaskDto.getDeviceSource()) |
| | | .orderByDesc(InspectionTask::getCreateTime); |
| | | IPage<InspectionTask> entityPage = inspectionTaskMapper.selectPage(page, queryWrapper); |
| | | |
| | | // æ æ°æ®æåè¿å |
| | |
| | | // è·åidéå |
| | | List<Long> ids = entityPage.getRecords().stream().map(InspectionTask::getId).collect(Collectors.toList()); |
| | | //ç»è®°äººids |
| | | List<Long> registrantIds = entityPage.getRecords().stream().map(InspectionTask::getRegistrantId).collect(Collectors.toList()); |
| | | List<Long> registrantIds = entityPage.getRecords().stream() |
| | | .map(InspectionTask::getRegistrantId) |
| | | .filter(Objects::nonNull) |
| | | .distinct() |
| | | .collect(Collectors.toList()); |
| | | // æ¹éæ¥è¯¢ç»è®°äºº |
| | | Map<Long, SysUser> sysUserMap; |
| | | if (!registrantIds.isEmpty()) { |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.unattended.access.controller; |
| | | |
| | | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
| | | import com.ruoyi.framework.web.domain.AjaxResult; |
| | | import com.ruoyi.unattended.access.dto.AccessVerifyRequest; |
| | | import com.ruoyi.unattended.access.pojo.AccessGate; |
| | | import com.ruoyi.unattended.access.pojo.AccessRecord; |
| | | import com.ruoyi.unattended.access.pojo.AccessVehicle; |
| | | import com.ruoyi.unattended.access.service.AccessService; |
| | | import io.swagger.annotations.Api; |
| | | import io.swagger.annotations.ApiOperation; |
| | | import org.springframework.util.CollectionUtils; |
| | | import org.springframework.web.bind.annotation.*; |
| | | |
| | | import javax.annotation.Resource; |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * é¨ç¦ç®¡ç |
| | | */ |
| | | @RestController |
| | | @RequestMapping("/environmentAccess/access") |
| | | @Api(tags = "é¨ç¦ç®¡ç") |
| | | public class AccessController { |
| | | |
| | | @Resource |
| | | private AccessService accessService; |
| | | |
| | | /* ---------------- é¨ç¦è®¾å¤ ---------------- */ |
| | | |
| | | @GetMapping("/gate/page") |
| | | @ApiOperation("é¨ç¦è®¾å¤å页") |
| | | public AjaxResult gatePage(Page<AccessGate> page, AccessGate query) { |
| | | return AjaxResult.success(accessService.gatePage(page, query)); |
| | | } |
| | | |
| | | @PostMapping("/gate/add") |
| | | @ApiOperation("æ°å¢é¨ç¦è®¾å¤") |
| | | public AjaxResult gateAdd(@RequestBody AccessGate gate) { |
| | | return AjaxResult.success(accessService.gateSave(gate)); |
| | | } |
| | | |
| | | @PutMapping("/gate/update") |
| | | @ApiOperation("ä¿®æ¹é¨ç¦è®¾å¤") |
| | | public AjaxResult gateUpdate(@RequestBody AccessGate gate) { |
| | | return AjaxResult.success(accessService.gateUpdate(gate)); |
| | | } |
| | | |
| | | @DeleteMapping("/gate/delete") |
| | | @ApiOperation("å é¤é¨ç¦è®¾å¤") |
| | | public AjaxResult gateDelete(@RequestBody List<Long> ids) { |
| | | if (CollectionUtils.isEmpty(ids)) { |
| | | return AjaxResult.error("è¯·éæ©è³å°ä¸æ¡æ°æ®"); |
| | | } |
| | | return AjaxResult.success(accessService.gateRemove(ids)); |
| | | } |
| | | |
| | | /* ---------------- 车è¾éè¡ææ ---------------- */ |
| | | |
| | | @GetMapping("/vehicle/page") |
| | | @ApiOperation("è½¦è¾æ¡£æ¡å页") |
| | | public AjaxResult vehiclePage(Page<AccessVehicle> page, AccessVehicle query) { |
| | | return AjaxResult.success(accessService.vehiclePage(page, query)); |
| | | } |
| | | |
| | | @PostMapping("/vehicle/add") |
| | | @ApiOperation("æ°å¢è½¦è¾æ¡£æ¡") |
| | | public AjaxResult vehicleAdd(@RequestBody AccessVehicle vehicle) { |
| | | return AjaxResult.success(accessService.vehicleSave(vehicle)); |
| | | } |
| | | |
| | | @PutMapping("/vehicle/update") |
| | | @ApiOperation("ä¿®æ¹è½¦è¾æ¡£æ¡") |
| | | public AjaxResult vehicleUpdate(@RequestBody AccessVehicle vehicle) { |
| | | return AjaxResult.success(accessService.vehicleUpdate(vehicle)); |
| | | } |
| | | |
| | | @DeleteMapping("/vehicle/delete") |
| | | @ApiOperation("å é¤è½¦è¾æ¡£æ¡") |
| | | public AjaxResult vehicleDelete(@RequestBody List<Long> ids) { |
| | | if (CollectionUtils.isEmpty(ids)) { |
| | | return AjaxResult.error("è¯·éæ©è³å°ä¸æ¡æ°æ®"); |
| | | } |
| | | return AjaxResult.success(accessService.vehicleRemove(ids)); |
| | | } |
| | | |
| | | /* ---------------- è¯å«æ¾è¡ ---------------- */ |
| | | |
| | | @PostMapping("/verify") |
| | | @ApiOperation("è¯å«æ ¸éªå¹¶æ¾è¡ï¼è½¦ç+RFID / 人è¸+å·¥å¡ï¼æ¯æå°ç£
èå¨ï¼") |
| | | public AjaxResult verify(@RequestBody AccessVerifyRequest request) { |
| | | if (request == null || request.getAccessType() == null) { |
| | | return AjaxResult.error("请æå®è¯å«ç±»å vehicle/person"); |
| | | } |
| | | return AjaxResult.success(accessService.verify(request)); |
| | | } |
| | | |
| | | @GetMapping("/record/page") |
| | | @ApiOperation("è¿åºè®°å½å页") |
| | | public AjaxResult recordPage(Page<AccessRecord> page, AccessRecord query) { |
| | | return AjaxResult.success(accessService.recordPage(page, query)); |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.unattended.access.dto; |
| | | |
| | | import io.swagger.annotations.ApiModelProperty; |
| | | import lombok.Data; |
| | | |
| | | /** |
| | | * é¨ç¦è¯å«è¯·æ± |
| | | */ |
| | | @Data |
| | | public class AccessVerifyRequest { |
| | | |
| | | @ApiModelProperty("é¨ç¦id") |
| | | private Long gateId; |
| | | |
| | | @ApiModelProperty("ç±»å vehicle/person") |
| | | private String accessType; |
| | | |
| | | @ApiModelProperty("车çå·ï¼è½¦çè¯å«ç»æï¼") |
| | | private String plateNumber; |
| | | |
| | | @ApiModelProperty("RFIDæ ç¾") |
| | | private String rfidTag; |
| | | |
| | | @ApiModelProperty("人åå§åï¼äººè¸è¯å«ç»æï¼") |
| | | private String personName; |
| | | |
| | | @ApiModelProperty("äººè¸æ¯å¯¹ç»æ matched/mismatch") |
| | | private String idCardFace; |
| | | |
| | | @ApiModelProperty("å·¥å¡å·") |
| | | private String workCardNo; |
| | | |
| | | @ApiModelProperty("æ¹å in/out") |
| | | private String direction; |
| | | |
| | | @ApiModelProperty("æ¿è¿å") |
| | | private String carrierName; |
| | | |
| | | @ApiModelProperty("å
³èå°ç£
è®°å½idï¼å°ç£
èå¨ï¼") |
| | | private Long weighbridgeRecordId; |
| | | |
| | | @ApiModelProperty("æ¯å¦å°ç£
è卿¾è¡") |
| | | private Boolean weighbridgeLinked; |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.unattended.access.mapper; |
| | | |
| | | import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
| | | import com.ruoyi.unattended.access.pojo.AccessGate; |
| | | import org.apache.ibatis.annotations.Mapper; |
| | | |
| | | @Mapper |
| | | public interface AccessGateMapper extends BaseMapper<AccessGate> { |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.unattended.access.mapper; |
| | | |
| | | import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
| | | import com.ruoyi.unattended.access.pojo.AccessRecord; |
| | | import org.apache.ibatis.annotations.Mapper; |
| | | |
| | | @Mapper |
| | | public interface AccessRecordMapper extends BaseMapper<AccessRecord> { |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.unattended.access.mapper; |
| | | |
| | | import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
| | | import com.ruoyi.unattended.access.pojo.AccessVehicle; |
| | | import org.apache.ibatis.annotations.Mapper; |
| | | |
| | | @Mapper |
| | | public interface AccessVehicleMapper extends BaseMapper<AccessVehicle> { |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.unattended.access.pojo; |
| | | |
| | | import com.baomidou.mybatisplus.annotation.FieldFill; |
| | | import com.baomidou.mybatisplus.annotation.IdType; |
| | | import com.baomidou.mybatisplus.annotation.TableField; |
| | | import com.baomidou.mybatisplus.annotation.TableId; |
| | | import com.baomidou.mybatisplus.annotation.TableName; |
| | | import com.fasterxml.jackson.annotation.JsonFormat; |
| | | import io.swagger.annotations.ApiModel; |
| | | import io.swagger.annotations.ApiModelProperty; |
| | | import lombok.Data; |
| | | import org.springframework.format.annotation.DateTimeFormat; |
| | | |
| | | import java.io.Serializable; |
| | | import java.time.LocalDate; |
| | | import java.time.LocalDateTime; |
| | | |
| | | /** |
| | | * é¨ç¦è®¾å¤ |
| | | */ |
| | | @Data |
| | | @ApiModel |
| | | @TableName("access_gate") |
| | | public class AccessGate implements Serializable { |
| | | |
| | | private static final long serialVersionUID = 1L; |
| | | |
| | | @TableId(type = IdType.AUTO) |
| | | private Long id; |
| | | |
| | | @ApiModelProperty("é¨ç¦åç§°") |
| | | private String gateName; |
| | | |
| | | @ApiModelProperty("åºå") |
| | | private String area; |
| | | |
| | | @ApiModelProperty("ä½ç½®") |
| | | private String location; |
| | | |
| | | @ApiModelProperty("å
³èå°ç£
ç¼å·") |
| | | private String weighbridgeNumber; |
| | | |
| | | @ApiModelProperty("ç±»å vehicle/person/all") |
| | | private String gateType; |
| | | |
| | | @ApiModelProperty("设å¤ç¶æ online/offline") |
| | | private String deviceStatus; |
| | | |
| | | @ApiModelProperty("ç»´æ¤äºº") |
| | | private String maintainer; |
| | | |
| | | @ApiModelProperty("ç»´æ¤æ¶é´") |
| | | @JsonFormat(pattern = "yyyy-MM-dd") |
| | | @DateTimeFormat(pattern = "yyyy-MM-dd") |
| | | private LocalDate maintenanceTime; |
| | | |
| | | @ApiModelProperty("夿³¨") |
| | | private String remark; |
| | | |
| | | @TableField(fill = FieldFill.INSERT) |
| | | private Long createUser; |
| | | |
| | | @TableField(fill = FieldFill.INSERT) |
| | | @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") |
| | | private LocalDateTime createTime; |
| | | |
| | | @TableField(fill = FieldFill.INSERT_UPDATE) |
| | | private Long updateUser; |
| | | |
| | | @TableField(fill = FieldFill.INSERT_UPDATE) |
| | | @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") |
| | | private LocalDateTime updateTime; |
| | | |
| | | @TableField(fill = FieldFill.INSERT) |
| | | private Long tenantId; |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.unattended.access.pojo; |
| | | |
| | | import com.baomidou.mybatisplus.annotation.FieldFill; |
| | | import com.baomidou.mybatisplus.annotation.IdType; |
| | | import com.baomidou.mybatisplus.annotation.TableField; |
| | | import com.baomidou.mybatisplus.annotation.TableId; |
| | | import com.baomidou.mybatisplus.annotation.TableName; |
| | | import com.fasterxml.jackson.annotation.JsonFormat; |
| | | import io.swagger.annotations.ApiModel; |
| | | import io.swagger.annotations.ApiModelProperty; |
| | | import lombok.Data; |
| | | import org.springframework.format.annotation.DateTimeFormat; |
| | | |
| | | import java.io.Serializable; |
| | | import java.time.LocalDateTime; |
| | | |
| | | /** |
| | | * é¨ç¦è¿åºè®°å½ |
| | | */ |
| | | @Data |
| | | @ApiModel |
| | | @TableName("access_record") |
| | | public class AccessRecord implements Serializable { |
| | | |
| | | private static final long serialVersionUID = 1L; |
| | | |
| | | @TableId(type = IdType.AUTO) |
| | | private Long id; |
| | | |
| | | @ApiModelProperty("é¨ç¦id") |
| | | private Long gateId; |
| | | |
| | | @ApiModelProperty("é¨ç¦åç§°") |
| | | private String gateName; |
| | | |
| | | @ApiModelProperty("ç±»å vehicle/person") |
| | | private String accessType; |
| | | |
| | | @ApiModelProperty("车çå·") |
| | | private String plateNumber; |
| | | |
| | | @ApiModelProperty("RFIDæ ç¾") |
| | | private String rfidTag; |
| | | |
| | | @ApiModelProperty("æ¿è¿å") |
| | | private String carrierName; |
| | | |
| | | @ApiModelProperty("人åå§å") |
| | | private String personName; |
| | | |
| | | @ApiModelProperty("人è¸è¯å«ç»æ") |
| | | private String idCardFace; |
| | | |
| | | @ApiModelProperty("å·¥å¡å·") |
| | | private String workCardNo; |
| | | |
| | | @ApiModelProperty("æ¹å in/out") |
| | | private String direction; |
| | | |
| | | @ApiModelProperty("æ ¸éªç»æ pass/reject") |
| | | private String verifyResult; |
| | | |
| | | @ApiModelProperty("æ ¸éªæ¹å¼") |
| | | private String verifyMethod; |
| | | |
| | | @ApiModelProperty("æ¾è¡ç¶æ released/blocked") |
| | | private String releaseStatus; |
| | | |
| | | @ApiModelProperty("æ¯å¦å°ç£
è卿¾è¡") |
| | | private Integer weighbridgeLinked; |
| | | |
| | | @ApiModelProperty("å
³èå°ç£
è®°å½id") |
| | | private Long weighbridgeRecordId; |
| | | |
| | | @ApiModelProperty("æç»åå ") |
| | | private String rejectReason; |
| | | |
| | | @ApiModelProperty("éè¡æ¶é´") |
| | | @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") |
| | | @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") |
| | | private LocalDateTime accessTime; |
| | | |
| | | @TableField(fill = FieldFill.INSERT) |
| | | @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") |
| | | private LocalDateTime createTime; |
| | | |
| | | @TableField(fill = FieldFill.INSERT) |
| | | private Long tenantId; |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.unattended.access.pojo; |
| | | |
| | | import com.baomidou.mybatisplus.annotation.FieldFill; |
| | | import com.baomidou.mybatisplus.annotation.IdType; |
| | | import com.baomidou.mybatisplus.annotation.TableField; |
| | | import com.baomidou.mybatisplus.annotation.TableId; |
| | | import com.baomidou.mybatisplus.annotation.TableName; |
| | | import com.fasterxml.jackson.annotation.JsonFormat; |
| | | import io.swagger.annotations.ApiModel; |
| | | import io.swagger.annotations.ApiModelProperty; |
| | | import lombok.Data; |
| | | import org.springframework.format.annotation.DateTimeFormat; |
| | | |
| | | import java.io.Serializable; |
| | | import java.time.LocalDate; |
| | | import java.time.LocalDateTime; |
| | | |
| | | /** |
| | | * 车è¾éè¡ææï¼è½¦çè¯å« + RFID æ ç¾ï¼ |
| | | */ |
| | | @Data |
| | | @ApiModel |
| | | @TableName("access_vehicle") |
| | | public class AccessVehicle implements Serializable { |
| | | |
| | | private static final long serialVersionUID = 1L; |
| | | |
| | | @TableId(type = IdType.AUTO) |
| | | private Long id; |
| | | |
| | | @ApiModelProperty("车çå·") |
| | | private String plateNumber; |
| | | |
| | | @ApiModelProperty("车çé¢è²") |
| | | private String plateColor; |
| | | |
| | | @ApiModelProperty("RFIDæ ç¾") |
| | | private String rfidTag; |
| | | |
| | | @ApiModelProperty("æ¿è¿å") |
| | | private String carrierName; |
| | | |
| | | @ApiModelProperty("车è¾ç±»å") |
| | | private String vehicleType; |
| | | |
| | | @ApiModelProperty("叿ºå§å") |
| | | private String driverName; |
| | | |
| | | @ApiModelProperty("叿ºçµè¯") |
| | | private String driverPhone; |
| | | |
| | | @ApiModelProperty("æææå¼å§") |
| | | @JsonFormat(pattern = "yyyy-MM-dd") |
| | | @DateTimeFormat(pattern = "yyyy-MM-dd") |
| | | private LocalDate validStart; |
| | | |
| | | @ApiModelProperty("æææç»æ") |
| | | @JsonFormat(pattern = "yyyy-MM-dd") |
| | | @DateTimeFormat(pattern = "yyyy-MM-dd") |
| | | private LocalDate validEnd; |
| | | |
| | | @ApiModelProperty("ç¶æ valid/disabled") |
| | | private String status; |
| | | |
| | | @ApiModelProperty("夿³¨") |
| | | private String remark; |
| | | |
| | | @TableField(fill = FieldFill.INSERT) |
| | | private Long createUser; |
| | | |
| | | @TableField(fill = FieldFill.INSERT) |
| | | @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") |
| | | private LocalDateTime createTime; |
| | | |
| | | @TableField(fill = FieldFill.INSERT_UPDATE) |
| | | private Long updateUser; |
| | | |
| | | @TableField(fill = FieldFill.INSERT_UPDATE) |
| | | @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") |
| | | private LocalDateTime updateTime; |
| | | |
| | | @TableField(fill = FieldFill.INSERT) |
| | | private Long tenantId; |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.unattended.access.service; |
| | | |
| | | import com.baomidou.mybatisplus.core.metadata.IPage; |
| | | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
| | | import com.ruoyi.unattended.access.dto.AccessVerifyRequest; |
| | | import com.ruoyi.unattended.access.pojo.AccessGate; |
| | | import com.ruoyi.unattended.access.pojo.AccessRecord; |
| | | import com.ruoyi.unattended.access.pojo.AccessVehicle; |
| | | |
| | | public interface AccessService { |
| | | |
| | | /* ---------- é¨ç¦è®¾å¤ ---------- */ |
| | | IPage<AccessGate> gatePage(Page<AccessGate> page, AccessGate query); |
| | | |
| | | boolean gateSave(AccessGate gate); |
| | | |
| | | boolean gateUpdate(AccessGate gate); |
| | | |
| | | boolean gateRemove(java.util.List<Long> ids); |
| | | |
| | | /* ---------- 车è¾éè¡ææ ---------- */ |
| | | IPage<AccessVehicle> vehiclePage(Page<AccessVehicle> page, AccessVehicle query); |
| | | |
| | | boolean vehicleSave(AccessVehicle vehicle); |
| | | |
| | | boolean vehicleUpdate(AccessVehicle vehicle); |
| | | |
| | | boolean vehicleRemove(java.util.List<Long> ids); |
| | | |
| | | /* ---------- è¯å«ä¸æ¾è¡ ---------- */ |
| | | AccessRecord verify(AccessVerifyRequest request); |
| | | |
| | | IPage<AccessRecord> recordPage(Page<AccessRecord> page, AccessRecord query); |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.unattended.access.service.impl; |
| | | |
| | | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
| | | import com.baomidou.mybatisplus.core.metadata.IPage; |
| | | import com.baomidou.mybatisplus.core.toolkit.Wrappers; |
| | | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
| | | import com.ruoyi.unattended.access.dto.AccessVerifyRequest; |
| | | import com.ruoyi.unattended.access.mapper.AccessGateMapper; |
| | | import com.ruoyi.unattended.access.mapper.AccessRecordMapper; |
| | | import com.ruoyi.unattended.access.mapper.AccessVehicleMapper; |
| | | import com.ruoyi.unattended.access.pojo.AccessGate; |
| | | import com.ruoyi.unattended.access.pojo.AccessRecord; |
| | | import com.ruoyi.unattended.access.pojo.AccessVehicle; |
| | | import com.ruoyi.unattended.access.service.AccessService; |
| | | import com.ruoyi.unattended.pojo.UnattendedOperation; |
| | | import com.ruoyi.unattended.service.UnattendedOperationService; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | import org.springframework.util.CollectionUtils; |
| | | import org.springframework.util.StringUtils; |
| | | |
| | | import javax.annotation.Resource; |
| | | import java.time.LocalDateTime; |
| | | import java.util.ArrayList; |
| | | import java.util.List; |
| | | |
| | | @Slf4j |
| | | @Service |
| | | public class AccessServiceImpl implements AccessService { |
| | | |
| | | @Resource |
| | | private AccessGateMapper accessGateMapper; |
| | | |
| | | @Resource |
| | | private AccessRecordMapper accessRecordMapper; |
| | | |
| | | @Resource |
| | | private AccessVehicleMapper accessVehicleMapper; |
| | | |
| | | @Resource |
| | | private UnattendedOperationService unattendedOperationService; |
| | | |
| | | @Override |
| | | public IPage<AccessGate> gatePage(Page<AccessGate> page, AccessGate query) { |
| | | LambdaQueryWrapper<AccessGate> wrapper = Wrappers.<AccessGate>lambdaQuery() |
| | | .like(StringUtils.hasText(query.getGateName()), AccessGate::getGateName, query.getGateName()) |
| | | .like(StringUtils.hasText(query.getArea()), AccessGate::getArea, query.getArea()) |
| | | .orderByDesc(AccessGate::getCreateTime); |
| | | return accessGateMapper.selectPage(page, wrapper); |
| | | } |
| | | |
| | | @Override |
| | | public boolean gateSave(AccessGate gate) { |
| | | if (!StringUtils.hasText(gate.getDeviceStatus())) { |
| | | gate.setDeviceStatus("online"); |
| | | } |
| | | if (!StringUtils.hasText(gate.getGateType())) { |
| | | gate.setGateType("all"); |
| | | } |
| | | return accessGateMapper.insert(gate) > 0; |
| | | } |
| | | |
| | | @Override |
| | | public boolean gateUpdate(AccessGate gate) { |
| | | return accessGateMapper.updateById(gate) > 0; |
| | | } |
| | | |
| | | @Override |
| | | public boolean gateRemove(List<Long> ids) { |
| | | if (CollectionUtils.isEmpty(ids)) { |
| | | return false; |
| | | } |
| | | return accessGateMapper.deleteBatchIds(ids) > 0; |
| | | } |
| | | |
| | | /* ---------- 车è¾éè¡ææ ---------- */ |
| | | |
| | | @Override |
| | | public IPage<AccessVehicle> vehiclePage(Page<AccessVehicle> page, AccessVehicle query) { |
| | | LambdaQueryWrapper<AccessVehicle> wrapper = Wrappers.<AccessVehicle>lambdaQuery() |
| | | .like(StringUtils.hasText(query.getPlateNumber()), AccessVehicle::getPlateNumber, query.getPlateNumber()) |
| | | .like(StringUtils.hasText(query.getRfidTag()), AccessVehicle::getRfidTag, query.getRfidTag()) |
| | | .like(StringUtils.hasText(query.getCarrierName()), AccessVehicle::getCarrierName, query.getCarrierName()) |
| | | .eq(StringUtils.hasText(query.getStatus()), AccessVehicle::getStatus, query.getStatus()) |
| | | .orderByDesc(AccessVehicle::getId); |
| | | return accessVehicleMapper.selectPage(page, wrapper); |
| | | } |
| | | |
| | | @Override |
| | | public boolean vehicleSave(AccessVehicle vehicle) { |
| | | if (!StringUtils.hasText(vehicle.getStatus())) { |
| | | vehicle.setStatus("valid"); |
| | | } |
| | | return accessVehicleMapper.insert(vehicle) > 0; |
| | | } |
| | | |
| | | @Override |
| | | public boolean vehicleUpdate(AccessVehicle vehicle) { |
| | | return accessVehicleMapper.updateById(vehicle) > 0; |
| | | } |
| | | |
| | | @Override |
| | | public boolean vehicleRemove(List<Long> ids) { |
| | | if (CollectionUtils.isEmpty(ids)) { |
| | | return false; |
| | | } |
| | | return accessVehicleMapper.deleteBatchIds(ids) > 0; |
| | | } |
| | | |
| | | private AccessVehicle matchVehicle(String plateNumber, String rfidTag) { |
| | | AccessVehicle byPlate = null; |
| | | if (StringUtils.hasText(plateNumber)) { |
| | | byPlate = accessVehicleMapper.selectOne(Wrappers.<AccessVehicle>lambdaQuery() |
| | | .eq(AccessVehicle::getPlateNumber, plateNumber).last("limit 1")); |
| | | } |
| | | if (byPlate != null) { |
| | | return byPlate; |
| | | } |
| | | if (StringUtils.hasText(rfidTag)) { |
| | | return accessVehicleMapper.selectOne(Wrappers.<AccessVehicle>lambdaQuery() |
| | | .eq(AccessVehicle::getRfidTag, rfidTag).last("limit 1")); |
| | | } |
| | | return null; |
| | | } |
| | | |
| | | /** |
| | | * è¯å«æ ¸éª + æ¾è¡å¤å® + è®°å½ã |
| | | * |
| | | * 车è¾ï¼è½¦çè¯å« + RFID åè¦ç´ ï¼è¯å«å°å³æ¾è¡ï¼å¨è½¦è¾æ¡£æ¡ä¸ç车è¾è¡¥å
æ¿è¿åä¿¡æ¯ï¼ |
| | | * 人åï¼äººè¸è¯å« + å·¥å¡å·å¡ åè¦ç´ ï¼ |
| | | * å°ç£
èå¨ï¼æå® weighbridgeRecordId æ¶ï¼æ ¡éªè¯¥å°ç£
è®°å½åå¨ä¸çº¢å¤æ£æµæ å¼å¸¸ã |
| | | */ |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public AccessRecord verify(AccessVerifyRequest request) { |
| | | AccessRecord record = new AccessRecord(); |
| | | record.setAccessTime(LocalDateTime.now()); |
| | | record.setGateId(request.getGateId()); |
| | | record.setDirection(StringUtils.hasText(request.getDirection()) ? request.getDirection() : "in"); |
| | | record.setCarrierName(request.getCarrierName()); |
| | | record.setWeighbridgeRecordId(request.getWeighbridgeRecordId()); |
| | | record.setWeighbridgeLinked(Boolean.TRUE.equals(request.getWeighbridgeLinked()) ? 1 : 0); |
| | | |
| | | AccessGate gate = request.getGateId() == null ? null : accessGateMapper.selectById(request.getGateId()); |
| | | if (gate != null) { |
| | | record.setGateName(gate.getGateName()); |
| | | } |
| | | |
| | | List<String> methods = new ArrayList<>(); |
| | | List<String> rejects = new ArrayList<>(); |
| | | boolean verified = false; |
| | | |
| | | if ("person".equalsIgnoreCase(request.getAccessType())) { |
| | | record.setAccessType("person"); |
| | | record.setPersonName(request.getPersonName()); |
| | | record.setWorkCardNo(request.getWorkCardNo()); |
| | | record.setIdCardFace(request.getIdCardFace()); |
| | | if ("matched".equalsIgnoreCase(request.getIdCardFace())) { |
| | | methods.add("人è¸è¯å«"); |
| | | } |
| | | if (StringUtils.hasText(request.getWorkCardNo())) { |
| | | methods.add("å·¥å¡"); |
| | | } |
| | | // åè¦ç´ ï¼äººè¸ + å·¥å¡ |
| | | if (!StringUtils.hasText(request.getPersonName()) || !"matched".equalsIgnoreCase(request.getIdCardFace())) { |
| | | rejects.add("人è¸è¯å«æªéè¿"); |
| | | } |
| | | if (!StringUtils.hasText(request.getWorkCardNo())) { |
| | | rejects.add("æªå·å·¥å¡"); |
| | | } |
| | | verified = rejects.isEmpty(); |
| | | } else { |
| | | record.setAccessType("vehicle"); |
| | | record.setPlateNumber(request.getPlateNumber()); |
| | | record.setRfidTag(request.getRfidTag()); |
| | | // 车çè¯å« + RFID åå åï¼è¯å«å°å³è§ä¸ºéè¿ï¼æªç»è®°è½¦è¾ä¸æ¦æª |
| | | if (StringUtils.hasText(request.getPlateNumber())) { |
| | | methods.add("车çè¯å«"); |
| | | } else { |
| | | rejects.add("车çè¯å«å¤±è´¥"); |
| | | } |
| | | if (StringUtils.hasText(request.getRfidTag())) { |
| | | methods.add("RFID"); |
| | | } else { |
| | | rejects.add("RFIDæ ç¾æªè¯å«"); |
| | | } |
| | | if (rejects.isEmpty()) { |
| | | AccessVehicle vehicle = matchVehicle(request.getPlateNumber(), request.getRfidTag()); |
| | | if (vehicle != null) { |
| | | if (!StringUtils.hasText(record.getCarrierName()) && StringUtils.hasText(vehicle.getCarrierName())) { |
| | | record.setCarrierName(vehicle.getCarrierName()); |
| | | } |
| | | if ("disabled".equalsIgnoreCase(vehicle.getStatus())) { |
| | | rejects.add("该车è¾éè¡ææå·²åç¨"); |
| | | } |
| | | } |
| | | } |
| | | verified = rejects.isEmpty(); |
| | | } |
| | | |
| | | // å°ç£
è卿 ¡éª |
| | | if (Boolean.TRUE.equals(request.getWeighbridgeLinked())) { |
| | | if (request.getWeighbridgeRecordId() == null) { |
| | | rejects.add("缺å°å
³èå°ç£
è®°å½"); |
| | | verified = false; |
| | | } else { |
| | | UnattendedOperation op = unattendedOperationService.getById(request.getWeighbridgeRecordId()); |
| | | if (op == null) { |
| | | rejects.add("å
³èå°ç£
è®°å½ä¸åå¨"); |
| | | verified = false; |
| | | } else { |
| | | if (StringUtils.hasText(op.getCarrierName())) { |
| | | record.setCarrierName(op.getCarrierName()); |
| | | } |
| | | if (op.getCheatFlag() != null && op.getCheatFlag() == 1) { |
| | | rejects.add("å°ç£
æ£æµå¼å¸¸:" + (op.getCheatReason() == null ? "车è¾ä½ç½®åç§»" : op.getCheatReason())); |
| | | verified = false; |
| | | } |
| | | if ("alarm".equalsIgnoreCase(op.getInfraredStatus())) { |
| | | rejects.add("çº¢å¤æ£æµæ¥è¦"); |
| | | verified = false; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | record.setVerifyMethod(methods.isEmpty() ? null : String.join("+", methods)); |
| | | |
| | | if (verified && rejects.isEmpty()) { |
| | | record.setVerifyResult("pass"); |
| | | record.setReleaseStatus("released"); |
| | | record.setRejectReason(null); |
| | | } else { |
| | | record.setVerifyResult("reject"); |
| | | record.setReleaseStatus("blocked"); |
| | | record.setRejectReason(String.join("ï¼", rejects)); |
| | | } |
| | | accessRecordMapper.insert(record); |
| | | |
| | | if ("vehicle".equals(record.getAccessType()) && "released".equals(record.getReleaseStatus())) { |
| | | log.info("[é¨ç¦æ¾è¡] 车ç={} é¨ç¦={} æ¹å¼={}", record.getPlateNumber(), record.getGateName(), record.getVerifyMethod()); |
| | | } |
| | | return record; |
| | | } |
| | | |
| | | @Override |
| | | public IPage<AccessRecord> recordPage(Page<AccessRecord> page, AccessRecord query) { |
| | | LambdaQueryWrapper<AccessRecord> wrapper = Wrappers.<AccessRecord>lambdaQuery() |
| | | .like(StringUtils.hasText(query.getPlateNumber()), AccessRecord::getPlateNumber, query.getPlateNumber()) |
| | | .like(StringUtils.hasText(query.getPersonName()), AccessRecord::getPersonName, query.getPersonName()) |
| | | .eq(StringUtils.hasText(query.getAccessType()), AccessRecord::getAccessType, query.getAccessType()) |
| | | .eq(StringUtils.hasText(query.getGateId() == null ? null : String.valueOf(query.getGateId())), |
| | | AccessRecord::getGateId, query.getGateId()) |
| | | .orderByDesc(AccessRecord::getAccessTime); |
| | | return accessRecordMapper.selectPage(page, wrapper); |
| | | } |
| | | } |
| | |
| | | @PostMapping("/weighbridgeSystemAdd") |
| | | @ApiOperation("æ°å¢") |
| | | public AjaxResult add(@RequestBody UnattendedOperation unattendedOperation) { |
| | | return AjaxResult.success(unattendedOperationService.save(unattendedOperation)); |
| | | UnattendedOperation result = unattendedOperationService.autoWeigh(unattendedOperation); |
| | | unattendedOperationService.save(result); |
| | | return AjaxResult.success(result); |
| | | } |
| | | |
| | | /** |
| | |
| | | } |
| | | |
| | | /** |
| | | * æ 人å¼å®èªå¨ç§°éï¼è®¡ç®åéã红å¤é²ä½å¼ã订åå¹é
ãçæç£
åå·/äºç»´ç å¹¶ä¿å |
| | | */ |
| | | @PostMapping("/weighbridgeAutoWeigh") |
| | | @ApiOperation("èªå¨ç§°é") |
| | | public AjaxResult autoWeigh(@RequestBody UnattendedOperation operation) { |
| | | try { |
| | | UnattendedOperation result = unattendedOperationService.autoWeigh(operation); |
| | | unattendedOperationService.save(result); |
| | | return AjaxResult.success(result); |
| | | } catch (IllegalArgumentException e) { |
| | | return AjaxResult.error(e.getMessage()); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * æ¨é ERP |
| | | */ |
| | | @PostMapping("/weighbridgePushErp/{id}") |
| | | @ApiOperation("æ¨éERP") |
| | | public AjaxResult pushErp(@PathVariable Long id) { |
| | | try { |
| | | return AjaxResult.success(unattendedOperationService.pushErp(id)); |
| | | } catch (IllegalArgumentException e) { |
| | | return AjaxResult.error(e.getMessage()); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * æ¿è¿å绩æåæ |
| | | */ |
| | | @GetMapping("/carrierPerformance") |
| | | @ApiOperation("æ¿è¿å绩æåæ") |
| | | public AjaxResult carrierPerformance(String startDate, String endDate) { |
| | | DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); |
| | | return AjaxResult.success(unattendedOperationService.carrierPerformance( |
| | | LocalDateTime.parse(startDate, formatter), LocalDateTime.parse(endDate, formatter))); |
| | | } |
| | | |
| | | /** |
| | | *ç»è®¡ |
| | | * @return |
| | | */ |
| | |
| | | LocalDateTime endDateTime = LocalDateTime.parse(endDate, dateTimeFormatter); |
| | | return AjaxResult.success(unattendedOperationService.weighbridgeDailyReport(startDateTime,endDateTime)); |
| | | } |
| | | |
| | | |
| | | |
| | | } |
| | |
| | | package com.ruoyi.unattended.mapper; |
| | | |
| | | import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
| | | import com.baomidou.mybatisplus.core.metadata.IPage; |
| | | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
| | | import com.ruoyi.account.pojo.AccountReconciliation; |
| | | import com.ruoyi.unattended.pojo.UnattendedOperation; |
| | | import org.apache.ibatis.annotations.Mapper; |
| | | import org.apache.ibatis.annotations.Param; |
| | | import org.apache.ibatis.annotations.Select; |
| | | |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | |
| | | @Mapper |
| | | public interface UnattendedOperationMapper extends BaseMapper<UnattendedOperation> { |
| | | |
| | | /** ææ¿è¿åå¹é
éå®è®¢å */ |
| | | @Select("<script>" + |
| | | "select sales_contract_no as order_no, 'sales' as order_type " + |
| | | "from sales_ledger where customer_name = #{carrierName} " + |
| | | "order by id desc limit 1" + |
| | | "</script>") |
| | | List<Map<String, Object>> matchSalesOrder(@Param("carrierName") String carrierName); |
| | | |
| | | /** ææ¿è¿åå¹é
éè´è®¢å */ |
| | | @Select("<script>" + |
| | | "select purchase_contract_number as order_no, 'purchase' as order_type " + |
| | | "from purchase_ledger where supplier_name = #{carrierName} " + |
| | | "order by id desc limit 1" + |
| | | "</script>") |
| | | List<Map<String, Object>> matchPurchaseOrder(@Param("carrierName") String carrierName); |
| | | } |
| | |
| | | @TableField(fill = FieldFill.INSERT) |
| | | private Long tenantId; |
| | | |
| | | /* *************************** æ 人å¼å®å¢å¼º *************************** */ |
| | | |
| | | @ApiModelProperty(value = "åé(kg)") |
| | | @Excel(name = "åé") |
| | | private BigDecimal netWeight; |
| | | |
| | | @ApiModelProperty(value = "æ¿è¿å") |
| | | @Excel(name = "æ¿è¿å") |
| | | private String carrierName; |
| | | |
| | | @ApiModelProperty(value = "å¹é
订åå·") |
| | | @Excel(name = "订åå·") |
| | | private String orderNo; |
| | | |
| | | @ApiModelProperty(value = "订åç±»å purchase/sales") |
| | | private String orderType; |
| | | |
| | | @ApiModelProperty(value = "æ¯å¦å¹é
订å") |
| | | private Integer orderMatched; |
| | | |
| | | @ApiModelProperty(value = "æ¯å¦ä½å¼/å¼å¸¸") |
| | | private Integer cheatFlag; |
| | | |
| | | @ApiModelProperty(value = "å¼å¸¸åå ") |
| | | private String cheatReason; |
| | | |
| | | @ApiModelProperty(value = "çº¢å¤æ£æµç¶æ normal/offset/alarm") |
| | | private String infraredStatus; |
| | | |
| | | @ApiModelProperty(value = "ERPæ¨éç¶æ pending/success/failed") |
| | | private String erpPushStatus; |
| | | |
| | | @ApiModelProperty(value = "ERPæ¨éæ¶é´") |
| | | @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") |
| | | private LocalDateTime erpPushTime; |
| | | |
| | | @ApiModelProperty(value = "ç£
åäºç»´ç å
容") |
| | | private String qrCode; |
| | | |
| | | @ApiModelProperty(value = "ç£
åå·") |
| | | @Excel(name = "ç£
åå·") |
| | | private String weighNo; |
| | | |
| | | @ApiModelProperty(value = "ç§°éç¶æ weighing/done/abnormal") |
| | | private String weighStatus; |
| | | } |
| | |
| | | |
| | | private Long tripCount; |
| | | |
| | | private Long cargoVolume; |
| | | private BigDecimal cargoVolume; |
| | | |
| | | private BigDecimal deviationRate; |
| | | |
| | | /** 红å¤åç§»æç§°éå¼å¸¸è½¦æ¬¡ */ |
| | | private Long abnormalCount; |
| | | |
| | | /** å¤å®ä¸ºä½å¼ç车次 */ |
| | | private Long cheatCount; |
| | | |
| | | @JsonFormat(pattern = "yyyy-MM-dd") |
| | | private LocalDate date; |
| | | } |
| | |
| | | package com.ruoyi.unattended.service; |
| | | |
| | | import com.baomidou.mybatisplus.core.metadata.IPage; |
| | | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
| | | import com.baomidou.mybatisplus.extension.service.IService; |
| | | import com.ruoyi.account.pojo.AccountReconciliation; |
| | | import com.ruoyi.unattended.pojo.UnattendedOperation; |
| | | import com.ruoyi.unattended.pojo.vo.UnattendedOperationVo; |
| | | |
| | | import java.time.LocalDateTime; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | |
| | | public interface UnattendedOperationService extends IService<UnattendedOperation> { |
| | | |
| | | |
| | | /** |
| | | * æ¯æ¥è¿ç£
ç»è®¡ï¼æå¤©èåï¼ |
| | | */ |
| | | List<UnattendedOperationVo> weighbridgeDailyReport(LocalDateTime startDate, LocalDateTime endDate); |
| | | |
| | | /** |
| | | * æ¿è¿å绩æåæï¼åæ¶çãä½å¼æ¬¡æ°ï¼ |
| | | */ |
| | | List<Map<String, Object>> carrierPerformance(LocalDateTime startDate, LocalDateTime endDate); |
| | | |
| | | /** |
| | | * æ 人å¼å®ç§°éï¼èªå¨è®¡ç®åéã红å¤é²ä½å¼å¤å®ã订åå¹é
ãç£
åå·/äºç»´ç |
| | | */ |
| | | UnattendedOperation autoWeigh(UnattendedOperation operation); |
| | | |
| | | /** |
| | | * æ¨é ERP |
| | | */ |
| | | Map<String, Object> pushErp(Long id); |
| | | } |
| | |
| | | package com.ruoyi.unattended.service.impl; |
| | | |
| | | |
| | | import com.baomidou.mybatisplus.core.metadata.IPage; |
| | | import com.baomidou.mybatisplus.core.toolkit.Wrappers; |
| | | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
| | | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
| | | import com.ruoyi.account.mapper.AccountReconciliationMapper; |
| | | import com.ruoyi.account.pojo.AccountReconciliation; |
| | | import com.ruoyi.account.service.AccountReconciliationService; |
| | | import com.ruoyi.unattended.mapper.UnattendedOperationMapper; |
| | | import com.ruoyi.unattended.pojo.UnattendedOperation; |
| | | import com.ruoyi.unattended.pojo.vo.UnattendedOperationVo; |
| | | import com.ruoyi.unattended.service.UnattendedOperationService; |
| | | import lombok.AllArgsConstructor; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.util.StringUtils; |
| | | |
| | | import java.math.BigDecimal; |
| | | import java.math.RoundingMode; |
| | | import java.time.LocalDate; |
| | | import java.time.LocalDateTime; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | import java.time.format.DateTimeFormatter; |
| | | import java.util.*; |
| | | import java.util.concurrent.ThreadLocalRandom; |
| | | import java.util.stream.Collectors; |
| | | |
| | | @AllArgsConstructor |
| | | @Slf4j |
| | | @Service |
| | | public class UnattendedOperationServiceImpl extends ServiceImpl<UnattendedOperationMapper, UnattendedOperation> implements UnattendedOperationService { |
| | | @AllArgsConstructor |
| | | public class UnattendedOperationServiceImpl |
| | | extends ServiceImpl<UnattendedOperationMapper, UnattendedOperation> |
| | | implements UnattendedOperationService { |
| | | |
| | | /** 红å¤åç§»éå¼(%)ï¼è¶
è¿å¤å®ä¸ºè½¦è¾æªå®å
¨ä¸ç£
*/ |
| | | private static final BigDecimal OFFSET_LIMIT = new BigDecimal("5"); |
| | | |
| | | private UnattendedOperationMapper unattendedOperationMapper; |
| | | private final UnattendedOperationMapper unattendedOperationMapper; |
| | | |
| | | /** |
| | | * æ¯æ¥è¿ç£
ç»è®¡ï¼æå¤©èåï¼è´§éååéä¹åï¼åå·®çååç§»è¶
éè½¦æ¬¡å æ¯ã |
| | | */ |
| | | @Override |
| | | public List<UnattendedOperationVo> weighbridgeDailyReport(LocalDateTime startDate, LocalDateTime endDate) { |
| | | List<UnattendedOperation> unattendedOperations = unattendedOperationMapper.selectList(Wrappers.<UnattendedOperation>lambdaQuery().between(UnattendedOperation::getWeightTime, startDate, endDate)); |
| | | Map<LocalDateTime, List<UnattendedOperation>> listMap = unattendedOperations.stream().collect(Collectors.groupingBy(UnattendedOperation::getWeightTime)); |
| | | List<UnattendedOperationVo> list = listMap.entrySet().stream().map(entry -> { |
| | | UnattendedOperationVo unattendedOperationVo = new UnattendedOperationVo(); |
| | | unattendedOperationVo.setDate(entry.getKey().toLocalDate()); |
| | | unattendedOperationVo.setTripCount((long) entry.getValue().size()); |
| | | unattendedOperationVo.setCargoVolume(entry.getValue().stream().map(UnattendedOperation::getGrossWeight).mapToLong(BigDecimal::longValue).sum()); |
| | | long sum = entry.getValue().stream().map(UnattendedOperation::getPositionOffset).mapToLong(BigDecimal::longValue).sum(); |
| | | unattendedOperationVo.setDeviationRate(BigDecimal.valueOf(sum).divide(BigDecimal.valueOf(entry.getValue().size()), 4, BigDecimal.ROUND_HALF_UP)); |
| | | return unattendedOperationVo; |
| | | }).collect(Collectors.toList()); |
| | | return list; |
| | | List<UnattendedOperation> ops = listBetween(startDate, endDate); |
| | | Map<LocalDate, List<UnattendedOperation>> byDay = ops.stream() |
| | | .filter(o -> o.getWeightTime() != null) |
| | | .collect(Collectors.groupingBy(o -> o.getWeightTime().toLocalDate(), TreeMap::new, Collectors.toList())); |
| | | |
| | | List<UnattendedOperationVo> result = new ArrayList<>(); |
| | | for (Map.Entry<LocalDate, List<UnattendedOperation>> entry : byDay.entrySet()) { |
| | | List<UnattendedOperation> day = entry.getValue(); |
| | | UnattendedOperationVo vo = new UnattendedOperationVo(); |
| | | vo.setDate(entry.getKey()); |
| | | vo.setTripCount((long) day.size()); |
| | | |
| | | BigDecimal cargoVolume = day.stream() |
| | | .map(this::netWeightOf) |
| | | .reduce(BigDecimal.ZERO, BigDecimal::add); |
| | | vo.setCargoVolume(scale(cargoVolume)); |
| | | |
| | | // åå·®ç = åç§»è¶
é车次 / æ»è½¦æ¬¡ * 100 |
| | | long abnormal = day.stream().filter(this::isAbnormal).count(); |
| | | BigDecimal rate = day.isEmpty() ? BigDecimal.ZERO |
| | | : BigDecimal.valueOf(abnormal) |
| | | .multiply(BigDecimal.valueOf(100)) |
| | | .divide(BigDecimal.valueOf(day.size()), 2, RoundingMode.HALF_UP); |
| | | vo.setDeviationRate(rate); |
| | | |
| | | // éå ç»è®¡ï¼VO æ°å¢åæ®µï¼ |
| | | vo.setAbnormalCount(abnormal); |
| | | vo.setCheatCount(day.stream().filter(o -> o.getCheatFlag() != null && o.getCheatFlag() == 1).count()); |
| | | result.add(vo); |
| | | } |
| | | return result; |
| | | } |
| | | |
| | | @Override |
| | | public List<Map<String, Object>> carrierPerformance(LocalDateTime startDate, LocalDateTime endDate) { |
| | | List<UnattendedOperation> ops = listBetween(startDate, endDate); |
| | | Map<String, List<UnattendedOperation>> byCarrier = ops.stream() |
| | | .collect(Collectors.groupingBy( |
| | | o -> StringUtils.hasText(o.getCarrierName()) ? o.getCarrierName() : "æªæå®æ¿è¿å")); |
| | | |
| | | List<Map<String, Object>> result = new ArrayList<>(); |
| | | for (Map.Entry<String, List<UnattendedOperation>> entry : byCarrier.entrySet()) { |
| | | List<UnattendedOperation> list = entry.getValue(); |
| | | long total = list.size(); |
| | | long abnormal = list.stream().filter(this::isAbnormal).count(); |
| | | long cheat = list.stream().filter(o -> o.getCheatFlag() != null && o.getCheatFlag() == 1).count(); |
| | | // åæ¶çï¼æ å¼å¸¸ä¸çº¢å¤ç¶ææ£å¸¸çè½¦æ¬¡å æ¯ |
| | | long onTime = total - abnormal; |
| | | BigDecimal onTimeRate = total == 0 ? BigDecimal.ZERO |
| | | : BigDecimal.valueOf(onTime).multiply(BigDecimal.valueOf(100)) |
| | | .divide(BigDecimal.valueOf(total), 2, RoundingMode.HALF_UP); |
| | | BigDecimal cargo = scale(list.stream().map(this::netWeightOf).reduce(BigDecimal.ZERO, BigDecimal::add)); |
| | | BigDecimal cheatRate = total == 0 ? BigDecimal.ZERO |
| | | : BigDecimal.valueOf(cheat).multiply(BigDecimal.valueOf(100)) |
| | | .divide(BigDecimal.valueOf(total), 2, RoundingMode.HALF_UP); |
| | | |
| | | Map<String, Object> row = new LinkedHashMap<>(); |
| | | row.put("carrierName", entry.getKey()); |
| | | row.put("tripCount", total); |
| | | row.put("cargoVolume", cargo); |
| | | row.put("onTimeCount", onTime); |
| | | row.put("onTimeRate", onTimeRate); |
| | | row.put("cheatCount", cheat); |
| | | row.put("cheatRate", cheatRate); |
| | | // 绩æè¯åï¼åæ¶çå 70%ï¼ä½å¼çååå 30% |
| | | BigDecimal score = onTimeRate.multiply(new BigDecimal("0.7")) |
| | | .add(new BigDecimal("100").subtract(cheatRate).multiply(new BigDecimal("0.3"))) |
| | | .setScale(2, RoundingMode.HALF_UP); |
| | | row.put("score", score); |
| | | result.add(row); |
| | | } |
| | | result.sort((a, b) -> ((BigDecimal) b.get("score")).compareTo((BigDecimal) a.get("score"))); |
| | | return result; |
| | | } |
| | | |
| | | /** |
| | | * èªå¨ç§°éæµç¨ï¼ |
| | | * 1. æ ¡éªç®é/æ¯éï¼è®¡ç®åé |
| | | * 2. 红å¤åç§»é²ä½å¼å¤å®ï¼åç§»çè¶
éå¼åæ¥è¦å¹¶æ è®°ä½å¼ï¼ |
| | | * 3. å¹é
éè´/éå®è®¢åï¼æè½¦ç+æ¿è¿å卿æå°è´¦ä¸å¹é
ï¼ |
| | | * 4. çæç£
åå·ä¸äºç»´ç å
容 |
| | | */ |
| | | @Override |
| | | public UnattendedOperation autoWeigh(UnattendedOperation op) { |
| | | if (op.getTareWeight() == null || op.getGrossWeight() == null) { |
| | | throw new IllegalArgumentException("ç®é忝éä¸è½ä¸ºç©º"); |
| | | } |
| | | if (op.getGrossWeight().compareTo(op.getTareWeight()) <= 0) { |
| | | throw new IllegalArgumentException("æ¯éå¿
须大äºç®é"); |
| | | } |
| | | if (op.getWeightTime() == null) { |
| | | op.setWeightTime(LocalDateTime.now()); |
| | | } |
| | | |
| | | // 1. åé |
| | | op.setNetWeight(scale(op.getGrossWeight().subtract(op.getTareWeight()))); |
| | | |
| | | // 2. 红å¤é²ä½å¼ï¼positionOffset 表示车è¾ä½ç½®åç§»ç¾åæ¯ |
| | | BigDecimal offset = op.getPositionOffset() == null ? BigDecimal.ZERO : op.getPositionOffset(); |
| | | if (offset.compareTo(OFFSET_LIMIT) > 0) { |
| | | op.setInfraredStatus("alarm"); |
| | | op.setCheatFlag(1); |
| | | op.setCheatReason("车è¾ä½ç½®åç§» " + offset + "%ï¼è¶
è¿éå¼ç " + OFFSET_LIMIT + "%ï¼çä¼¼æªå®å
¨ä¸ç£
"); |
| | | op.setWeighStatus("abnormal"); |
| | | } else { |
| | | op.setInfraredStatus("normal"); |
| | | op.setCheatFlag(0); |
| | | op.setCheatReason(null); |
| | | op.setWeighStatus("done"); |
| | | } |
| | | |
| | | // 3. 订åå¹é
ï¼æè´§ç©å/æ¿è¿åå¹é
éè´æéå®å°è´¦ |
| | | matchOrder(op); |
| | | |
| | | // 4. ç£
åå· + äºç»´ç |
| | | if (!StringUtils.hasText(op.getWeighNo())) { |
| | | op.setWeighNo(generateWeighNo(op.getWeightTime())); |
| | | } |
| | | op.setQrCode(buildQrContent(op)); |
| | | |
| | | // 5. ERP é»è®¤å¾
æ¨é |
| | | if (!StringUtils.hasText(op.getErpPushStatus())) { |
| | | op.setErpPushStatus("pending"); |
| | | } |
| | | return op; |
| | | } |
| | | |
| | | @Override |
| | | public Map<String, Object> pushErp(Long id) { |
| | | UnattendedOperation op = getById(id); |
| | | if (op == null) { |
| | | throw new IllegalArgumentException("å°ç£
è®°å½ä¸åå¨"); |
| | | } |
| | | Map<String, Object> result = new LinkedHashMap<>(); |
| | | // å®é
é¡¹ç®æ¤å¤è°ç¨ ERP æ¥å£ï¼å½å以æ¬å°ç¶ææµè½¬ä¿è¯ä¸å¡éç¯ |
| | | boolean success = true; |
| | | UnattendedOperation update = new UnattendedOperation(); |
| | | update.setId(id); |
| | | update.setErpPushStatus(success ? "success" : "failed"); |
| | | update.setErpPushTime(LocalDateTime.now()); |
| | | updateById(update); |
| | | |
| | | result.put("id", id); |
| | | result.put("weighNo", op.getWeighNo()); |
| | | result.put("erpPushStatus", update.getErpPushStatus()); |
| | | result.put("erpPushTime", update.getErpPushTime()); |
| | | result.put("message", success ? "å·²æ¨éè³ ERP" : "ERP æ¨é失败"); |
| | | log.info("[ERPæ¨é] weighNo={}, status={}", op.getWeighNo(), update.getErpPushStatus()); |
| | | return result; |
| | | } |
| | | |
| | | /* ------------------------------ ç§ææ¹æ³ ------------------------------ */ |
| | | |
| | | private List<UnattendedOperation> listBetween(LocalDateTime start, LocalDateTime end) { |
| | | return unattendedOperationMapper.selectList( |
| | | Wrappers.<UnattendedOperation>lambdaQuery() |
| | | .between(UnattendedOperation::getWeightTime, start, end) |
| | | .orderByAsc(UnattendedOperation::getWeightTime)); |
| | | } |
| | | |
| | | private BigDecimal netWeightOf(UnattendedOperation op) { |
| | | if (op.getNetWeight() != null) { |
| | | return op.getNetWeight(); |
| | | } |
| | | if (op.getGrossWeight() != null && op.getTareWeight() != null) { |
| | | return op.getGrossWeight().subtract(op.getTareWeight()); |
| | | } |
| | | return BigDecimal.ZERO; |
| | | } |
| | | |
| | | private boolean isAbnormal(UnattendedOperation op) { |
| | | if (op.getCheatFlag() != null && op.getCheatFlag() == 1) { |
| | | return true; |
| | | } |
| | | if ("abnormal".equalsIgnoreCase(op.getInfraredStatus())) { |
| | | return true; |
| | | } |
| | | return op.getPositionOffset() != null && op.getPositionOffset().compareTo(OFFSET_LIMIT) > 0; |
| | | } |
| | | |
| | | private void matchOrder(UnattendedOperation op) { |
| | | // éè¿è®¾å¤/è´§ç©ä¿¡æ¯å¹é
å°è´¦ï¼å½ä¸å³åå
¥è®¢åä¿¡æ¯ã |
| | | List<Map<String, Object>> rows = Collections.emptyList(); |
| | | if (StringUtils.hasText(op.getCarrierName())) { |
| | | rows = unattendedOperationMapper.matchSalesOrder(op.getCarrierName()); |
| | | if (rows == null || rows.isEmpty()) { |
| | | rows = unattendedOperationMapper.matchPurchaseOrder(op.getCarrierName()); |
| | | } |
| | | } |
| | | if (rows != null && !rows.isEmpty()) { |
| | | Map<String, Object> row = rows.get(0); |
| | | op.setOrderNo(Objects.toString(row.get("order_no"), null)); |
| | | op.setOrderType(Objects.toString(row.get("order_type"), null)); |
| | | op.setOrderMatched(1); |
| | | } else { |
| | | op.setOrderMatched(0); |
| | | } |
| | | } |
| | | |
| | | private String generateWeighNo(LocalDateTime time) { |
| | | return "WB" + time.format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss")) |
| | | + String.format("%03d", ThreadLocalRandom.current().nextInt(1000)); |
| | | } |
| | | |
| | | private String buildQrContent(UnattendedOperation op) { |
| | | return String.format("%s|%s|%s|%s|%s|%s", |
| | | text(op.getWeighNo()), |
| | | text(op.getPlateNumber()), |
| | | op.getNetWeight() == null ? "" : op.getNetWeight().toPlainString(), |
| | | op.getWeightTime() == null ? "" : op.getWeightTime().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")), |
| | | text(op.getCarrierName()), |
| | | text(op.getWeighbridgeNumber())); |
| | | } |
| | | |
| | | private static String text(String value) { |
| | | return value == null ? "" : value; |
| | | } |
| | | |
| | | private static BigDecimal scale(BigDecimal v) { |
| | | return v == null ? BigDecimal.ZERO : v.setScale(2, RoundingMode.HALF_UP); |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.unattended.telemetry.controller; |
| | | |
| | | import com.ruoyi.framework.web.domain.AjaxResult; |
| | | import com.ruoyi.unattended.telemetry.pojo.DeviceTelemetry; |
| | | import com.ruoyi.unattended.telemetry.service.DeviceTelemetryService; |
| | | import io.swagger.annotations.Api; |
| | | import io.swagger.annotations.ApiOperation; |
| | | import org.springframework.web.bind.annotation.*; |
| | | |
| | | import javax.annotation.Resource; |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * 设å¤è¿ç¨çæ§ |
| | | */ |
| | | @RestController |
| | | @RequestMapping("/environmentAccess/deviceMonitor") |
| | | @Api(tags = "设å¤è¿ç¨çæ§") |
| | | public class DeviceTelemetryController { |
| | | |
| | | @Resource |
| | | private DeviceTelemetryService deviceTelemetryService; |
| | | |
| | | @GetMapping("/list") |
| | | @ApiOperation("宿¶çæ§å表") |
| | | public AjaxResult list() { |
| | | return AjaxResult.success(deviceTelemetryService.monitorList()); |
| | | } |
| | | |
| | | @PostMapping("/collect") |
| | | @ApiOperation("触å䏿¬¡éé") |
| | | public AjaxResult collect() { |
| | | return AjaxResult.success(deviceTelemetryService.collectOnce()); |
| | | } |
| | | |
| | | @GetMapping("/history") |
| | | @ApiOperation("å岿²çº¿") |
| | | public AjaxResult history(@RequestParam(required = false) Long deviceId, |
| | | @RequestParam(required = false) Integer limit) { |
| | | List<DeviceTelemetry> list = deviceTelemetryService.history(deviceId, limit); |
| | | return AjaxResult.success(list); |
| | | } |
| | | |
| | | @GetMapping("/alarms") |
| | | @ApiOperation("宿¶æ¥è¦") |
| | | public AjaxResult alarms() { |
| | | return AjaxResult.success(deviceTelemetryService.alarms()); |
| | | } |
| | | |
| | | @PostMapping("/control") |
| | | @ApiOperation("è¿ç¨å¯å") |
| | | public AjaxResult control(@RequestBody ControlRequest request) { |
| | | if (request == null || request.getDeviceId() == null) { |
| | | return AjaxResult.error("设å¤idä¸è½ä¸ºç©º"); |
| | | } |
| | | try { |
| | | return AjaxResult.success(deviceTelemetryService.control(request.getDeviceId(), request.getAction())); |
| | | } catch (IllegalArgumentException e) { |
| | | return AjaxResult.error(e.getMessage()); |
| | | } |
| | | } |
| | | |
| | | public static class ControlRequest { |
| | | private Long deviceId; |
| | | private String action; |
| | | |
| | | public Long getDeviceId() { |
| | | return deviceId; |
| | | } |
| | | |
| | | public void setDeviceId(Long deviceId) { |
| | | this.deviceId = deviceId; |
| | | } |
| | | |
| | | public String getAction() { |
| | | return action; |
| | | } |
| | | |
| | | public void setAction(String action) { |
| | | this.action = action; |
| | | } |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.unattended.telemetry.dto; |
| | | |
| | | import com.fasterxml.jackson.annotation.JsonFormat; |
| | | import com.ruoyi.unattended.telemetry.pojo.DeviceTelemetry; |
| | | import io.swagger.annotations.ApiModelProperty; |
| | | import lombok.Data; |
| | | |
| | | import java.math.BigDecimal; |
| | | import java.time.LocalDateTime; |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * 设å¤è¿ç¨çæ§è§å¾ |
| | | */ |
| | | @Data |
| | | public class DeviceMonitorVo { |
| | | |
| | | @ApiModelProperty("设å¤å°è´¦id") |
| | | private Long deviceId; |
| | | |
| | | @ApiModelProperty("设å¤åç§°") |
| | | private String deviceName; |
| | | |
| | | @ApiModelProperty("è§æ ¼åå·") |
| | | private String deviceModel; |
| | | |
| | | @ApiModelProperty("è¿ç¨æ§å¶ç¶æ running/stopped") |
| | | private String controlStatus; |
| | | |
| | | @ApiModelProperty("宿¶ç¶æ normal/warning/alarm") |
| | | private String status; |
| | | |
| | | @ApiModelProperty("宿¶æ¸©åº¦") |
| | | private BigDecimal temperature; |
| | | |
| | | @ApiModelProperty("宿¶åå") |
| | | private BigDecimal pressure; |
| | | |
| | | @ApiModelProperty("宿¶æ¯å¨") |
| | | private BigDecimal vibration; |
| | | |
| | | @ApiModelProperty("宿¶è½è") |
| | | private BigDecimal energy; |
| | | |
| | | @ApiModelProperty("ééæ¶é´") |
| | | @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") |
| | | private LocalDateTime collectTime; |
| | | |
| | | @ApiModelProperty("æ¥è¦å
容") |
| | | private String alarmContent; |
| | | |
| | | @ApiModelProperty("éå¼é
ç½®") |
| | | private Threshold threshold; |
| | | |
| | | @ApiModelProperty("æè¿æ²çº¿æ°æ®") |
| | | private List<DeviceTelemetry> series; |
| | | |
| | | @Data |
| | | public static class Threshold { |
| | | private BigDecimal tempMin; |
| | | private BigDecimal tempMax; |
| | | private BigDecimal pressureMin; |
| | | private BigDecimal pressureMax; |
| | | private BigDecimal vibrationMax; |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.unattended.telemetry.mapper; |
| | | |
| | | import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
| | | import com.ruoyi.unattended.telemetry.pojo.DeviceTelemetry; |
| | | import org.apache.ibatis.annotations.Mapper; |
| | | |
| | | @Mapper |
| | | public interface DeviceTelemetryMapper extends BaseMapper<DeviceTelemetry> { |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.unattended.telemetry.pojo; |
| | | |
| | | import com.baomidou.mybatisplus.annotation.IdType; |
| | | import com.baomidou.mybatisplus.annotation.TableField; |
| | | import com.baomidou.mybatisplus.annotation.TableId; |
| | | import com.baomidou.mybatisplus.annotation.TableName; |
| | | import com.fasterxml.jackson.annotation.JsonFormat; |
| | | import io.swagger.annotations.ApiModel; |
| | | import io.swagger.annotations.ApiModelProperty; |
| | | import lombok.Data; |
| | | |
| | | import java.io.Serializable; |
| | | import java.math.BigDecimal; |
| | | import java.time.LocalDateTime; |
| | | |
| | | /** |
| | | * 设å¤é¥æµå®æ¶æ°æ® |
| | | */ |
| | | @Data |
| | | @ApiModel |
| | | @TableName("device_telemetry") |
| | | public class DeviceTelemetry implements Serializable { |
| | | |
| | | private static final long serialVersionUID = 1L; |
| | | |
| | | @TableId(type = IdType.AUTO) |
| | | private Long id; |
| | | |
| | | @ApiModelProperty("设å¤å°è´¦id") |
| | | private Long deviceLedgerId; |
| | | |
| | | @ApiModelProperty("设å¤åç§°") |
| | | private String deviceName; |
| | | |
| | | @ApiModelProperty("温度(â)") |
| | | private BigDecimal temperature; |
| | | |
| | | @ApiModelProperty("åå(MPa)") |
| | | private BigDecimal pressure; |
| | | |
| | | @ApiModelProperty("æ¯å¨(mm/s)") |
| | | private BigDecimal vibration; |
| | | |
| | | @ApiModelProperty("è½è(kWh)") |
| | | private BigDecimal energy; |
| | | |
| | | @ApiModelProperty("ç¶æ normal/warning/alarm") |
| | | private String status; |
| | | |
| | | @ApiModelProperty("æ¥è¦å
容") |
| | | private String alarmContent; |
| | | |
| | | @ApiModelProperty("ééæ¶é´") |
| | | @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") |
| | | private LocalDateTime collectTime; |
| | | |
| | | @TableField(fill = com.baomidou.mybatisplus.annotation.FieldFill.INSERT) |
| | | @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") |
| | | private LocalDateTime createTime; |
| | | |
| | | @TableField(fill = com.baomidou.mybatisplus.annotation.FieldFill.INSERT) |
| | | private Long tenantId; |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.unattended.telemetry.service; |
| | | |
| | | import com.baomidou.mybatisplus.extension.service.IService; |
| | | import com.ruoyi.unattended.telemetry.dto.DeviceMonitorVo; |
| | | import com.ruoyi.unattended.telemetry.pojo.DeviceTelemetry; |
| | | |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | |
| | | public interface DeviceTelemetryService extends IService<DeviceTelemetry> { |
| | | |
| | | /** |
| | | * éé䏿¬¡å®æ¶æ°æ®ï¼ä¸ºææå¯ç¨é¥æµç设å¤çæä¸æ¡é¥æµè®°å½ï¼å¹¶åéå¼å¤å® |
| | | */ |
| | | List<DeviceMonitorVo> collectOnce(); |
| | | |
| | | /** |
| | | * å½åçæ§æ»è§ |
| | | */ |
| | | List<DeviceMonitorVo> monitorList(); |
| | | |
| | | /** |
| | | * å岿²çº¿ |
| | | */ |
| | | List<DeviceTelemetry> history(Long deviceId, Integer limit); |
| | | |
| | | /** |
| | | * è¿ç¨å¯å |
| | | */ |
| | | Map<String, Object> control(Long deviceId, String action); |
| | | |
| | | /** |
| | | * 宿¶æ¥è¦å表 |
| | | */ |
| | | List<DeviceMonitorVo> alarms(); |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.unattended.telemetry.service.impl; |
| | | |
| | | import com.baomidou.mybatisplus.core.toolkit.Wrappers; |
| | | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
| | | import com.ruoyi.device.mapper.DeviceLedgerMapper; |
| | | import com.ruoyi.device.pojo.DeviceLedger; |
| | | import com.ruoyi.unattended.telemetry.dto.DeviceMonitorVo; |
| | | import com.ruoyi.unattended.telemetry.mapper.DeviceTelemetryMapper; |
| | | import com.ruoyi.unattended.telemetry.pojo.DeviceTelemetry; |
| | | import com.ruoyi.unattended.telemetry.service.DeviceTelemetryService; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.stereotype.Service; |
| | | |
| | | import javax.annotation.Resource; |
| | | import java.math.BigDecimal; |
| | | import java.math.RoundingMode; |
| | | import java.time.LocalDateTime; |
| | | import java.util.*; |
| | | import java.util.stream.Collectors; |
| | | |
| | | /** |
| | | * 设å¤é¥æµæå¡ã |
| | | * |
| | | * 说æï¼ç°åºä¼ æå¨éè¿ PLC/ç½å
³æ¥å
¥ï¼æ¬æå¡æä¾ç»ä¸çééå
¥å£ä¸éå¼å¤å®ã |
| | | * æ¥è¦ä¸è¿ç¨æ§å¶è½åãcollectOnce ä¼åºäºè®¾å¤æè¿ä¸æ¬¡æ°æ®åå¹³æ»æ¨æ¼ï¼ |
| | | * æ¥å
¥çå®ç½å
³ååªéæ¿æ¢ sample() çæ°æ®æ¥æºå³å¯ï¼å
¶ä½ä¸å¡é»è¾ä¿æä¸åã |
| | | */ |
| | | @Slf4j |
| | | @Service |
| | | public class DeviceTelemetryServiceImpl extends ServiceImpl<DeviceTelemetryMapper, DeviceTelemetry> |
| | | implements DeviceTelemetryService { |
| | | |
| | | @Resource |
| | | private DeviceLedgerMapper deviceLedgerMapper; |
| | | |
| | | @Resource |
| | | private DeviceTelemetryMapper telemetryMapper; |
| | | |
| | | private static final BigDecimal DEFAULT_TEMP_MIN = new BigDecimal("20"); |
| | | private static final BigDecimal DEFAULT_TEMP_MAX = new BigDecimal("80"); |
| | | private static final BigDecimal DEFAULT_PRESSURE_MIN = new BigDecimal("0.20"); |
| | | private static final BigDecimal DEFAULT_PRESSURE_MAX = new BigDecimal("1.50"); |
| | | private static final BigDecimal DEFAULT_VIBRATION_MAX = new BigDecimal("5.00"); |
| | | |
| | | @Override |
| | | public List<DeviceMonitorVo> collectOnce() { |
| | | List<DeviceLedger> devices = deviceLedgerMapper.selectList( |
| | | Wrappers.<DeviceLedger>lambdaQuery() |
| | | .and(w -> w.eq(DeviceLedger::getTelemetryEnabled, 1).or().isNull(DeviceLedger::getTelemetryEnabled))); |
| | | if (devices == null || devices.isEmpty()) { |
| | | // è¥å°è´¦æªé
ç½®é¥æµå¼å
³ï¼ååé为å
¨é¨è®¾å¤ |
| | | devices = deviceLedgerMapper.selectList(null); |
| | | } |
| | | LocalDateTime now = LocalDateTime.now(); |
| | | List<DeviceTelemetry> batch = new ArrayList<>(); |
| | | for (DeviceLedger device : devices) { |
| | | DeviceTelemetry last = telemetryMapper.selectOne( |
| | | Wrappers.<DeviceTelemetry>lambdaQuery() |
| | | .eq(DeviceTelemetry::getDeviceLedgerId, device.getId()) |
| | | .orderByDesc(DeviceTelemetry::getCollectTime) |
| | | .last("limit 1")); |
| | | DeviceTelemetry telemetry = sample(device, last); |
| | | telemetry.setCollectTime(now); |
| | | telemetry.setDeviceLedgerId(device.getId()); |
| | | telemetry.setDeviceName(device.getDeviceName()); |
| | | judge(device, telemetry); |
| | | batch.add(telemetry); |
| | | } |
| | | if (!batch.isEmpty()) { |
| | | saveBatch(batch); |
| | | } |
| | | return monitorList(); |
| | | } |
| | | |
| | | /** |
| | | * éæ ·ï¼æ¥å
¥çå®ç½å
³æ¶æ¿æ¢æ¤å¤æ°æ®æ¥æºï¼Modbus/MQTT/OPC-UAï¼ |
| | | */ |
| | | private DeviceTelemetry sample(DeviceLedger device, DeviceTelemetry last) { |
| | | DeviceTelemetry t = new DeviceTelemetry(); |
| | | Random random = new Random(); |
| | | boolean running = !"stopped".equalsIgnoreCase(device.getControlStatus()); |
| | | if (!running) { |
| | | t.setTemperature(scale(22 + random.nextDouble() * 3)); |
| | | t.setPressure(scale(0.3 + random.nextDouble() * 0.1)); |
| | | t.setVibration(scale(random.nextDouble() * 0.5)); |
| | | t.setEnergy(BigDecimal.ZERO); |
| | | return t; |
| | | } |
| | | double baseTemp = last != null && last.getTemperature() != null |
| | | ? last.getTemperature().doubleValue() : 45 + random.nextDouble() * 10; |
| | | double basePress = last != null && last.getPressure() != null |
| | | ? last.getPressure().doubleValue() : 0.6 + random.nextDouble() * 0.3; |
| | | double baseVib = last != null && last.getVibration() != null |
| | | ? last.getVibration().doubleValue() : 1.2 + random.nextDouble() * 0.8; |
| | | |
| | | double temp = clamp(baseTemp + (random.nextDouble() - 0.5) * 1.6, 15, 95); |
| | | double press = clamp(basePress + (random.nextDouble() - 0.5) * 0.08, 0.1, 1.8); |
| | | double vib = clamp(baseVib + (random.nextDouble() - 0.5) * 0.25, 0.1, 8); |
| | | // å¶åè¶çï¼ç¨äºæ¼ç¤ºæ¥è¦é¾è·¯ |
| | | if (random.nextDouble() < 0.04) temp = temp + 30; |
| | | if (random.nextDouble() < 0.03) press = press + 0.9; |
| | | if (random.nextDouble() < 0.03) vib = vib + 4; |
| | | |
| | | t.setTemperature(scale(temp)); |
| | | t.setPressure(scale(press)); |
| | | t.setVibration(scale(vib)); |
| | | t.setEnergy(scale(8 + random.nextDouble() * 6)); |
| | | return t; |
| | | } |
| | | |
| | | /** |
| | | * éå¼å¤å®ï¼è¶ç -> warningï¼ä¸¥éè¶ç -> alarm |
| | | */ |
| | | private void judge(DeviceLedger device, DeviceTelemetry t) { |
| | | BigDecimal tempMin = nvl(device.getTempMin(), DEFAULT_TEMP_MIN); |
| | | BigDecimal tempMax = nvl(device.getTempMax(), DEFAULT_TEMP_MAX); |
| | | BigDecimal pressMin = nvl(device.getPressureMin(), DEFAULT_PRESSURE_MIN); |
| | | BigDecimal pressMax = nvl(device.getPressureMax(), DEFAULT_PRESSURE_MAX); |
| | | BigDecimal vibMax = nvl(device.getVibrationMax(), DEFAULT_VIBRATION_MAX); |
| | | |
| | | List<String> alarms = new ArrayList<>(); |
| | | String status = "normal"; |
| | | |
| | | if (t.getTemperature() != null) { |
| | | if (t.getTemperature().compareTo(tempMax) > 0 || t.getTemperature().compareTo(tempMin) < 0) { |
| | | alarms.add("温度è¶ç:" + t.getTemperature() + "â (æ£å¸¸ " + tempMin + "~" + tempMax + ")"); |
| | | status = "warning"; |
| | | } |
| | | } |
| | | if (t.getPressure() != null) { |
| | | if (t.getPressure().compareTo(pressMax) > 0 || t.getPressure().compareTo(pressMin) < 0) { |
| | | alarms.add("ååè¶ç:" + t.getPressure() + "MPa (æ£å¸¸ " + pressMin + "~" + pressMax + ")"); |
| | | status = "warning"; |
| | | } |
| | | } |
| | | if (t.getVibration() != null && t.getVibration().compareTo(vibMax) > 0) { |
| | | alarms.add("æ¯å¨è¶ç:" + t.getVibration() + "mm/s (ä¸é " + vibMax + ")"); |
| | | status = "alarm"; |
| | | } |
| | | // å¤é¡¹åæ¶è¶çå级为 alarm |
| | | if (alarms.size() >= 2) { |
| | | status = "alarm"; |
| | | } |
| | | t.setStatus(status); |
| | | t.setAlarmContent(alarms.isEmpty() ? null : String.join("ï¼", alarms)); |
| | | } |
| | | |
| | | @Override |
| | | public List<DeviceMonitorVo> monitorList() { |
| | | List<DeviceLedger> devices = deviceLedgerMapper.selectList(null); |
| | | if (devices == null || devices.isEmpty()) { |
| | | return Collections.emptyList(); |
| | | } |
| | | List<DeviceMonitorVo> result = new ArrayList<>(); |
| | | for (DeviceLedger device : devices) { |
| | | List<DeviceTelemetry> recent = telemetryMapper.selectList( |
| | | Wrappers.<DeviceTelemetry>lambdaQuery() |
| | | .eq(DeviceTelemetry::getDeviceLedgerId, device.getId()) |
| | | .orderByDesc(DeviceTelemetry::getCollectTime) |
| | | .last("limit 30")); |
| | | DeviceMonitorVo vo = new DeviceMonitorVo(); |
| | | vo.setDeviceId(device.getId()); |
| | | vo.setDeviceName(device.getDeviceName()); |
| | | vo.setDeviceModel(device.getDeviceModel()); |
| | | vo.setControlStatus(device.getControlStatus() == null ? "running" : device.getControlStatus()); |
| | | vo.setThreshold(buildThreshold(device)); |
| | | if (!recent.isEmpty()) { |
| | | DeviceTelemetry latest = recent.get(0); |
| | | vo.setTemperature(latest.getTemperature()); |
| | | vo.setPressure(latest.getPressure()); |
| | | vo.setVibration(latest.getVibration()); |
| | | vo.setEnergy(latest.getEnergy()); |
| | | vo.setCollectTime(latest.getCollectTime()); |
| | | vo.setStatus(latest.getStatus()); |
| | | vo.setAlarmContent(latest.getAlarmContent()); |
| | | } else { |
| | | vo.setStatus("offline"); |
| | | } |
| | | Collections.reverse(recent); |
| | | vo.setSeries(recent); |
| | | result.add(vo); |
| | | } |
| | | return result; |
| | | } |
| | | |
| | | private DeviceMonitorVo.Threshold buildThreshold(DeviceLedger device) { |
| | | DeviceMonitorVo.Threshold th = new DeviceMonitorVo.Threshold(); |
| | | th.setTempMin(nvl(device.getTempMin(), DEFAULT_TEMP_MIN)); |
| | | th.setTempMax(nvl(device.getTempMax(), DEFAULT_TEMP_MAX)); |
| | | th.setPressureMin(nvl(device.getPressureMin(), DEFAULT_PRESSURE_MIN)); |
| | | th.setPressureMax(nvl(device.getPressureMax(), DEFAULT_PRESSURE_MAX)); |
| | | th.setVibrationMax(nvl(device.getVibrationMax(), DEFAULT_VIBRATION_MAX)); |
| | | return th; |
| | | } |
| | | |
| | | @Override |
| | | public List<DeviceTelemetry> history(Long deviceId, Integer limit) { |
| | | int size = limit == null || limit <= 0 ? 100 : Math.min(limit, 1000); |
| | | List<DeviceTelemetry> list = telemetryMapper.selectList( |
| | | Wrappers.<DeviceTelemetry>lambdaQuery() |
| | | .eq(deviceId != null, DeviceTelemetry::getDeviceLedgerId, deviceId) |
| | | .orderByDesc(DeviceTelemetry::getCollectTime) |
| | | .last("limit " + size)); |
| | | Collections.reverse(list); |
| | | return list; |
| | | } |
| | | |
| | | @Override |
| | | public Map<String, Object> control(Long deviceId, String action) { |
| | | DeviceLedger device = deviceLedgerMapper.selectById(deviceId); |
| | | if (device == null) { |
| | | throw new IllegalArgumentException("设å¤ä¸åå¨"); |
| | | } |
| | | String status; |
| | | if ("start".equalsIgnoreCase(action)) { |
| | | status = "running"; |
| | | } else if ("stop".equalsIgnoreCase(action)) { |
| | | status = "stopped"; |
| | | } else { |
| | | throw new IllegalArgumentException("䏿¯æçæä½:" + action); |
| | | } |
| | | DeviceLedger update = new DeviceLedger(); |
| | | update.setId(deviceId); |
| | | update.setControlStatus(status); |
| | | deviceLedgerMapper.updateById(update); |
| | | |
| | | Map<String, Object> result = new LinkedHashMap<>(); |
| | | result.put("deviceId", deviceId); |
| | | result.put("deviceName", device.getDeviceName()); |
| | | result.put("controlStatus", status); |
| | | result.put("commandTime", LocalDateTime.now().toString()); |
| | | result.put("message", "running".equals(status) ? "å¯å¨æä»¤å·²ä¸å" : "忢æä»¤å·²ä¸å"); |
| | | log.info("[è¿ç¨æ§å¶] deviceId={}, action={}, status={}", deviceId, action, status); |
| | | return result; |
| | | } |
| | | |
| | | @Override |
| | | public List<DeviceMonitorVo> alarms() { |
| | | List<DeviceMonitorVo> all = monitorList(); |
| | | return all.stream() |
| | | .filter(v -> "warning".equals(v.getStatus()) || "alarm".equals(v.getStatus())) |
| | | .sorted((a, b) -> { |
| | | int ra = "alarm".equals(a.getStatus()) ? 0 : 1; |
| | | int rb = "alarm".equals(b.getStatus()) ? 0 : 1; |
| | | if (ra != rb) return ra - rb; |
| | | if (a.getCollectTime() == null) return 1; |
| | | if (b.getCollectTime() == null) return -1; |
| | | return b.getCollectTime().compareTo(a.getCollectTime()); |
| | | }) |
| | | .collect(Collectors.toList()); |
| | | } |
| | | |
| | | private static BigDecimal scale(double v) { |
| | | return BigDecimal.valueOf(v).setScale(2, RoundingMode.HALF_UP); |
| | | } |
| | | |
| | | private static double clamp(double v, double min, double max) { |
| | | return Math.max(min, Math.min(max, v)); |
| | | } |
| | | |
| | | private static BigDecimal nvl(BigDecimal value, BigDecimal fallback) { |
| | | return value == null ? fallback : value; |
| | | } |
| | | } |