doc/20260528_create_vehicle_borrow_record.sql
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,37 @@ CREATE TABLE `vehicle_borrow_record` ( `id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主é®ID', `borrow_no` VARCHAR(32) NOT NULL COMMENT 'ååºåå·', `vehicle_id` BIGINT NOT NULL COMMENT '车è¾ID', `vehicle_plate_number` VARCHAR(20) NOT NULL COMMENT '车çå·å¿«ç §', `applicant_id` BIGINT NOT NULL COMMENT 'ç³è¯·äººID', `applicant_name` VARCHAR(64) NOT NULL COMMENT 'ç³è¯·äººå§å', `applicant_dept_id` BIGINT DEFAULT NULL COMMENT 'ç³è¯·é¨é¨ID', `applicant_dept_name` VARCHAR(100) DEFAULT NULL COMMENT 'ç³è¯·é¨é¨åç§°', `borrow_reason` VARCHAR(500) DEFAULT NULL COMMENT 'ååºåå ', `borrow_start_time` DATETIME NOT NULL COMMENT 'ååºå¼å§æ¶é´', `planned_return_time` DATETIME NOT NULL COMMENT '计åå½è¿æ¶é´', `actual_return_time` DATETIME DEFAULT NULL COMMENT 'å®é å½è¿æ¶é´', `borrow_status` VARCHAR(20) NOT NULL DEFAULT 'DRAFT' COMMENT 'ååºç¶æ DRAFT IN_APPROVAL BORROWING RETURNED REJECTED', `approval_instance_id` BIGINT DEFAULT NULL COMMENT 'ååºå®¡æ¹å®ä¾ID', `approved_time` DATETIME DEFAULT NULL COMMENT 'ååºå®¡æ¹éè¿æ¶é´', `returned_time` DATETIME DEFAULT NULL COMMENT 'å½è¿æ¶é´', `extend_approval_instance_id` BIGINT DEFAULT NULL COMMENT 'å»¶æå®¡æ¹å®ä¾ID', `extend_status` VARCHAR(20) NOT NULL DEFAULT 'NONE' COMMENT 'å»¶æç¶æ NONE PENDING APPROVED REJECTED', `extend_target_return_time` DATETIME DEFAULT NULL COMMENT 'å»¶æç®æ å½è¿æ¶é´', `extend_reason` VARCHAR(500) DEFAULT NULL COMMENT 'å»¶æåå ', `extend_approved_time` DATETIME DEFAULT NULL COMMENT 'å»¶æå®¡æ¹éè¿æ¶é´', `deleted` TINYINT DEFAULT 0 COMMENT 'é»è¾å é¤ 0æªå é¤ 1å·²å é¤', `create_user` BIGINT DEFAULT NULL COMMENT 'å建人', `create_time` DATETIME DEFAULT NULL COMMENT 'å建æ¶é´', `update_user` BIGINT DEFAULT NULL COMMENT 'æ´æ°äºº', `update_time` DATETIME DEFAULT NULL COMMENT 'æ´æ°æ¶é´', `dept_id` BIGINT DEFAULT NULL COMMENT 'é¨é¨ID', PRIMARY KEY (`id`), UNIQUE KEY `uk_vehicle_borrow_record_borrow_no` (`borrow_no`), KEY `idx_vehicle_borrow_record_vehicle_id` (`vehicle_id`), KEY `idx_vehicle_borrow_record_approval_instance_id` (`approval_instance_id`), KEY `idx_vehicle_borrow_record_extend_approval_instance_id` (`extend_approval_instance_id`), KEY `idx_vehicle_borrow_record_borrow_status` (`borrow_status`), KEY `idx_vehicle_borrow_record_extend_status` (`extend_status`), KEY `idx_vehicle_borrow_record_create_time` (`create_time`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='车è¾ååºè®°å½è¡¨'; doc/20260528_³µÁ¾¹ÜÀí½è³ö¹é»¹ÑÓÆÚǰ¶ËÁªµ÷Îĵµ.md
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,257 @@ # 车è¾ç®¡çååºå½è¿å»¶æå端èè°ææ¡£ ## 1. 说æ - 车è¾ä¸»æ¡£ä»ç¶èµ° `VehicleController` åææ¥å£ã - ååºè®°å½æ°å¢å°åä¸ä¸ª `VehicleController` ä¸ç `/borrow/**` è·¯ç±ã - ååºç³è¯·ãå»¶æç³è¯·é½èµ°åå审æ¹ï¼å®¡æ¹å®ä¾ç»ä¸ä½¿ç¨ `/approvalInstance/approve`ã - å端已æä¸å¡ç±»ååºåï¼ - `19`ï¼è½¦è¾ååºå®¡æ¹ - `20`ï¼è½¦è¾å»¶æå®¡æ¹ ## 2. 车è¾ä¸»æ¡£æ¥å£ ### 2.1 å页æ¥è¯¢ - `GET /approve/vehicle/listPage` æ¥è¯¢åæ°ï¼ | åæ°å | ç±»å | 说æ | | --- | --- | --- | | current | Long | å½å页 | | size | Long | æ¯é¡µæ¡æ° | | plateNumber | String | 车çå·æ¨¡ç³æ¥è¯¢ | | status | String | 使ç¨ç¶æï¼`IDLE` / `IN_USE` / `MAINTENANCE` / `SCRAPPED` | ### 2.2 æ°å¢ - `POST /approve/vehicle/save` ```json { "plateNumber": "粤A12345", "mileage": 12580.5, "status": "IDLE" } ``` ### 2.3 详æ - `GET /approve/vehicle/detail?id=1` ### 2.4 ä¿®æ¹ - `POST /approve/vehicle/update` ### 2.5 å é¤ - `DELETE /approve/vehicle/delete` ```json [1, 2, 3] ``` ## 3. ååºè®°å½æ¥å£ ### 3.1 ç¶æåå ¸ #### ååºç¶æ `borrowStatus` | å¼ | 说æ | | --- | --- | | `DRAFT` | è稿 | | `IN_APPROVAL` | 审æ¹ä¸ | | `BORROWING` | ååºä¸ | | `RETURNED` | å·²å½è¿ | | `REJECTED` | 已驳å | #### å»¶æç¶æ `extendStatus` | å¼ | 说æ | | --- | --- | | `NONE` | æªç³è¯· | | `PENDING` | 审æ¹ä¸ | | `APPROVED` | å·²éè¿ | | `REJECTED` | 已驳å | ### 3.2 å页æ¥è¯¢ - `GET /approve/vehicle/borrow/listPage` æ¥è¯¢åæ°ï¼ | åæ°å | ç±»å | 说æ | | --- | --- | --- | | current | Long | å½å页 | | size | Long | æ¯é¡µæ¡æ° | | borrowNo | String | ååºåå· | | vehiclePlateNumber | String | 车çå· | | applicantName | String | ç³è¯·äºº | | borrowStatus | String | ååºç¶æ | | extendStatus | String | å»¶æç¶æ | ### 3.3 æ°å¢ååºç³è¯· - `POST /approve/vehicle/borrow/save` 请æ±ä½ï¼ | åæ°å | ç±»å | å¿ å¡« | 说æ | | --- | --- | --- | --- | | vehicleId | Long | æ¯ | 车è¾ID | | borrowReason | String | å¦ | ååºåå | | borrowStartTime | String | æ¯ | ååºå¼å§æ¶é´ï¼`yyyy-MM-dd HH:mm:ss` | | plannedReturnTime | String | æ¯ | 计åå½è¿æ¶é´ï¼å¿ é¡»æäºå¼å§æ¶é´ | | borrowStatus | String | å¦ | `DRAFT` æ `IN_APPROVAL`ï¼é»è®¤ `DRAFT` | | approvalTemplateId | Long | å¦ | ååå®¡æ¹æ¨¡æ¿IDï¼ä¸ä¼ åæä¸å¡ç±»åèªå¨åææ°å¯ç¨æ¨¡æ¿ | | borrowStorageBlobDTOs | Array | å¦ | ååºç³è¯·éä»¶å表 | 示ä¾ï¼ ```json { "vehicleId": 1, "borrowReason": "å®¢æ·æè®¿", "borrowStartTime": "2026-05-28 09:00:00", "plannedReturnTime": "2026-05-28 18:00:00", "borrowStatus": "IN_APPROVAL" } ``` è¿åè§åï¼ - `DRAFT`ï¼åªä¿åè稿ï¼ä¸å建审æ¹å®ä¾ã - `IN_APPROVAL`ï¼ä¿ååèªå¨å建审æ¹å®ä¾ï¼è¿ååå¯ç´æ¥è¿å ¥å®¡æ¹å表ã - 审æ¹éè¿åï¼ååºç¶æå为 `BORROWING`ï¼è½¦è¾ç¶æå为 `IN_USE`ã - 审æ¹é©³ååï¼ååºç¶æå为 `REJECTED`ã ### 3.4 详æ - `GET /approve/vehicle/borrow/detail?id=1` ### 3.5 ä¿®æ¹ååºç³è¯· - `POST /approve/vehicle/borrow/update` 说æï¼ - ä» å 许 `DRAFT` å `REJECTED` ç¶æä¿®æ¹ã - å¦æä¿®æ¹åæäº¤ä¸º `IN_APPROVAL`ï¼å端ä¼éæ°åèµ·åå审æ¹ã ### 3.6 å é¤ååºè®°å½ - `DELETE /approve/vehicle/borrow/delete` ```json [1, 2, 3] ``` 说æï¼ - 审æ¹ä¸åååºä¸çè®°å½ä¸å 许å é¤ã ### 3.7 å½è¿è½¦è¾ - `POST /approve/vehicle/borrow/return` 请æ±ä½ï¼ ```json { "id": 1, "actualReturnTime": "2026-05-28 17:30:00" } ``` 说æï¼ - ä» ååºä¸çè®°å½å¯å½è¿ã - 妿å½åæå»¶æå®¡æ¹ä¸çç³è¯·ï¼ç¦æ¢ç´æ¥å½è¿ã - å½è¿åååºç¶æå为 `RETURNED`ï¼è½¦è¾ç¶æåå° `IDLE`ã - å½è¿æ¥å£æ¯æéä»¶åæ®µ `returnStorageBlobDTOs`ã å½è¿è¯·æ±ä½ç¤ºä¾ï¼ ```json { "id": 1, "actualReturnTime": "2026-05-28 17:30:00", "returnStorageBlobDTOs": [ { "id": 1001, "application": "file" } ] } ``` ### 3.8 åèµ·å»¶æç³è¯· - `POST /approve/vehicle/borrow/delay` 请æ±ä½ï¼ | åæ°å | ç±»å | å¿ å¡« | 说æ | | --- | --- | --- | --- | | id | Long | æ¯ | ååºè®°å½ID | | extendTargetReturnTime | String | æ¯ | å»¶æåçå½è¿æ¶é´ï¼å¿ é¡»æäºå½å计åå½è¿æ¶é´ | | extendReason | String | å¦ | å»¶æåå | | approvalTemplateId | Long | å¦ | å»¶æå®¡æ¹æ¨¡æ¿IDï¼ä¸ä¼ åæä¸å¡ç±»åèªå¨åææ°å¯ç¨æ¨¡æ¿ | 示ä¾ï¼ ```json { "id": 1, "extendTargetReturnTime": "2026-05-29 18:00:00", "extendReason": "客æ·ä¸´æ¶å ä¼ï¼éè¦ç»§ç»ç¨è½¦" } ``` 说æï¼ - ä» ååºä¸çè®°å½å¯ä»¥åèµ·å»¶æã - ä¸è½éå¤åèµ·æªå¤çå®çå»¶æç³è¯·ã - å»¶æå®¡æ¹éè¿åï¼ä¼æ´æ°ååºè®°å½ç计åå½è¿æ¶é´ï¼å¹¶ä¿çå»¶æå®¡æ¹è®°å½ã ### 3.9 éä»¶åæ®µçº¦å® - ååºç³è¯·éä»¶åæ®µåï¼`borrowStorageBlobDTOs` - å½è¿éä»¶åæ®µåï¼`returnStorageBlobDTOs` - åæ®µå å®¹ä¿æåç°ææä»¶ä¸ä¼ ç»ä»¶åå¡«æ°æ®ä¸è´ï¼è³å°å 嫿件 `id` ## 4. åå审æ¹èè° ### 4.1 å建审æ¹å®ä¾å - å端ä¼èªå¨åå ¥ `approval_instance`ã`approval_instance_node`ã`approval_task`ã`approval_record`ã - å端å¯ä»¥è·³è½¬å°å®¡æ¹å表æå®¡æ¹è¯¦æ 页ï¼ä½¿ç¨è¿åç `approvalInstanceId` è¿è¡èå¨ã ### 4.2 审æ¹å¤ç - å®¡æ¹æ¥å£ä»ç¶ä½¿ç¨ï¼ `POST /approvalInstance/approve` 请æ±ä½ç¤ºä¾ï¼ ```json { "id": 10001, "approveAction": "APPROVED", "approveComment": "åæååº" } ``` å¯éå¼ï¼ - `APPROVED` - `REJECTED` ### 4.3 页é¢å»ºè®®èå¨ - ååºç³è¯·æäº¤åï¼å³ä¾§å®¡æ¹ç¶ææ¾ç¤º `审æ¹ä¸`ã - 审æ¹éè¿åï¼åè¡¨ç¶æåæ¢ä¸º `ååºä¸`ã - å»¶æç³è¯·æäº¤åï¼å±ç¤ºå»¶æç¶æä¸º `审æ¹ä¸`ã - å»¶æéè¿åï¼æ´æ°è®¡åå½è¿æ¶é´ã sql/vehicle-api.md
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,221 @@ # 车è¾ç®¡çæ¥å£ææ¡£ ## åºç¡ä¿¡æ¯ - **Base URL**: `http://localhost:端å£å·` - **Content-Type**: `application/json` - **è®¤è¯æ¹å¼**: éè¦ç»å½ï¼è¯·æ±å¤´æºå¸¦ tokenï¼ ## æ°æ®åå ¸ - 车è¾ä½¿ç¨ç¶æ | æä¸¾å¼ | å«ä¹ | |--------|------| | `IDLE` | ç©ºé² | | `IN_USE` | 使ç¨ä¸ | | `MAINTENANCE` | ç»´ä¿®ä¸ | | `SCRAPPED` | å·²æ¥åº | --- ## 1. å页æ¥è¯¢è½¦è¾å表 ### 请æ±ä¿¡æ¯ - **URL**: `/approve/vehicle/listPage` - **Method**: `GET` ### 请æ±åæ°ï¼Query Stringï¼ | åæ°å | ç±»å | å¿ å¡« | 说æ | |--------|------|------|------| | current | Long | æ¯ | å½å页ç ï¼é»è®¤1 | | size | Long | æ¯ | æ¯é¡µæ¡æ°ï¼é»è®¤10 | | plateNumber | String | å¦ | 车çå·ï¼æ¨¡ç³æ¥è¯¢ï¼ | | status | String | å¦ | 使ç¨ç¶æï¼ç²¾ç¡®å¹é ï¼ | ### 请æ±ç¤ºä¾ ``` GET /approve/vehicle/listPage?current=1&size=10&plateNumber=京&status=IN_USE ``` ### ååºç¤ºä¾ ```json { "code": 200, "msg": "æä½æå", "data": { "records": [ { "id": 1, "plateNumber": "京A12345", "mileage": 52000.00, "status": "IN_USE", "deleted": 0, "createUser": 1, "createTime": "2026-05-28 10:00:00", "updateUser": 1, "updateTime": "2026-05-28 10:00:00", "deptId": 100 } ], "total": 1, "size": 10, "current": 1, "pages": 1 } } ``` --- ## 2. æ°å¢è½¦è¾ ### 请æ±ä¿¡æ¯ - **URL**: `/approve/vehicle/save` - **Method**: `POST` - **Content-Type**: `application/json` ### 请æ±ä½åæ° | åæ°å | ç±»å | å¿ å¡« | 说æ | |--------|------|------|------| | plateNumber | String | æ¯ | 车çå· | | mileage | Number | å¦ | 车è¾å ¬éæ°ï¼é»è®¤0 | | status | String | æ¯ | 使ç¨ç¶æï¼IDLE / IN_USE / MAINTENANCE / SCRAPPED | ### 请æ±ç¤ºä¾ ```json { "plateNumber": "京A12345", "mileage": 0, "status": "IDLE" } ``` ### ååºç¤ºä¾ ```json { "code": 200, "msg": "æä½æå", "data": true } ``` --- ## 3. 车è¾è¯¦æ ### 请æ±ä¿¡æ¯ - **URL**: `/approve/vehicle/detail` - **Method**: `GET` ### 请æ±åæ°ï¼Query Stringï¼ | åæ°å | ç±»å | å¿ å¡« | 说æ | |--------|------|------|------| | id | Long | æ¯ | 车è¾ID | ### 请æ±ç¤ºä¾ ``` GET /approve/vehicle/detail?id=1 ``` ### ååºç¤ºä¾ ```json { "code": 200, "msg": "æä½æå", "data": { "id": 1, "plateNumber": "京A12345", "mileage": 52000.00, "status": "IN_USE", "deleted": 0, "createUser": 1, "createTime": "2026-05-28 10:00:00", "updateUser": 1, "updateTime": "2026-05-28 10:00:00", "deptId": 100 } } ``` --- ## 4. ä¿®æ¹è½¦è¾ ### 请æ±ä¿¡æ¯ - **URL**: `/approve/vehicle/update` - **Method**: `POST` - **Content-Type**: `application/json` ### 请æ±ä½åæ° | åæ°å | ç±»å | å¿ å¡« | 说æ | |--------|------|------|------| | id | Long | æ¯ | 车è¾ID | | plateNumber | String | å¦ | 车çå· | | mileage | Number | å¦ | 车è¾å ¬éæ° | | status | String | å¦ | 使ç¨ç¶æ | ### 请æ±ç¤ºä¾ ```json { "id": 1, "plateNumber": "京A12345", "mileage": 52000.00, "status": "IN_USE" } ``` ### ååºç¤ºä¾ ```json { "code": 200, "msg": "æä½æå", "data": true } ``` --- ## 5. å é¤è½¦è¾ ### 请æ±ä¿¡æ¯ - **URL**: `/approve/vehicle/delete` - **Method**: `DELETE` - **Content-Type**: `application/json` ### 请æ±ä½åæ° | åæ°å | ç±»å | å¿ å¡« | 说æ | |--------|------|------|------| | ids | Array<Long> | æ¯ | 车è¾IDæ°ç» | ### 请æ±ç¤ºä¾ ```json [1, 2, 3] ``` ### ååºç¤ºä¾ ```json { "code": 200, "msg": "æä½æå", "data": true } ``` sql/vehicle.sql
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,14 @@ -- 车è¾ç®¡ç表 CREATE TABLE `vehicle` ( `id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '车è¾ID', `plate_number` VARCHAR(20) NOT NULL COMMENT '车çå·', `mileage` DECIMAL(10,2) DEFAULT 0 COMMENT '车è¾å ¬éæ°', `status` VARCHAR(20) NOT NULL DEFAULT 'IDLE' COMMENT '使ç¨ç¶æ: IDLEç©ºé² IN_USE使ç¨ä¸ MAINTENANCEç»´ä¿®ä¸ SCRAPPEDå·²æ¥åº', `deleted` TINYINT DEFAULT 0 COMMENT 'é»è¾å é¤: 0å¦ 1æ¯', `create_user` BIGINT COMMENT 'å建人', `create_time` DATETIME COMMENT 'å建æ¶é´', `update_user` BIGINT COMMENT 'æ´æ°äºº', `update_time` DATETIME COMMENT 'æ´æ°æ¶é´', `dept_id` BIGINT COMMENT 'é¨é¨ID', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='车è¾ç®¡ç表'; src/main/java/com/ruoyi/approve/bean/dto/VehicleBorrowRecordDto.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,21 @@ package com.ruoyi.approve.bean.dto; import com.ruoyi.approve.pojo.VehicleBorrowRecord; import com.ruoyi.basic.dto.StorageBlobDTO; import lombok.Data; import java.util.List; @Data public class VehicleBorrowRecordDto extends VehicleBorrowRecord { private Long approvalTemplateId; private String createTimeStart; private String createTimeEnd; private List<StorageBlobDTO> borrowStorageBlobDTOs; private List<StorageBlobDTO> returnStorageBlobDTOs; private String formConfig; } src/main/java/com/ruoyi/approve/bean/dto/VehicleDto.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,11 @@ package com.ruoyi.approve.bean.dto; import com.ruoyi.approve.pojo.Vehicle; import lombok.Data; @Data public class VehicleDto extends Vehicle { private String createTimeStart; private String createTimeEnd; } src/main/java/com/ruoyi/approve/bean/vo/VehicleBorrowRecordVo.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,14 @@ package com.ruoyi.approve.bean.vo; import com.ruoyi.approve.pojo.VehicleBorrowRecord; import com.ruoyi.basic.dto.StorageBlobVO; import lombok.Data; import java.util.List; @Data public class VehicleBorrowRecordVo extends VehicleBorrowRecord { private List<StorageBlobVO> borrowStorageBlobVOList; private List<StorageBlobVO> returnStorageBlobVOList; } src/main/java/com/ruoyi/approve/bean/vo/VehicleVo.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,8 @@ package com.ruoyi.approve.bean.vo; import com.ruoyi.approve.pojo.Vehicle; import lombok.Data; @Data public class VehicleVo extends Vehicle { } src/main/java/com/ruoyi/approve/controller/VehicleController.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,114 @@ package com.ruoyi.approve.controller; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.ruoyi.approve.bean.dto.VehicleBorrowRecordDto; import com.ruoyi.approve.bean.dto.VehicleDto; import com.ruoyi.approve.bean.vo.VehicleBorrowRecordVo; import com.ruoyi.approve.bean.vo.VehicleVo; import com.ruoyi.approve.service.VehicleService; import com.ruoyi.approve.service.VehicleBorrowRecordService; import com.ruoyi.framework.aspectj.lang.annotation.Log; import com.ruoyi.framework.aspectj.lang.enums.BusinessType; import com.ruoyi.framework.web.controller.BaseController; import com.ruoyi.framework.web.domain.R; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import lombok.AllArgsConstructor; import org.springframework.web.bind.annotation.*; import java.util.List; /** * 车è¾ç®¡ç å端æ§å¶å¨ */ @RestController @RequestMapping("/approve/vehicle") @Tag(name = "车è¾ç®¡ç", description = "车è¾ç®¡ç") @AllArgsConstructor public class VehicleController extends BaseController { private final VehicleService vehicleService; private final VehicleBorrowRecordService vehicleBorrowRecordService; @GetMapping("/listPage") @Operation(summary = "å页æ¥è¯¢è½¦è¾å表") @Log(title = "车è¾å表å页æ¥è¯¢", businessType = BusinessType.OTHER) public R listPage(Page<VehicleVo> page, VehicleDto vehicle) { return R.ok(vehicleService.listPage(page, vehicle)); } @PostMapping("/save") @Operation(summary = "æ°å¢è½¦è¾") @Log(title = "è½¦è¾æ°å¢", businessType = BusinessType.INSERT) public R save(@RequestBody VehicleDto vehicle) { return vehicleService.add(vehicle) ? R.ok() : R.fail(); } @GetMapping("/detail") @Operation(summary = "车è¾è¯¦æ ") public R detail(Long id) { return R.ok(vehicleService.detail(id)); } @PutMapping("/update") @Operation(summary = "ä¿®æ¹è½¦è¾") @Log(title = "车è¾ä¿®æ¹", businessType = BusinessType.UPDATE) public R update(@RequestBody VehicleDto vehicle) { return vehicleService.update(vehicle) ? R.ok() : R.fail(); } @DeleteMapping("/delete") @Operation(summary = "å é¤è½¦è¾") @Log(title = "车è¾å é¤", businessType = BusinessType.DELETE) public R delete(@RequestBody List<Long> ids) { return vehicleService.delete(ids) ? R.ok() : R.fail(); } @GetMapping("/borrow/listPage") @Operation(summary = "å页æ¥è¯¢ååºè®°å½å表") @Log(title = "ååºè®°å½å页æ¥è¯¢", businessType = BusinessType.OTHER) public R borrowListPage(Page<VehicleBorrowRecordVo> page, VehicleBorrowRecordDto record) { return R.ok(vehicleBorrowRecordService.listPage(page, record)); } @PostMapping("/borrow/save") @Operation(summary = "æ°å¢ååºè®°å½") @Log(title = "ååºè®°å½æ°å¢", businessType = BusinessType.INSERT) public R borrowSave(@RequestBody VehicleBorrowRecordDto record) { return vehicleBorrowRecordService.add(record) ? R.ok() : R.fail(); } @GetMapping("/borrow/detail") @Operation(summary = "ååºè®°å½è¯¦æ ") public R borrowDetail(Long id) { return R.ok(vehicleBorrowRecordService.detail(id)); } @PutMapping("/borrow/update") @Operation(summary = "ä¿®æ¹ååºè®°å½") @Log(title = "ååºè®°å½ä¿®æ¹", businessType = BusinessType.UPDATE) public R borrowUpdate(@RequestBody VehicleBorrowRecordDto record) { return vehicleBorrowRecordService.update(record) ? R.ok() : R.fail(); } @DeleteMapping("/borrow/delete") @Operation(summary = "å é¤ååºè®°å½") @Log(title = "ååºè®°å½å é¤", businessType = BusinessType.DELETE) public R borrowDelete(@RequestBody List<Long> ids) { return vehicleBorrowRecordService.delete(ids) ? R.ok() : R.fail(); } @PostMapping("/borrow/return") @Operation(summary = "å½è¿è½¦è¾") @Log(title = "车è¾å½è¿", businessType = BusinessType.UPDATE) public R borrowReturn(@RequestBody VehicleBorrowRecordDto record) { return vehicleBorrowRecordService.returnVehicle(record) ? R.ok() : R.fail(); } @PostMapping("/borrow/delay") @Operation(summary = "åèµ·å»¶æç³è¯·") @Log(title = "å»¶æç³è¯·", businessType = BusinessType.INSERT) public R borrowDelay(@RequestBody VehicleBorrowRecordDto record) { return vehicleBorrowRecordService.delay(record) ? R.ok() : R.fail(); } } src/main/java/com/ruoyi/approve/mapper/VehicleBorrowRecordMapper.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,19 @@ package com.ruoyi.approve.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.approve.bean.dto.VehicleBorrowRecordDto; import com.ruoyi.approve.bean.vo.VehicleBorrowRecordVo; import com.ruoyi.approve.pojo.VehicleBorrowRecord; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; /** * 车è¾ååºè®°å½ Mapper */ @Mapper public interface VehicleBorrowRecordMapper extends BaseMapper<VehicleBorrowRecord> { IPage<VehicleBorrowRecordVo> listPage(Page<VehicleBorrowRecordVo> page, @Param("record") VehicleBorrowRecordDto record); } src/main/java/com/ruoyi/approve/mapper/VehicleMapper.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,19 @@ package com.ruoyi.approve.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.approve.bean.dto.VehicleDto; import com.ruoyi.approve.bean.vo.VehicleVo; import com.ruoyi.approve.pojo.Vehicle; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; /** * 车è¾ç®¡ç Mapper */ @Mapper public interface VehicleMapper extends BaseMapper<Vehicle> { IPage<VehicleVo> listPage(Page<VehicleVo> page, @Param("vehicle") VehicleDto vehicle); } src/main/java/com/ruoyi/approve/pojo/Vehicle.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,61 @@ package com.ruoyi.approve.pojo; import com.baomidou.mybatisplus.annotation.*; import io.swagger.annotations.ApiModel; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Getter; import lombok.Setter; import lombok.ToString; import java.io.Serializable; import java.math.BigDecimal; import java.time.LocalDateTime; /** * 车è¾ç®¡ç表 */ @Getter @Setter @ToString @TableName("vehicle") @ApiModel(value = "Vehicle对象", description = "车è¾ç®¡ç表") public class Vehicle implements Serializable { private static final long serialVersionUID = 1L; @Schema(description = "车è¾ID") @TableId(value = "id", type = IdType.AUTO) private Long id; @Schema(description = "车çå·") private String plateNumber; @Schema(description = "车è¾å ¬éæ°") private BigDecimal mileage; @Schema(description = "使ç¨ç¶æ: IDLEç©ºé² IN_USE使ç¨ä¸") private String status; @Schema(description = "é»è¾å é¤: 0æªå é¤ 1å·²å é¤") private Integer deleted; @Schema(description = "å建人") @TableField(fill = FieldFill.INSERT) private Long createUser; @Schema(description = "å建æ¶é´") @TableField(fill = FieldFill.INSERT) private LocalDateTime createTime; @Schema(description = "æ´æ°äºº") @TableField(fill = FieldFill.INSERT_UPDATE) private Long updateUser; @Schema(description = "æ´æ°æ¶é´") @TableField(fill = FieldFill.INSERT_UPDATE) private LocalDateTime updateTime; @Schema(description = "é¨é¨ID") @TableField(fill = FieldFill.INSERT) private Long deptId; } src/main/java/com/ruoyi/approve/pojo/VehicleBorrowRecord.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,130 @@ package com.ruoyi.approve.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.v3.oas.annotations.media.Schema; import lombok.Data; import org.springframework.format.annotation.DateTimeFormat; import java.io.Serializable; import java.time.LocalDateTime; /** * 车è¾ååºè®°å½è¡¨ */ @Data @TableName("vehicle_borrow_record") @Schema(name = "VehicleBorrowRecord", description = "车è¾ååºè®°å½è¡¨") public class VehicleBorrowRecord implements Serializable { private static final long serialVersionUID = 1L; @Schema(description = "主é®ID") @TableId(value = "id", type = IdType.AUTO) private Long id; @Schema(description = "ååºåå·") private String borrowNo; @Schema(description = "车è¾ID") private Long vehicleId; @Schema(description = "车çå·") private String vehiclePlateNumber; @Schema(description = "ç³è¯·äººID") private Long applicantId; @Schema(description = "ç³è¯·äººå§å") private String applicantName; @Schema(description = "ç³è¯·é¨é¨ID") private Long applicantDeptId; @Schema(description = "ç³è¯·é¨é¨åç§°") private String applicantDeptName; @Schema(description = "ååºåå ") private String borrowReason; @Schema(description = "ååºå¼å§æ¶é´") @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") private LocalDateTime borrowStartTime; @Schema(description = "计åå½è¿æ¶é´") @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") private LocalDateTime plannedReturnTime; @Schema(description = "å®é å½è¿æ¶é´") @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") private LocalDateTime actualReturnTime; @Schema(description = "ååºç¶æ DRAFT-è稿 IN_APPROVAL-审æ¹ä¸ BORROWING-ååºä¸ RETURNED-å·²å½è¿ REJECTED-已驳å") private String borrowStatus; @Schema(description = "ååºå®¡æ¹å®ä¾ID") private Long approvalInstanceId; @Schema(description = "ååºå®¡æ¹éè¿æ¶é´") @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") private LocalDateTime approvedTime; @Schema(description = "å½è¿æ¶é´") @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") private LocalDateTime returnedTime; @Schema(description = "å»¶æå®¡æ¹å®ä¾ID") private Long extendApprovalInstanceId; @Schema(description = "å»¶æç¶æ NONE-æªç³è¯· PENDING-审æ¹ä¸ APPROVED-å·²éè¿ REJECTED-已驳å") private String extendStatus; @Schema(description = "å»¶æç®æ å½è¿æ¶é´") @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") private LocalDateTime extendTargetReturnTime; @Schema(description = "å»¶æåå ") private String extendReason; @Schema(description = "å»¶æå®¡æ¹éè¿æ¶é´") @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") private LocalDateTime extendApprovedTime; @Schema(description = "é»è¾å é¤: 0æªå é¤ 1å·²å é¤") private Integer deleted; @Schema(description = "å建人") @TableField(fill = FieldFill.INSERT) private Long createUser; @Schema(description = "å建æ¶é´") @TableField(fill = FieldFill.INSERT) @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") private LocalDateTime createTime; @Schema(description = "æ´æ°äºº") @TableField(fill = FieldFill.INSERT_UPDATE) private Long updateUser; @Schema(description = "æ´æ°æ¶é´") @TableField(fill = FieldFill.INSERT_UPDATE) @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") private LocalDateTime updateTime; @Schema(description = "é¨é¨ID") @TableField(fill = FieldFill.INSERT) private Long deptId; } src/main/java/com/ruoyi/approve/service/VehicleBorrowRecordService.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,35 @@ package com.ruoyi.approve.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.approve.bean.dto.VehicleBorrowRecordDto; import com.ruoyi.approve.bean.vo.VehicleBorrowRecordVo; import com.ruoyi.approve.pojo.VehicleBorrowRecord; import com.ruoyi.basic.dto.StorageBlobDTO; import java.util.List; /** * 车è¾ååºè®°å½ Service */ public interface VehicleBorrowRecordService extends IService<VehicleBorrowRecord> { IPage<VehicleBorrowRecordVo> listPage(Page<VehicleBorrowRecordVo> page, VehicleBorrowRecordDto record); Boolean add(VehicleBorrowRecordDto record); VehicleBorrowRecord detail(Long id); Boolean update(VehicleBorrowRecordDto record); Boolean delete(List<Long> ids); Boolean returnVehicle(VehicleBorrowRecordDto record); Boolean delay(VehicleBorrowRecordDto record); void saveBorrowAttachments(Long recordId, List<StorageBlobDTO> storageBlobDTOs); void saveReturnAttachments(Long recordId, List<StorageBlobDTO> storageBlobDTOs); } src/main/java/com/ruoyi/approve/service/VehicleService.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,26 @@ package com.ruoyi.approve.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.approve.bean.dto.VehicleDto; import com.ruoyi.approve.bean.vo.VehicleVo; import com.ruoyi.approve.pojo.Vehicle; import java.util.List; /** * 车è¾ç®¡ç Service */ public interface VehicleService extends IService<Vehicle> { IPage<VehicleVo> listPage(Page<VehicleVo> page, VehicleDto vehicle); Boolean add(VehicleDto vehicle); Vehicle detail(Long id); Boolean update(VehicleDto vehicle); Boolean delete(List<Long> ids); } src/main/java/com/ruoyi/approve/service/impl/ApprovalInstanceServiceImpl.java
@@ -12,6 +12,8 @@ import com.ruoyi.approve.mapper.ApprovalInstanceMapper; import com.ruoyi.approve.mapper.ApprovalTemplateNodeApproverMapper; import com.ruoyi.approve.mapper.FinReimbursementMapper; import com.ruoyi.approve.mapper.VehicleBorrowRecordMapper; import com.ruoyi.approve.mapper.VehicleMapper; import com.ruoyi.approve.pojo.*; import com.ruoyi.approve.service.*; import com.ruoyi.approve.utils.ApproveProcessConfigNodeUtils; @@ -86,6 +88,8 @@ private final SalesQuotationMapper salesQuotationMapper; private final ShippingInfoMapper shippingInfoMapper; private final QualityInspectHelper qualityInspectHelper; private final VehicleBorrowRecordMapper vehicleBorrowRecordMapper; private final VehicleMapper vehicleMapper; private final EnterpriseNewsScopeUserMapper enterpriseNewsScopeUserMapper; private final SysUserMapper sysUserMapper; private final SysUserDeptMapper sysUserDeptMapper; @@ -328,6 +332,26 @@ .eq(FinReimbursement::getId, instance.getBusinessId()) .set(FinReimbursement::getBillStatus, "REJECTED") ); } else if (TypeEnums.VEHICLE_BORROW_APPROVAL.getCode().equals(instance.getBusinessType())) { vehicleBorrowRecordMapper.update( null, new LambdaUpdateWrapper<VehicleBorrowRecord>() .eq(VehicleBorrowRecord::getId, instance.getBusinessId()) .set(VehicleBorrowRecord::getBorrowStatus, "REJECTED") .set(VehicleBorrowRecord::getApprovalInstanceId, instance.getId()) ); VehicleBorrowRecord borrowRecord = vehicleBorrowRecordMapper.selectById(instance.getBusinessId()); if (borrowRecord != null) { syncVehicleBorrowStatus(borrowRecord.getVehicleId()); } } else if (TypeEnums.VEHICLE_DELAY_APPROVAL.getCode().equals(instance.getBusinessType())) { vehicleBorrowRecordMapper.update( null, new LambdaUpdateWrapper<VehicleBorrowRecord>() .eq(VehicleBorrowRecord::getId, instance.getBusinessId()) .set(VehicleBorrowRecord::getExtendStatus, "REJECTED") .set(VehicleBorrowRecord::getExtendApprovalInstanceId, null) ); } return R.ok("审æ¹å·²é©³å"); } @@ -445,6 +469,11 @@ } if (TypeEnums.ENTERPRISE_NEWS_APPROVAL.getCode().equals(businessType)) { handleNewsApprovalFinished(instance, status); return; } if (TypeEnums.VEHICLE_BORROW_APPROVAL.getCode().equals(businessType) || TypeEnums.VEHICLE_DELAY_APPROVAL.getCode().equals(businessType)) { handleVehicleBorrowApprovalFinished(instance, status); } } @@ -586,6 +615,104 @@ shippingInfoMapper.updateById(shippingInfo); } private void handleVehicleBorrowApprovalFinished(ApprovalInstance instance, String status) { if (instance == null || instance.getBusinessId() == null) { return; } VehicleBorrowRecord record = vehicleBorrowRecordMapper.selectById(instance.getBusinessId()); if (record == null || Integer.valueOf(1).equals(record.getDeleted())) { return; } if (TypeEnums.VEHICLE_BORROW_APPROVAL.getCode().equals(instance.getBusinessType())) { if ("APPROVED".equals(status)) { Vehicle vehicle = vehicleMapper.selectById(record.getVehicleId()); if (vehicle == null) { throw new ServiceException("车è¾ä¸åå¨"); } VehicleBorrowRecord update = new VehicleBorrowRecord(); update.setId(record.getId()); update.setBorrowStatus("BORROWING"); update.setApprovedTime(instance.getFinishTime()); update.setApprovalInstanceId(instance.getId()); vehicleBorrowRecordMapper.updateById(update); syncVehicleBorrowStatus(vehicle.getId()); return; } if ("REJECTED".equals(status)) { VehicleBorrowRecord update = new VehicleBorrowRecord(); update.setId(record.getId()); update.setBorrowStatus("REJECTED"); update.setApprovalInstanceId(instance.getId()); vehicleBorrowRecordMapper.updateById(update); syncVehicleBorrowStatus(record.getVehicleId()); return; } if ("PENDING".equals(status)) { VehicleBorrowRecord update = new VehicleBorrowRecord(); update.setId(record.getId()); update.setBorrowStatus("IN_APPROVAL"); update.setApprovalInstanceId(instance.getId()); vehicleBorrowRecordMapper.updateById(update); } return; } if (TypeEnums.VEHICLE_DELAY_APPROVAL.getCode().equals(instance.getBusinessType())) { if ("APPROVED".equals(status)) { vehicleBorrowRecordMapper.update( null, new LambdaUpdateWrapper<VehicleBorrowRecord>() .eq(VehicleBorrowRecord::getId, record.getId()) .set(VehicleBorrowRecord::getBorrowStatus, "BORROWING") .set(VehicleBorrowRecord::getExtendStatus, "APPROVED") .set(VehicleBorrowRecord::getExtendApprovedTime, instance.getFinishTime()) .set(VehicleBorrowRecord::getPlannedReturnTime, record.getExtendTargetReturnTime()) .set(VehicleBorrowRecord::getExtendApprovalInstanceId, null) ); return; } if ("REJECTED".equals(status)) { vehicleBorrowRecordMapper.update( null, new LambdaUpdateWrapper<VehicleBorrowRecord>() .eq(VehicleBorrowRecord::getId, record.getId()) .set(VehicleBorrowRecord::getExtendStatus, "REJECTED") .set(VehicleBorrowRecord::getExtendApprovalInstanceId, null) ); return; } if ("PENDING".equals(status)) { VehicleBorrowRecord update = new VehicleBorrowRecord(); update.setId(record.getId()); update.setExtendStatus("PENDING"); update.setExtendApprovalInstanceId(instance.getId()); vehicleBorrowRecordMapper.updateById(update); } } } private void syncVehicleBorrowStatus(Long vehicleId) { if (vehicleId == null) { return; } long activeBorrowCount = vehicleBorrowRecordMapper.selectCount( new LambdaQueryWrapper<VehicleBorrowRecord>() .eq(VehicleBorrowRecord::getVehicleId, vehicleId) .eq(VehicleBorrowRecord::getDeleted, 0) .in(VehicleBorrowRecord::getBorrowStatus, "IN_APPROVAL", "BORROWING") ); Vehicle vehicle = vehicleMapper.selectById(vehicleId); if (vehicle == null) { throw new ServiceException("车è¾ä¸åå¨"); } Vehicle vehicleUpdate = new Vehicle(); vehicleUpdate.setId(vehicleId); vehicleUpdate.setStatus(activeBorrowCount > 0 ? "IN_USE" : "IDLE"); vehicleMapper.updateById(vehicleUpdate); } private List<ApprovalTask> createNodeAndTasks(ApprovalInstance instance, ApprovalTemplateNode templateNode) { List<ApprovalTemplateNodeApprover> approvers = approvalTemplateNodeApproverMapper.selectList( new LambdaQueryWrapper<ApprovalTemplateNodeApprover>() src/main/java/com/ruoyi/approve/service/impl/VehicleBorrowRecordServiceImpl.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,454 @@ package com.ruoyi.approve.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.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.ruoyi.approve.bean.dto.ApprovalInstanceDto; import com.ruoyi.approve.bean.dto.VehicleBorrowRecordDto; import com.ruoyi.approve.bean.vo.VehicleBorrowRecordVo; import com.ruoyi.approve.mapper.ApprovalTemplateMapper; import com.ruoyi.approve.mapper.VehicleBorrowRecordMapper; import com.ruoyi.approve.mapper.VehicleMapper; import com.ruoyi.approve.pojo.ApprovalTemplate; import com.ruoyi.approve.pojo.Vehicle; import com.ruoyi.approve.pojo.VehicleBorrowRecord; import com.ruoyi.approve.service.ApprovalInstanceService; import com.ruoyi.approve.service.VehicleBorrowRecordService; import com.ruoyi.basic.dto.StorageBlobDTO; import com.ruoyi.basic.enums.ApplicationTypeEnum; import com.ruoyi.basic.enums.RecordTypeEnum; import com.ruoyi.basic.utils.FileUtil; import com.ruoyi.common.enums.TypeEnums; import com.ruoyi.common.exception.ServiceException; import com.ruoyi.common.utils.OrderUtils; import com.ruoyi.common.utils.SecurityUtils; import com.ruoyi.framework.security.LoginUser; import com.ruoyi.project.system.domain.SysDept; import com.ruoyi.project.system.domain.SysUser; import com.ruoyi.project.system.mapper.SysDeptMapper; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.StringUtils; import java.time.LocalDateTime; import java.util.Collections; import java.util.List; import java.util.Locale; /** * 车è¾ååºè®°å½ Service å®ç° */ @Service @RequiredArgsConstructor public class VehicleBorrowRecordServiceImpl extends ServiceImpl<VehicleBorrowRecordMapper, VehicleBorrowRecord> implements VehicleBorrowRecordService { private static final String BORROW_STATUS_DRAFT = "DRAFT"; private static final String BORROW_STATUS_IN_APPROVAL = "IN_APPROVAL"; private static final String BORROW_STATUS_BORROWING = "BORROWING"; private static final String BORROW_STATUS_RETURNED = "RETURNED"; private static final String BORROW_STATUS_REJECTED = "REJECTED"; private static final String VEHICLE_STATUS_IDLE = "IDLE"; private static final String EXTEND_STATUS_NONE = "NONE"; private static final String EXTEND_STATUS_PENDING = "PENDING"; private static final String EXTEND_STATUS_APPROVED = "APPROVED"; private static final String EXTEND_STATUS_REJECTED = "REJECTED"; private final VehicleBorrowRecordMapper vehicleBorrowRecordMapper; private final VehicleMapper vehicleMapper; private final SysDeptMapper sysDeptMapper; private final ApprovalTemplateMapper approvalTemplateMapper; private final ApprovalInstanceService approvalInstanceService; private final FileUtil fileUtil; @Override public IPage<VehicleBorrowRecordVo> listPage(Page<VehicleBorrowRecordVo> page, VehicleBorrowRecordDto record) { return vehicleBorrowRecordMapper.listPage(page, record); } @Override @Transactional(rollbackFor = Exception.class) public Boolean add(VehicleBorrowRecordDto record) { validateBorrowRecord(record, false); Vehicle vehicle = getAvailableVehicle(record.getVehicleId()); fillApplicantInfo(record); record.setBorrowNo(OrderUtils.countTodayByCreateTime( vehicleBorrowRecordMapper, "CLJC", "borrow_no", record.getCreateTime() != null ? record.getCreateTime() : LocalDateTime.now() )); record.setVehiclePlateNumber(vehicle.getPlateNumber()); record.setBorrowStatus(normalizeBorrowStatus(record.getBorrowStatus())); record.setExtendStatus(EXTEND_STATUS_NONE); record.setDeleted(0); boolean saved = this.save(record); if (!saved || record.getId() == null) { throw new ServiceException("æ°å¢è½¦è¾ååºè®°å½å¤±è´¥"); } saveBorrowAttachments(record.getId(), record.getBorrowStorageBlobDTOs()); if (BORROW_STATUS_IN_APPROVAL.equals(record.getBorrowStatus())) { startApproval(record, TypeEnums.VEHICLE_BORROW_APPROVAL.getCode()); } return true; } @Override public VehicleBorrowRecord detail(Long id) { if (id == null) { return null; } return this.getOne( new LambdaQueryWrapper<VehicleBorrowRecord>() .eq(VehicleBorrowRecord::getId, id) .eq(VehicleBorrowRecord::getDeleted, 0) .last("LIMIT 1") ); } @Override @Transactional(rollbackFor = Exception.class) public Boolean update(VehicleBorrowRecordDto record) { validateBorrowRecord(record, true); if (record.getId() == null) { throw new ServiceException("ååºè®°å½IDä¸è½ä¸ºç©º"); } VehicleBorrowRecord existing = detail(record.getId()); if (existing == null) { throw new ServiceException("ååºè®°å½ä¸åå¨"); } if (!BORROW_STATUS_DRAFT.equals(existing.getBorrowStatus()) && !BORROW_STATUS_REJECTED.equals(existing.getBorrowStatus())) { throw new ServiceException("å½åç¶æçååºè®°å½ä¸å 许修æ¹"); } Vehicle vehicle = getAvailableVehicle(record.getVehicleId()); fillApplicantInfo(record); record.setVehiclePlateNumber(vehicle.getPlateNumber()); record.setBorrowStatus(normalizeBorrowStatus(record.getBorrowStatus())); if (record.getExtendStatus() == null) { record.setExtendStatus(existing.getExtendStatus()); } record.setApprovalInstanceId(existing.getApprovalInstanceId()); record.setExtendApprovalInstanceId(existing.getExtendApprovalInstanceId()); record.setDeleted(existing.getDeleted()); boolean updated = this.updateById(record); if (!updated) { return false; } if (record.getBorrowStorageBlobDTOs() != null) { saveBorrowAttachments(record.getId(), record.getBorrowStorageBlobDTOs()); } if (BORROW_STATUS_IN_APPROVAL.equals(record.getBorrowStatus())) { if (existing.getApprovalInstanceId() != null) { approvalInstanceService.delete(Collections.singletonList(existing.getApprovalInstanceId())); } startApproval(record, TypeEnums.VEHICLE_BORROW_APPROVAL.getCode()); } return true; } @Override @Transactional(rollbackFor = Exception.class) public Boolean delete(List<Long> ids) { if (ids == null || ids.isEmpty()) { return false; } long activeCount = this.count( Wrappers.<VehicleBorrowRecord>lambdaQuery() .in(VehicleBorrowRecord::getId, ids) .eq(VehicleBorrowRecord::getDeleted, 0) .in(VehicleBorrowRecord::getBorrowStatus, BORROW_STATUS_IN_APPROVAL, BORROW_STATUS_BORROWING) ); if (activeCount > 0) { throw new ServiceException("审æ¹ä¸æååºä¸çè®°å½ä¸å 许å é¤"); } int rows = vehicleBorrowRecordMapper.update( null, Wrappers.<VehicleBorrowRecord>lambdaUpdate() .in(VehicleBorrowRecord::getId, ids) .eq(VehicleBorrowRecord::getDeleted, 0) .set(VehicleBorrowRecord::getDeleted, 1) ); return rows > 0; } @Override @Transactional(rollbackFor = Exception.class) public Boolean returnVehicle(VehicleBorrowRecordDto record) { if (record == null || record.getId() == null) { throw new ServiceException("ååºè®°å½IDä¸è½ä¸ºç©º"); } VehicleBorrowRecord existing = detail(record.getId()); if (existing == null) { throw new ServiceException("ååºè®°å½ä¸åå¨"); } if (!BORROW_STATUS_BORROWING.equals(existing.getBorrowStatus())) { throw new ServiceException("åªæååºä¸çè½¦è¾æè½å½è¿"); } if (EXTEND_STATUS_PENDING.equals(existing.getExtendStatus()) || existing.getExtendApprovalInstanceId() != null) { throw new ServiceException("å»¶æå®¡æ¹ä¸çè®°å½ä¸å è®¸ç´æ¥å½è¿"); } LocalDateTime now = record.getActualReturnTime() != null ? record.getActualReturnTime() : LocalDateTime.now(); VehicleBorrowRecord update = new VehicleBorrowRecord(); update.setId(existing.getId()); update.setActualReturnTime(now); update.setReturnedTime(now); update.setBorrowStatus(BORROW_STATUS_RETURNED); update.setUpdateUser(SecurityUtils.getUserId()); update.setUpdateTime(now); update.setExtendStatus(existing.getExtendStatus()); int rows = vehicleBorrowRecordMapper.updateById(update); if (rows != 1) { throw new ServiceException("å½è¿è½¦è¾å¤±è´¥"); } saveReturnAttachments(existing.getId(), record.getReturnStorageBlobDTOs()); Vehicle vehicle = new Vehicle(); vehicle.setId(existing.getVehicleId()); vehicle.setStatus("IDLE"); vehicleMapper.updateById(vehicle); return true; } @Override @Transactional(rollbackFor = Exception.class) public Boolean delay(VehicleBorrowRecordDto record) { if (record == null || record.getId() == null) { throw new ServiceException("ååºè®°å½IDä¸è½ä¸ºç©º"); } if (record.getExtendTargetReturnTime() == null) { throw new ServiceException("å»¶æåçå½è¿æ¶é´ä¸è½ä¸ºç©º"); } VehicleBorrowRecord existing = detail(record.getId()); if (existing == null) { throw new ServiceException("ååºè®°å½ä¸åå¨"); } if (!BORROW_STATUS_BORROWING.equals(existing.getBorrowStatus())) { throw new ServiceException("åªæååºä¸çè½¦è¾æè½ç³è¯·å»¶æ"); } if (EXTEND_STATUS_PENDING.equals(existing.getExtendStatus()) || existing.getExtendApprovalInstanceId() != null) { throw new ServiceException("å·²æå»¶æå®¡æ¹ä¸çç³è¯·ï¼è¯·å¿éå¤æäº¤"); } if (existing.getPlannedReturnTime() != null && !record.getExtendTargetReturnTime().isAfter(existing.getPlannedReturnTime())) { throw new ServiceException("å»¶æåçå½è¿æ¶é´å¿ é¡»æäºå½å计åå½è¿æ¶é´"); } VehicleBorrowRecord update = new VehicleBorrowRecord(); update.setId(existing.getId()); update.setExtendReason(record.getExtendReason()); update.setExtendTargetReturnTime(record.getExtendTargetReturnTime()); update.setExtendStatus(EXTEND_STATUS_PENDING); update.setUpdateUser(SecurityUtils.getUserId()); update.setUpdateTime(LocalDateTime.now()); int rows = vehicleBorrowRecordMapper.updateById(update); if (rows != 1) { throw new ServiceException("ä¿åå»¶æç³è¯·å¤±è´¥"); } VehicleBorrowRecord refreshed = detail(existing.getId()); startApproval(refreshed, TypeEnums.VEHICLE_DELAY_APPROVAL.getCode()); return true; } private void validateBorrowRecord(VehicleBorrowRecord record, boolean ignoreId) { if (record == null) { throw new ServiceException("ååºè®°å½ä¸è½ä¸ºç©º"); } if (!ignoreId && record.getId() != null) { throw new ServiceException("æ°å¢ååºè®°å½ä¸å è®¸ä¼ å ¥ID"); } if (record.getVehicleId() == null) { throw new ServiceException("è¯·éæ©è½¦è¾"); } if (record.getBorrowStartTime() == null) { throw new ServiceException("ååºå¼å§æ¶é´ä¸è½ä¸ºç©º"); } if (record.getPlannedReturnTime() == null) { throw new ServiceException("计åå½è¿æ¶é´ä¸è½ä¸ºç©º"); } if (!record.getPlannedReturnTime().isAfter(record.getBorrowStartTime())) { throw new ServiceException("计åå½è¿æ¶é´å¿ é¡»æäºååºå¼å§æ¶é´"); } if (!StringUtils.hasText(record.getBorrowStatus())) { record.setBorrowStatus(BORROW_STATUS_DRAFT); } String normalized = normalizeBorrowStatus(record.getBorrowStatus()); if (normalized == null) { throw new ServiceException("ååºç¶æåªæ¯æ DRAFT æ IN_APPROVAL"); } record.setBorrowStatus(normalized); if (StringUtils.hasText(record.getExtendStatus())) { String extendStatus = record.getExtendStatus().trim().toUpperCase(Locale.ROOT); if (!EXTEND_STATUS_NONE.equals(extendStatus) && !EXTEND_STATUS_PENDING.equals(extendStatus) && !EXTEND_STATUS_APPROVED.equals(extendStatus) && !EXTEND_STATUS_REJECTED.equals(extendStatus)) { throw new ServiceException("å»¶æç¶æä¸åæ³"); } } } private Vehicle getAvailableVehicle(Long vehicleId) { Vehicle vehicle = vehicleMapper.selectOne( new LambdaQueryWrapper<Vehicle>() .eq(Vehicle::getId, vehicleId) .eq(Vehicle::getDeleted, 0) .last("LIMIT 1") ); if (vehicle == null) { throw new ServiceException("车è¾ä¸åå¨"); } long activeBorrowCount = this.count( Wrappers.<VehicleBorrowRecord>lambdaQuery() .eq(VehicleBorrowRecord::getVehicleId, vehicleId) .eq(VehicleBorrowRecord::getDeleted, 0) .in(VehicleBorrowRecord::getBorrowStatus, BORROW_STATUS_IN_APPROVAL, BORROW_STATUS_BORROWING) ); if (activeBorrowCount > 0) { throw new ServiceException("å½å车è¾å·²æååºè®°å½ï¼ä¸è½éå¤ååº"); } if (!VEHICLE_STATUS_IDLE.equals(vehicle.getStatus())) { throw new ServiceException("å½å车è¾ä¸å¨å¯ååºç¶æ"); } return vehicle; } private void fillApplicantInfo(VehicleBorrowRecord record) { LoginUser loginUser = SecurityUtils.getLoginUser(); if (loginUser == null || loginUser.getUser() == null) { throw new ServiceException("请å ç»å½"); } SysUser user = loginUser.getUser(); record.setApplicantId(user.getUserId()); record.setApplicantName(user.getNickName()); record.setApplicantDeptId(user.getDeptId()); if (user.getDeptId() != null) { SysDept dept = sysDeptMapper.selectById(user.getDeptId()); record.setApplicantDeptName(dept != null ? dept.getDeptName() : null); } } private void startApproval(VehicleBorrowRecord record, Long businessType) { Long templateId = null; if (record instanceof VehicleBorrowRecordDto) { templateId = ((VehicleBorrowRecordDto) record).getApprovalTemplateId(); } ApprovalTemplate approvalTemplate = resolveApprovalTemplate(templateId, businessType); ApprovalInstanceDto approvalInstanceDto = new ApprovalInstanceDto(); approvalInstanceDto.setTemplateId(approvalTemplate.getId()); approvalInstanceDto.setTemplateName(approvalTemplate.getTemplateName()); approvalInstanceDto.setBusinessId(record.getId()); approvalInstanceDto.setBusinessType(businessType); approvalInstanceDto.setTitle(buildApprovalTitle(record, businessType)); approvalInstanceDto.setApplicantId(record.getApplicantId()); approvalInstanceDto.setApplicantName(record.getApplicantName()); approvalInstanceDto.setApplyTime(LocalDateTime.now()); approvalInstanceDto.setStatus("PENDING"); approvalInstanceDto.setCurrentLevel(1); boolean saved = approvalInstanceService.add(approvalInstanceDto); if (!saved || approvalInstanceDto.getId() == null) { throw new ServiceException("å起车è¾å®¡æ¹å¤±è´¥"); } VehicleBorrowRecord update = new VehicleBorrowRecord(); update.setId(record.getId()); if (TypeEnums.VEHICLE_BORROW_APPROVAL.getCode().equals(businessType)) { update.setApprovalInstanceId(approvalInstanceDto.getId()); update.setBorrowStatus(BORROW_STATUS_IN_APPROVAL); } else if (TypeEnums.VEHICLE_DELAY_APPROVAL.getCode().equals(businessType)) { update.setExtendApprovalInstanceId(approvalInstanceDto.getId()); update.setExtendStatus(EXTEND_STATUS_PENDING); } int rows = vehicleBorrowRecordMapper.updateById(update); if (rows != 1) { throw new ServiceException("å填审æ¹å®ä¾å¤±è´¥"); } } private ApprovalTemplate resolveApprovalTemplate(Long templateId, Long businessType) { if (templateId != null) { ApprovalTemplate approvalTemplate = approvalTemplateMapper.selectById(templateId); if (approvalTemplate == null || !Integer.valueOf(0).equals(approvalTemplate.getDeleted())) { throw new ServiceException("å®¡æ¹æ¨¡æ¿ä¸åå¨"); } if (approvalTemplate.getEnabled() == null || approvalTemplate.getEnabled() == 0) { throw new ServiceException("å®¡æ¹æ¨¡æ¿æªå¯ç¨"); } if (approvalTemplate.getBusinessType() != null && !approvalTemplate.getBusinessType().equals(businessType)) { throw new ServiceException("å®¡æ¹æ¨¡æ¿ç±»åä¸å½åä¸å¡ä¸å¹é "); } return approvalTemplate; } ApprovalTemplate approvalTemplate = approvalTemplateMapper.selectOne( Wrappers.<ApprovalTemplate>lambdaQuery() .eq(ApprovalTemplate::getBusinessType, businessType) .eq(ApprovalTemplate::getEnabled, (byte) 1) .eq(ApprovalTemplate::getDeleted, 0) .orderByDesc(ApprovalTemplate::getId) .last("LIMIT 1") ); if (approvalTemplate == null) { throw new ServiceException("请å é 置车è¾å®¡æ¹æ¨¡æ¿"); } return approvalTemplate; } @Override public void saveBorrowAttachments(Long recordId, List<StorageBlobDTO> storageBlobDTOs) { if (recordId == null || recordId <= 0) { return; } if (storageBlobDTOs == null) { return; } fileUtil.saveStorageAttachmentByRecordTypeAndRecordId( ApplicationTypeEnum.FILE.getType(), RecordTypeEnum.VEHICLE_BORROW_RECORD, recordId, storageBlobDTOs ); } @Override public void saveReturnAttachments(Long recordId, List<StorageBlobDTO> storageBlobDTOs) { if (recordId == null || recordId <= 0) { return; } if (storageBlobDTOs == null) { return; } fileUtil.saveStorageAttachmentByRecordTypeAndRecordId( ApplicationTypeEnum.FILE.getType(), RecordTypeEnum.VEHICLE_RETURN_RECORD, recordId, storageBlobDTOs ); } private String buildApprovalTitle(VehicleBorrowRecord record, Long businessType) { if (TypeEnums.VEHICLE_BORROW_APPROVAL.getCode().equals(businessType)) { return "车è¾ååºç³è¯·ï¼" + record.getBorrowNo(); } if (TypeEnums.VEHICLE_DELAY_APPROVAL.getCode().equals(businessType)) { return "车è¾å»¶æç³è¯·ï¼" + record.getBorrowNo(); } return "车è¾å®¡æ¹ï¼" + record.getBorrowNo(); } private String normalizeBorrowStatus(String borrowStatus) { if (!StringUtils.hasText(borrowStatus)) { return BORROW_STATUS_DRAFT; } String normalized = borrowStatus.trim().toUpperCase(Locale.ROOT); if (BORROW_STATUS_DRAFT.equals(normalized) || BORROW_STATUS_IN_APPROVAL.equals(normalized) || BORROW_STATUS_BORROWING.equals(normalized) || BORROW_STATUS_RETURNED.equals(normalized) || BORROW_STATUS_REJECTED.equals(normalized)) { return normalized; } return null; } } src/main/java/com/ruoyi/approve/service/impl/VehicleServiceImpl.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,74 @@ package com.ruoyi.approve.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.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.ruoyi.approve.bean.dto.VehicleDto; import com.ruoyi.approve.bean.vo.VehicleVo; import com.ruoyi.approve.mapper.VehicleMapper; import com.ruoyi.approve.pojo.Vehicle; import com.ruoyi.approve.service.VehicleService; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.util.List; /** * 车è¾ç®¡ç Service å®ç° */ @Service @RequiredArgsConstructor public class VehicleServiceImpl extends ServiceImpl<VehicleMapper, Vehicle> implements VehicleService { private final VehicleMapper vehicleMapper; @Override public IPage<VehicleVo> listPage(Page<VehicleVo> page, VehicleDto vehicle) { return vehicleMapper.listPage(page, vehicle); } @Override @Transactional(rollbackFor = Exception.class) public Boolean add(VehicleDto vehicle) { return this.save(vehicle); } @Override public Vehicle detail(Long id) { if (id == null) { return null; } return this.getOne( new LambdaQueryWrapper<Vehicle>() .eq(Vehicle::getId, id) .eq(Vehicle::getDeleted, 0) .last("LIMIT 1") ); } @Override @Transactional(rollbackFor = Exception.class) public Boolean update(VehicleDto vehicle) { if (vehicle == null || vehicle.getId() == null) { return false; } return this.updateById(vehicle); } @Override @Transactional(rollbackFor = Exception.class) public Boolean delete(List<Long> ids) { if (ids == null || ids.isEmpty()) { return false; } return this.update( Wrappers.<Vehicle>lambdaUpdate() .in(Vehicle::getId, ids) .eq(Vehicle::getDeleted, 0) .set(Vehicle::getDeleted, 1) ); } } src/main/java/com/ruoyi/basic/enums/RecordTypeEnum.java
@@ -201,7 +201,11 @@ ENTERPRISE_NEWS("enterprise_news"), APPROVAL_INSTANCE("approval_instance"), ACCOUNT_INVOICE_APPLICATION("account_invoice_application"), ACCOUNT_PURCHASE_INVOICE("account_purchase_invoice"); ACCOUNT_PURCHASE_INVOICE("account_purchase_invoice"), // Vehicle VEHICLE("vehicle"), VEHICLE_BORROW_RECORD("vehicle_borrow_record"), VEHICLE_RETURN_RECORD("vehicle_return_record"); private final String type; RecordTypeEnum(String type) { this.type = type; } src/main/java/com/ruoyi/common/enums/TypeEnums.java
@@ -7,8 +7,8 @@ @AllArgsConstructor public enum TypeEnums implements BaseEnum<Long> { PUBLIC_OUT(1L, "å ¬åºç®¡ç"), LEAVE(2L, "请å管ç"), // PUBLIC_OUT(1L, "å ¬åºç®¡ç"), // LEAVE(2L, "请å管ç"), BUSINESS_TRIP(3L, "åºå·®ç®¡ç"), REIMBURSEMENT(4L, "æ¥é管ç"), PURCHASE_APPROVAL(5L, "éè´å®¡æ¹"), @@ -25,7 +25,8 @@ TRAVEL_REIMBURSEMENT_APPROVAL(16L, "åºå·®æ¥é审æ¹"), EXPENSE_APPROVAL(17L, "è´¹ç¨å®¡æ¹"), ENTERPRISE_NEWS_APPROVAL(18L, "ä¼ä¸æ°é»å®¡æ¹"), VEHICLE_APPROVAL(19L, "车è¾å®¡æ¹"); VEHICLE_BORROW_APPROVAL(19L, "车è¾ååºå®¡æ¹"), VEHICLE_DELAY_APPROVAL(20L, "车è¾å»¶æå®¡æ¹"); src/main/resources/mapper/approve/ApprovalInstanceMapper.xml
@@ -22,6 +22,7 @@ <result column="update_user" property="updateUser" /> <result column="update_time" property="updateTime" /> <result column="deleted" property="deleted" /> <result column="form_config" property="formConfig" /> </resultMap> <select id="listPage" resultType="com.ruoyi.approve.bean.vo.ApprovalInstanceVo"> select ai.*,su.nick_name as create_user_name from src/main/resources/mapper/approve/VehicleBorrowRecordMapper.xml
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,32 @@ <?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.ruoyi.approve.mapper.VehicleBorrowRecordMapper"> <select id="listPage" resultType="com.ruoyi.approve.bean.vo.VehicleBorrowRecordVo"> SELECT * FROM vehicle_borrow_record <where> deleted = 0 <if test="record.borrowNo != null and record.borrowNo != ''"> AND borrow_no LIKE CONCAT('%', #{record.borrowNo}, '%') </if> <if test="record.vehiclePlateNumber != null and record.vehiclePlateNumber != ''"> AND vehicle_plate_number LIKE CONCAT('%', #{record.vehiclePlateNumber}, '%') </if> <if test="record.applicantName != null and record.applicantName != ''"> AND applicant_name LIKE CONCAT('%', #{record.applicantName}, '%') </if> <if test="record.borrowStatus != null and record.borrowStatus != ''"> AND borrow_status = #{record.borrowStatus} </if> <if test="record.extendStatus != null and record.extendStatus != ''"> AND extend_status = #{record.extendStatus} </if> <if test="record.vehicleId != null"> AND vehicle_id = #{record.vehicleId} </if> </where> ORDER BY create_time DESC </select> </mapper> src/main/resources/mapper/approve/VehicleMapper.xml
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,19 @@ <?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.ruoyi.approve.mapper.VehicleMapper"> <select id="listPage" resultType="com.ruoyi.approve.bean.vo.VehicleVo"> SELECT * FROM vehicle <where> deleted = 0 <if test="vehicle.plateNumber != null and vehicle.plateNumber != ''"> AND plate_number LIKE CONCAT('%', #{vehicle.plateNumber}, '%') </if> <if test="vehicle.status != null and vehicle.status != ''"> AND status = #{vehicle.status} </if> </where> ORDER BY create_time DESC </select> </mapper>