Compare commits

...

71 Commits

Author SHA1 Message Date
b88a92b7e1 对ue开发接口 2025-10-31 20:08:44 +08:00
lg
fb72063369 分包合同修改 2025-10-31 19:13:16 +08:00
zt
9809213a12 bug 2025-10-31 15:43:31 +08:00
lcj
87e1cb7473 摄像头拍摄逻辑,车辆管理 2025-10-30 20:03:43 +08:00
lcj
c456ae215f 同步材料数据 2025-10-30 18:45:21 +08:00
lcj
091d7bfc0e 同步材料数据 2025-10-30 18:02:08 +08:00
lg
c9f80fe498 bug修改 2025-10-30 16:39:33 +08:00
7a3deba52a 10-30-修改 2025-10-30 16:13:00 +08:00
eb015fcecc 10-30-修改 2025-10-30 15:28:07 +08:00
d9fab2a4a2 10-30-新增销方名称搜索条件 2025-10-30 10:50:56 +08:00
lcj
7f125b4548 车辆管理 2025-10-30 09:59:19 +08:00
88dcecc88c Merge remote-tracking branch 'origin/dev' into dev 2025-10-30 09:38:23 +08:00
5458329252 10-30-修改 2025-10-30 09:38:13 +08:00
lg
c4e275f5a8 征税项目修改 2025-10-30 09:34:15 +08:00
zt
f8f89fd96e 人脸 2025-10-29 19:45:35 +08:00
lg
b8ffa41a30 项目风险评估签审意见修改 2025-10-29 19:44:05 +08:00
lg
f99cd08d57 项目风险评估签审意见修改 2025-10-29 19:42:24 +08:00
lcj
45cae080a0 车辆管理,修改物资出入库逻辑 2025-10-29 19:35:33 +08:00
84a8f49e95 10-29-LocalDate修改 2025-10-29 19:32:36 +08:00
e725991ece Merge remote-tracking branch 'origin/dev' into dev 2025-10-29 19:17:30 +08:00
c2a06a729c 10-29-LocalDate修改 2025-10-29 19:17:09 +08:00
lg
4a5e50a7a2 投标保证金收回修改 2025-10-29 19:15:15 +08:00
9b30a7bcec 10-29-LocalDate修改 2025-10-29 18:51:06 +08:00
ec383e44db 10-29-LocalDate修改 2025-10-29 18:42:14 +08:00
d3909f131f 10-29-LocalDate修改 2025-10-29 18:41:00 +08:00
zt
a444d4c953 Merge remote-tracking branch 'origin/dev' into dev 2025-10-29 16:31:21 +08:00
zt
3c01ec861b 人脸报错 2025-10-29 16:31:04 +08:00
365cf00644 10-29-bug修复 2025-10-29 16:12:16 +08:00
4faff3b0bc Merge remote-tracking branch 'origin/dev' into dev 2025-10-29 15:57:06 +08:00
866f2336c3 10-29-bug修复 2025-10-29 15:56:56 +08:00
e4132ea540 处理长顺的老数据 2025-10-29 11:33:13 +08:00
zt
53208c36cf 考勤机 2025-10-29 11:21:47 +08:00
8f2a3e6e50 10-29-bug修复,安装包新增描述 2025-10-29 09:53:45 +08:00
lcj
b67a7d5370 考勤接口 2025-10-28 20:39:14 +08:00
zt
07509c8e15 考勤机 2025-10-28 20:23:23 +08:00
e6c58a64ac 10-28-bug修复 2025-10-28 20:02:57 +08:00
lg
abb6b8c13a 分包合同竣工结算添加合同文本字段 2025-10-28 19:05:36 +08:00
lg
de492728bc cbs权限 2025-10-28 18:42:24 +08:00
82fa732db6 10-28-bug修复 2025-10-28 15:52:07 +08:00
lcj
275d640263 车辆管理 2025-10-28 14:17:45 +08:00
7e47b8a74f 施工产值增加第N周 2025-10-28 11:57:14 +08:00
d4301da0ec 添加详情权限 2025-10-28 11:02:53 +08:00
lg
fe5b5473dd 审核数据添加 2025-10-28 09:44:33 +08:00
0126d44761 ws注解放开 2025-10-27 20:42:03 +08:00
dbefd88e41 添加客户详情权限 2025-10-27 20:09:39 +08:00
9a3b7ebe54 添加审核 2025-10-27 20:00:56 +08:00
81be4a862c 10-27-添加审核完善 2025-10-27 19:57:35 +08:00
lg
43f2db9f7e 审核数据添加 2025-10-27 19:51:55 +08:00
abefa90408 10-27-添加审核完善 2025-10-27 19:14:24 +08:00
08f48b7817 10-27-添加审核 2025-10-27 18:45:49 +08:00
lcj
6b8ace60d4 车辆管理,修改材料 2025-10-27 17:10:41 +08:00
lg
b997dd5f00 详情权限取消 2025-10-27 15:35:49 +08:00
6174743858 Merge remote-tracking branch 'origin/dev' into dev 2025-10-27 15:25:19 +08:00
82d55d7188 10-27-修改bug 2025-10-27 15:25:06 +08:00
lg
aec5eacd0b 采购详情权限取消 2025-10-27 15:25:04 +08:00
zt
a320b85965 考勤机 2025-10-27 15:14:29 +08:00
lg
b61a7c153d 投标文件-项目类型中文返回 2025-10-27 14:44:50 +08:00
fb9b01cf34 修改物资跟踪管理台账查询列表修改 2025-10-27 14:27:11 +08:00
lg
32f134873a 采购合同金额字段 2025-10-27 11:37:41 +08:00
f4220be9d6 10-27-修改bug 2025-10-27 10:29:27 +08:00
lg
8252fd7216 标后分析返回对象增加字段 2025-10-27 09:44:11 +08:00
lg
57855f4307 bug修改 2025-10-25 22:18:10 +08:00
6784eafe6e 供应商-客户中间表引用表的新增修改删除修改 2025-10-25 22:17:26 +08:00
0b42c1d6a6 Merge remote-tracking branch 'origin/dev' into dev 2025-10-25 22:16:07 +08:00
4b37a7327f 10-25-修改 2025-10-25 22:16:00 +08:00
lg
0287f1e4ce bug修改 2025-10-25 21:58:47 +08:00
5d8af1cab8 10-25-供应商删除修改 2025-10-25 21:57:13 +08:00
123896f08b Merge remote-tracking branch 'origin/dev' into dev 2025-10-25 21:25:40 +08:00
a8a198b51f 10-25-增加校验 2025-10-25 21:25:33 +08:00
lg
f00b98714a Merge remote-tracking branch 'origin/dev' into dev 2025-10-25 21:24:39 +08:00
lg
4ff87f3996 bug修改 2025-10-25 21:23:58 +08:00
332 changed files with 10383 additions and 1363 deletions

View File

@ -66,27 +66,27 @@ spring:
# username: xinnengyuan # username: xinnengyuan
# password: mEZPC5Sdf3r2HENi # password: mEZPC5Sdf3r2HENi
# 从库数据源 # 从库数据源
slave: # slave:
lazy: true # lazy: true
type: ${spring.datasource.type} # type: ${spring.datasource.type}
driverClassName: com.mysql.cj.jdbc.Driver # driverClassName: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://192.168.110.2:13386/zmkgc?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true # url: jdbc:mysql://192.168.110.2:13386/zmkgc?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true
username: zmkgc # username: zmkgc
password: nWKDKRNRT48tFBdh # password: nWKDKRNRT48tFBdh
slave1: # slave1:
lazy: true # lazy: true
type: ${spring.datasource.type} # type: ${spring.datasource.type}
driverClassName: com.mysql.cj.jdbc.Driver # driverClassName: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://192.168.110.2:13386/zmkgprod?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true # url: jdbc:mysql://192.168.110.2:13386/zmkgprod?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true
username: zmkgprod # username: zmkgprod
password: MaY8nehwWkJriWPm # password: MaY8nehwWkJriWPm
slave2: # slave2:
lazy: true # lazy: true
type: ${spring.datasource.type} # type: ${spring.datasource.type}
driverClassName: com.mysql.cj.jdbc.Driver # driverClassName: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://192.168.110.2:13386/zmkgc?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true # url: jdbc:mysql://192.168.110.2:13386/zmkgc?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true
username: zmkgc # username: zmkgc
password: nWKDKRNRT48tFBdh # password: nWKDKRNRT48tFBdh
# slave: # slave:
# lazy: true # lazy: true
# type: ${spring.datasource.type} # type: ${spring.datasource.type}

View File

@ -134,6 +134,7 @@ security:
# todo 仅测试 # todo 仅测试
- /facility/matrix/** - /facility/matrix/**
- /hat/device/data - /hat/device/data
- /websocket/ue
# 多租户配置 # 多租户配置
tenant: tenant:
@ -281,14 +282,16 @@ springdoc:
packages-to-scan: org.dromara.gps packages-to-scan: org.dromara.gps
- group: 24.招标模块 - group: 24.招标模块
packages-to-scan: org.dromara.tender packages-to-scan: org.dromara.tender
# - group: 25.app版本模块 - group: 29.app版本模块
# packages-to-scan: org.dromara.app packages-to-scan: org.dromara.app
- group: 25.数据迁移模块 - group: 25.数据迁移模块
packages-to-scan: org.dromara.transferData packages-to-scan: org.dromara.transferData
- group: 26.netty消息模块 - group: 26.netty消息模块
packages-to-scan: org.dromara.websocket packages-to-scan: org.dromara.websocket
- group: 27.新中大模块 - group: 27.新中大模块
packages-to-scan: org.dromara.xzd packages-to-scan: org.dromara.xzd
- group: 28.车辆模块
packages-to-scan: org.dromara.vehicle
# knife4j的增强配置不需要增强可以不配 # knife4j的增强配置不需要增强可以不配
knife4j: knife4j:
enable: true enable: true
@ -306,12 +309,13 @@ xss:
- /project/project - /project/project
- /xzd/contractDetails/** - /xzd/contractDetails/**
- /xzd/contractChange/** - /xzd/contractChange/**
- /comprehensive/csContractChange/** - /xzd/comprehensive/csContractChange/**
- /comprehensive/csContractInformation/** - /xzd/comprehensive/csContractInformation/**
- /hetongbiangeng/** - /xzd/hetongbiangeng/**
- /fenbaohetongbiangg/** - /xzd/fenbaohetongjungong/**
- /fenbaohetongxinxi/** - /xzd/fenbaohetongbiangg/**
- /contractManagement/** - /xzd/fenbaohetongxinxi/**
- /xzd/contractManagement/**
# 全局线程池相关配置 # 全局线程池相关配置
# 如使用JDK21请直接使用虚拟线程 不要开启此配置 # 如使用JDK21请直接使用虚拟线程 不要开启此配置

View File

@ -0,0 +1,99 @@
package org.dromara.test;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.dromara.materials.domain.MatMaterialIssue;
import org.dromara.materials.domain.MatMaterialReceive;
import org.dromara.materials.domain.MatMaterials;
import org.dromara.materials.domain.MatMaterialsInventory;
import org.dromara.materials.domain.enums.MatMaterialsInventoryOutPutEnum;
import org.dromara.materials.service.IMatMaterialIssueService;
import org.dromara.materials.service.IMatMaterialReceiveService;
import org.dromara.materials.service.IMatMaterialsInventoryService;
import org.dromara.materials.service.IMatMaterialsService;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
/**
* @author lilemy
* @date 2025-10-30 15:58
*/
@Slf4j
@SpringBootTest
public class MaterialsTest {
/**
* 长顺项目id
*/
private static final Long PROJECT_ID = 1897161054676336641L;
@Resource
private IMatMaterialsService materialsService;
@Resource
private IMatMaterialsInventoryService materialsInventoryService;
@Resource
private IMatMaterialReceiveService materialReceiveService;
@Resource
private IMatMaterialIssueService materialIssueService;
@Test
void test() {
List<MatMaterials> materials = materialsService.lambdaQuery()
.eq(MatMaterials::getProjectId, PROJECT_ID)
.list();
Set<Long> materialIds = materials.stream().map(MatMaterials::getId).collect(Collectors.toSet());
List<MatMaterialsInventory> inventoryList = materialsInventoryService.lambdaQuery()
.in(MatMaterialsInventory::getMaterialsId, materialIds)
.eq(MatMaterialsInventory::getOutPut, MatMaterialsInventoryOutPutEnum.OUT.getValue())
.list();
Map<String, List<MatMaterials>> map = materials.stream()
.collect(Collectors.groupingBy(MatMaterials::getFormCode));
for (MatMaterials material : materials) {
String formCode = material.getFormCode();
// 查看入库数据
MatMaterialReceive receive = materialReceiveService.lambdaQuery()
.eq(MatMaterialReceive::getFormCode, formCode)
.one();
// 查看出库数据
/* List<MatMaterialsInventory> inventoryList = materialsInventoryService.lambdaQuery()
.eq(MatMaterialsInventory::getMaterialsId, material.getId())
.eq(MatMaterialsInventory::getOutPut, MatMaterialsInventoryOutPutEnum.OUT.getValue())
.list();
if (CollUtil.isEmpty(inventoryList)) {
continue;
}*/
// 创建领料出库数据
List<MatMaterialIssue> issueList = inventoryList.stream().map(inventory -> {
MatMaterialIssue issue = new MatMaterialIssue();
issue.setProjectId(PROJECT_ID);
issue.setMaterialSource("2");
issue.setFormCode(receive.getFormCode());
issue.setProjectName(receive.getProjectName());
issue.setMaterialName(receive.getMaterialName());
issue.setOrderingUnit(receive.getOrderingUnit());
issue.setSupplierUnit(receive.getSupplierUnit());
issue.setIssueUnit(inventory.getRecipient());
issue.setIssueUnitId(inventory.getRecipientId());
issue.setShipper(inventory.getShipper());
// issue.setStorageUnit();
issue.setCertCount(0);
issue.setReportCount(0);
issue.setTechDocCount(0);
issue.setLicenseCount(0);
log.info("领料出库数据:{}", issue);
log.info("=============================");
return issue;
}).toList();
}
}
}

View File

@ -3,8 +3,12 @@ package org.dromara.app.controller;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.dromara.app.domain.SysPackage; import org.dromara.app.domain.SysPackage;
import org.dromara.app.domain.bo.SysPackageBo;
import org.dromara.app.domain.vo.SysPackageVo;
import org.dromara.app.service.SysPackageServiceImpl; import org.dromara.app.service.SysPackageServiceImpl;
import org.dromara.common.core.domain.R; import org.dromara.common.core.domain.R;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.system.domain.vo.SysOssVo; import org.dromara.system.domain.vo.SysOssVo;
import org.dromara.system.service.impl.SysOssServiceImpl; import org.dromara.system.service.impl.SysOssServiceImpl;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@ -26,6 +30,14 @@ public class SysPackageController {
@Autowired @Autowired
private SysPackageServiceImpl sysPackageService; private SysPackageServiceImpl sysPackageService;
/**
* 获取列表
*/
@GetMapping("/list")
public TableDataInfo<SysPackageVo> list(SysPackageBo bo, PageQuery pageQuery) {
return sysPackageService.queryPageList(bo, pageQuery);
}
/** /**
* 获取最新版本 * 获取最新版本
*/ */
@ -42,7 +54,7 @@ public class SysPackageController {
* 上传最新安装包及版本 * 上传最新安装包及版本
*/ */
@GetMapping("/uploadNewVersion") @GetMapping("/uploadNewVersion")
public R<SysPackage> uploadNewVersion(String version, String type, MultipartFile file) { public R<SysPackage> uploadNewVersion(String version, String type, MultipartFile file, String context) {
String originalFileName = "apk/package/app-release.apk"; String originalFileName = "apk/package/app-release.apk";
// 先查询最新记录 // 先查询最新记录
@ -57,11 +69,11 @@ public class SysPackageController {
} }
// 分离事务:再处理数据库操作 // 分离事务:再处理数据库操作
return handleDatabaseOperations(version, type, upload, list); return handleDatabaseOperations(version, type, upload, list, context);
} }
@Transactional @Transactional
public R<SysPackage> handleDatabaseOperations(String version, String type, SysOssVo upload, List<SysPackage> existingPackages) { public R<SysPackage> handleDatabaseOperations(String version, String type, SysOssVo upload, List<SysPackage> existingPackages, String context) {
try { try {
// 先删除旧文件记录 // 先删除旧文件记录
if (existingPackages != null && !existingPackages.isEmpty()) { if (existingPackages != null && !existingPackages.isEmpty()) {
@ -78,6 +90,7 @@ public class SysPackageController {
sysPackage.setFileId(upload.getOssId()); sysPackage.setFileId(upload.getOssId());
sysPackage.setFileUrl(upload.getUrl()); sysPackage.setFileUrl(upload.getUrl());
sysPackage.setType(type); sysPackage.setType(type);
sysPackage.setContext(context);
boolean save = sysPackageService.save(sysPackage); boolean save = sysPackageService.save(sysPackage);
if (!save) { if (!save) {

View File

@ -30,4 +30,9 @@ public class SysPackage extends BaseEntity {
private String fileUrl; private String fileUrl;
/**
* 更新内容
*/
private String context;
} }

View File

@ -0,0 +1,32 @@
package org.dromara.app.domain.bo;
import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.dromara.common.mybatis.core.domain.BaseEntity;
import org.dromara.xzd.domain.XzdContractDetails;
@Data
@EqualsAndHashCode(callSuper = true)
@AutoMapper(target = XzdContractDetails.class, reverseConvertGenerate = false)
public class SysPackageBo extends BaseEntity {
private Long id;
/**
* 0安卓1苹果2鸿蒙
*/
private String type;
private String version;
private Long fileId;
private String fileUrl;
/**
* 更新内容
*/
private String context;
}

View File

@ -27,4 +27,9 @@ public class SysPackageVo {
@ExcelProperty("安装包地址") @ExcelProperty("安装包地址")
private String fileUrl; private String fileUrl;
/**
* 更新内容
*/
private String context;
} }

View File

@ -1,10 +1,24 @@
package org.dromara.app.service; package org.dromara.app.service;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.dromara.app.domain.SysPackage; import org.dromara.app.domain.SysPackage;
import org.dromara.app.domain.bo.SysPackageBo;
import org.dromara.app.domain.vo.SysPackageVo;
import org.dromara.app.mapper.SysPackageMapper; import org.dromara.app.mapper.SysPackageMapper;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@Service @Service
public class SysPackageServiceImpl extends ServiceImpl<SysPackageMapper, SysPackage> { public class SysPackageServiceImpl extends ServiceImpl<SysPackageMapper, SysPackage> {
public TableDataInfo<SysPackageVo> queryPageList(SysPackageBo bo, PageQuery pageQuery) {
// LambdaQueryWrapper<SysPackage> lqw = buildQueryWrapper(bo);
Page<SysPackageVo> result = baseMapper.selectVoPage(pageQuery.build(), new LambdaQueryWrapper<>());
// setValue(result.getRecords());
return TableDataInfo.build(result);
}
} }

View File

@ -0,0 +1,43 @@
package org.dromara.bigscreen.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;
/**
* 无人机消息redis订阅专用线程池
*/
@Configuration // 标记为配置类让Spring扫描加载
@EnableAsync // 启用Spring异步功能若项目已在启动类加过此处可省略但加上更稳妥
public class AsyncConfig {
/**
* 定义名为 "messageAsyncExecutor" 的线程池 Bean与 @Async("messageAsyncExecutor") 对应)
*/
@Bean(name = "messageAsyncExecutor") // Bean名称必须是 "messageAsyncExecutor",大小写敏感
public Executor messageAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
// 1. 核心线程数根据CPU核心数或业务量调整示例5个
executor.setCorePoolSize(5);
// 2. 最大线程数避免线程过多导致资源耗尽示例10个
executor.setMaxPoolSize(10);
// 3. 任务队列容量缓冲待处理的异步任务示例50个
executor.setQueueCapacity(50);
// 4. 线程名前缀便于日志排查格式message-async-1, message-async-2...
executor.setThreadNamePrefix("message-async-");
// 5. 线程空闲时间超过60秒空闲则销毁释放资源
executor.setKeepAliveSeconds(60);
// 6. 任务拒绝策略:队列满时,由提交任务的线程临时执行(避免消息丢失)
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
// 7. 初始化线程池(必须调用,否则线程池不会生效)
executor.initialize();
return executor;
}
}

View File

@ -0,0 +1,20 @@
package org.dromara.bigscreen.config;// 路径com.ruoyi.framework.config.WebSocketConfig
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
/**
* WebSocket 配置类:确保 @ServerEndpoint 端点随项目启动注册
*/
@Configuration
public class WebSocketConfig {
/**
* 注册 WebSocket 端点处理器
* 作用Spring 启动时自动扫描并初始化 @ServerEndpoint 注解的类
*/
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
}

View File

@ -5,7 +5,11 @@ import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil; import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.dromara.bigscreen.service.IAsyncMessageHandlerService;
import org.dromara.bigscreen.service.impl.InitOnStartWebSocketServer;
import org.dromara.common.websocket.dto.WebSocketMessageDto; import org.dromara.common.websocket.dto.WebSocketMessageDto;
import org.dromara.common.websocket.holder.WebSocketSessionHolder;
import org.dromara.common.websocket.utils.WebSocketUtils; import org.dromara.common.websocket.utils.WebSocketUtils;
import org.dromara.drone.domain.DroProjectDrone; import org.dromara.drone.domain.DroProjectDrone;
import org.dromara.drone.service.IDroProjectDroneService; import org.dromara.drone.service.IDroProjectDroneService;
@ -17,25 +21,18 @@ import org.springframework.stereotype.Component;
import java.util.Collections; import java.util.Collections;
import java.util.Objects; import java.util.Objects;
import java.util.Set;
/** /**
* Redis消息监听器用于处理订阅频道收到的消息 * Redis消息监听器用于处理订阅频道收到的消息
*/ */
@Slf4j
@Component @Component
public class RedisMessageListener implements MessageListener { public class RedisMessageListener implements MessageListener {
@Lazy // 注入异步消息处理服务
private final StringRedisTemplate stringRedisTemplate;
@Resource @Resource
@Lazy private IAsyncMessageHandlerService asyncMessageHandlerService;
private IDroProjectDroneService droProjectDroneService;
// 构造函数注入StringRedisTemplate
public RedisMessageListener(StringRedisTemplate stringRedisTemplate) {
this.stringRedisTemplate = stringRedisTemplate;
}
/** /**
* 处理接收到的消息 * 处理接收到的消息
* @param message 消息对象 * @param message 消息对象
@ -43,50 +40,19 @@ public class RedisMessageListener implements MessageListener {
*/ */
@Override @Override
public void onMessage(Message message, byte[] pattern) { public void onMessage(Message message, byte[] pattern) {
// 处理消息 try {
// System.out.println("返回:"+stringRedisTemplate.getStringSerializer().deserialize(message.getBody())); // 1. 快速日志记录(证明监听到消息)
String gateway = JSONUtil.parseObj(stringRedisTemplate.getStringSerializer().deserialize(message.getBody())).getStr("gateway"); log.info("【Redis消息监听】收到消息长度{}字节,提交异步处理", message.getBody().length);
String key = "";
if (JSONUtil.parseObj(stringRedisTemplate.getStringSerializer().deserialize(message.getBody())).getJSONObject("data").get("job_number") != null) {
key = "wrj:osd1:"+gateway;
}else if (JSONUtil.parseObj(stringRedisTemplate.getStringSerializer().deserialize(message.getBody())).getJSONObject("data").get("wireless_link") != null) {
key = "wrj:osd2:"+gateway;
}else if (JSONUtil.parseObj(stringRedisTemplate.getStringSerializer().deserialize(message.getBody())).getJSONObject("data").get("network_state") != null) {
key = "wrj:osd3:"+gateway;
DroProjectDrone droProjectDrone = droProjectDroneService.getBaseMapper().selectOne(new LambdaQueryWrapper<DroProjectDrone>().eq(DroProjectDrone::getDroneSn, gateway));
setWs(message, gateway, droProjectDrone);
}else{
key = "wrj:osd4:"+gateway;
DroProjectDrone droProjectDrone = droProjectDroneService.getBaseMapper().selectOne(new LambdaQueryWrapper<DroProjectDrone>().eq(DroProjectDrone::getDroneSn, gateway));
setWs(message, gateway, droProjectDrone);
// 2. 提交给异步服务处理(核心:线程分离,监听线程立即返回)
asyncMessageHandlerService.handleRedisMessageAsync(message);
} catch (Exception e) {
// 捕获提交过程中的异常(如异步服务注入失败)
log.error("【Redis消息监听】提交异步处理失败", e);
} }
stringRedisTemplate
.opsForValue()
.set(key
, Objects.requireNonNull(stringRedisTemplate.getStringSerializer().deserialize(message.getBody())));
} }
private void setWs(Message message, String gateway, DroProjectDrone droProjectDrone) {
String pushContent = buildPushMessage(gateway,stringRedisTemplate.getStringSerializer().deserialize(message.getBody()), droProjectDrone.getProjectId());
// 发送给指定用户equipment.getUserId()
WebSocketMessageDto messageDto = new WebSocketMessageDto();
messageDto.setMessage(pushContent);
messageDto.setSessionKeys(Collections.singletonList(droProjectDrone.getProjectId()));
WebSocketUtils.publishMessage(messageDto);
}
private String buildPushMessage(String key, String message, Long projectId) {
JSONObject messageObj = new JSONObject();
messageObj.put("type", "wrj_DATA_UPDATE");
messageObj.put("projectId",projectId.toString());
messageObj.put("clientId",key);
// 位置信息
JSONObject locationObj = new JSONObject();
locationObj.put("latitude", JSONUtil.parseObj(message).getJSONObject("data").get("latitude").toString()); // 纬度
locationObj.put("longitude", JSONUtil.parseObj(message).getJSONObject("data").get("longitude").toString()); // 经度
messageObj.put("location", locationObj);
return messageObj.toString();
}
} }

View File

@ -1,9 +1,13 @@
package org.dromara.bigscreen.manager; package org.dromara.bigscreen.manager;
import com.aizuda.snailjob.client.job.core.annotation.JobExecutor;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import jakarta.annotation.PostConstruct; import jakarta.annotation.PostConstruct;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.dromara.bigscreen.service.impl.InitOnStartWebSocketServer;
import org.dromara.common.redis.utils.RedisUtils; import org.dromara.common.redis.utils.RedisUtils;
import org.dromara.drone.domain.DroProjectDrone;
import org.dromara.drone.service.IDroProjectDroneService; import org.dromara.drone.service.IDroProjectDroneService;
import org.springframework.context.annotation.Lazy; import org.springframework.context.annotation.Lazy;
import org.springframework.data.redis.listener.PatternTopic; import org.springframework.data.redis.listener.PatternTopic;
@ -49,7 +53,7 @@ public class RedisSubscribeManager {
public void initSubscribe() { public void initSubscribe() {
log.info("项目启动初始化Redis订阅..."); log.info("项目启动初始化Redis订阅...");
// 步骤1从数据库获取最新的主题列表原逻辑getTopicsByKeyPrefix // 步骤1从数据库获取最新的主题列表原逻辑getTopicsByKeyPrefix
List<String> latestKeys = droProjectDroneService.getTopicsByKeyPrefix(); List<DroProjectDrone> latestKeys = droProjectDroneService.getBaseMapper().selectList(new LambdaQueryWrapper<DroProjectDrone>().groupBy(DroProjectDrone::getDroneSn));
if (latestKeys == null || latestKeys.isEmpty()) { if (latestKeys == null || latestKeys.isEmpty()) {
log.warn("未获取到任何主题,将取消所有现有订阅"); log.warn("未获取到任何主题,将取消所有现有订阅");
cancelAllSubscribes(); cancelAllSubscribes();
@ -58,11 +62,13 @@ public class RedisSubscribeManager {
// 步骤2构建最新的完整主题格式wrj:key // 步骤2构建最新的完整主题格式wrj:key
Set<String> latestFullTopics = new HashSet<>(); Set<String> latestFullTopics = new HashSet<>();
for (String key : latestKeys) { for (DroProjectDrone key : latestKeys) {
latestFullTopics.add("wrj:osd1:" + key); latestFullTopics.add("wrj:osd1:" + key.getDroneSn());
latestFullTopics.add("wrj:osd2:" + key); latestFullTopics.add("wrj:osd2:" + key.getDroneSn());
latestFullTopics.add("wrj:osd3:" + key); latestFullTopics.add("wrj:osd3:" + key.getDroneSn());
latestFullTopics.add("wrj:osd4:" + key); if (key.getAirplaneSn() != null && !key.getAirplaneSn().isEmpty()) {
latestFullTopics.add("wrj:osd4:" + key.getAirplaneSn());
}
} }
@ -77,7 +83,7 @@ public class RedisSubscribeManager {
* 4. 定时任务定期更新订阅每5分钟执行一次可调整cron表达式 * 4. 定时任务定期更新订阅每5分钟执行一次可调整cron表达式
* cron格式秒 分 时 日 月 周 年示例0 0/5 * * * ? 表示每5分钟 * cron格式秒 分 时 日 月 周 年示例0 0/5 * * * ? 表示每5分钟
*/ */
@Scheduled(cron = "0 0/6 * * * ?") // @Scheduled(cron = "0 0/6 * * * ?")
public void dynamicUpdateSubscribe() { public void dynamicUpdateSubscribe() {
try { try {
Object object = RedisUtils.getCacheObject("xmjdap:ws"); Object object = RedisUtils.getCacheObject("xmjdap:ws");
@ -94,7 +100,7 @@ public class RedisSubscribeManager {
return; return;
} }
// 步骤1从数据库获取最新的主题列表原逻辑getTopicsByKeyPrefix // 步骤1从数据库获取最新的主题列表原逻辑getTopicsByKeyPrefix
List<String> latestKeys = droProjectDroneService.getTopicsByKeyPrefix(); List<DroProjectDrone> latestKeys = droProjectDroneService.getBaseMapper().selectList(new LambdaQueryWrapper<DroProjectDrone>().groupBy(DroProjectDrone::getDroneSn));
if (latestKeys == null || latestKeys.isEmpty()) { if (latestKeys == null || latestKeys.isEmpty()) {
log.warn("定时任务未获取到任何主题,将取消所有现有订阅"); log.warn("定时任务未获取到任何主题,将取消所有现有订阅");
cancelAllSubscribes(); cancelAllSubscribes();
@ -103,11 +109,67 @@ public class RedisSubscribeManager {
// 步骤2构建最新的完整主题格式wrj:key // 步骤2构建最新的完整主题格式wrj:key
Set<String> latestFullTopics = new HashSet<>(); Set<String> latestFullTopics = new HashSet<>();
for (String key : latestKeys) { for (DroProjectDrone key : latestKeys) {
latestFullTopics.add("wrj:osd1:" + key); latestFullTopics.add("wrj:osd1:" + key.getDroneSn());
latestFullTopics.add("wrj:osd2:" + key); latestFullTopics.add("wrj:osd2:" + key.getDroneSn());
latestFullTopics.add("wrj:osd3:" + key); latestFullTopics.add("wrj:osd3:" + key.getDroneSn());
latestFullTopics.add("wrj:osd4:" + key); if (key.getAirplaneSn() != null && !key.getAirplaneSn().isEmpty()) {
latestFullTopics.add("wrj:osd4:" + key.getAirplaneSn());
}
}
// 步骤3对比现有订阅删除过期主题
cancelExpiredSubscribes(latestFullTopics);
// 步骤4对比现有订阅新增未订阅的主题
addNewSubscribes(latestFullTopics);
log.info("Redis订阅更新完成当前已订阅主题数{}", subscribedTopics.size());
} catch (Exception e) {
log.error("Redis订阅更新定时任务执行失败", e);
// 异常时不修改现有订阅,避免误删
}
}
// @Scheduled(cron = "0/10 * * * * ?")
@JobExecutor(name = "ueWsConnect")
public void ueWsConnect() {
try {
int onlineCount = InitOnStartWebSocketServer.getOnlineCount();
Object object = RedisUtils.getCacheObject("xmjdap:ws");
log.info("开始执行Redis订阅更新定时任务...");
if (onlineCount == 0 && object == null) {
cancelAllSubscribes();
return;
}
if (object != null) {
long oldTime = Long.parseLong(String.valueOf(object));
long now = System.currentTimeMillis();
if (now-oldTime > 300000) {
RedisUtils.deleteObject("xmjdap:ws");
cancelAllSubscribes();
return;
}
log.info("Redis时间缓存更新定时任务");
}
// 步骤1从数据库获取最新的主题列表原逻辑getTopicsByKeyPrefix
List<DroProjectDrone> latestKeys = droProjectDroneService.getBaseMapper().selectList(new LambdaQueryWrapper<DroProjectDrone>().groupBy(DroProjectDrone::getDroneSn));
if (latestKeys == null || latestKeys.isEmpty()) {
log.warn("定时任务未获取到任何主题,将取消所有现有订阅");
cancelAllSubscribes();
return;
}
// 步骤2构建最新的完整主题格式wrj:key
Set<String> latestFullTopics = new HashSet<>();
for (DroProjectDrone key : latestKeys) {
latestFullTopics.add("wrj:osd1:" + key.getDroneSn());
latestFullTopics.add("wrj:osd2:" + key.getDroneSn());
latestFullTopics.add("wrj:osd3:" + key.getDroneSn());
if (key.getAirplaneSn() != null && !key.getAirplaneSn().isEmpty()) {
latestFullTopics.add("wrj:osd4:" + key.getAirplaneSn());
}
} }
// 步骤3对比现有订阅删除过期主题 // 步骤3对比现有订阅删除过期主题
@ -126,7 +188,7 @@ public class RedisSubscribeManager {
/** /**
* 取消过期的订阅(现有订阅不在最新列表中的主题) * 取消过期的订阅(现有订阅不在最新列表中的主题)
*/ */
private void cancelExpiredSubscribes(Set<String> latestFullTopics) { public void cancelExpiredSubscribes(Set<String> latestFullTopics) {
// 遍历现有订阅,找出需要删除的主题 // 遍历现有订阅,找出需要删除的主题
Set<String> expiredTopics = subscribedTopics.keySet().stream() Set<String> expiredTopics = subscribedTopics.keySet().stream()
.filter(topic -> !latestFullTopics.contains(topic)) .filter(topic -> !latestFullTopics.contains(topic))
@ -152,7 +214,7 @@ public class RedisSubscribeManager {
/** /**
* 新增未订阅的主题(最新列表中有但现有订阅没有的主题) * 新增未订阅的主题(最新列表中有但现有订阅没有的主题)
*/ */
private void addNewSubscribes(Set<String> latestFullTopics) { public void addNewSubscribes(Set<String> latestFullTopics) {
// 遍历最新主题,找出需要新增的主题 // 遍历最新主题,找出需要新增的主题
Set<String> newTopics = latestFullTopics.stream() Set<String> newTopics = latestFullTopics.stream()
.filter(topic -> !subscribedTopics.containsKey(topic)) .filter(topic -> !subscribedTopics.containsKey(topic))

View File

@ -0,0 +1,14 @@
package org.dromara.bigscreen.service;
import org.springframework.data.redis.connection.Message;
/**
* 异步消息处理服务接口扩展原有接口增加Redis消息处理方法
*/
public interface IAsyncMessageHandlerService {
/**
* 异步处理Redis消息核心接收Message对象处理业务逻辑
* @param message Redis消息对象
*/
void handleRedisMessageAsync(Message message);
}

View File

@ -0,0 +1,180 @@
package org.dromara.bigscreen.service.impl;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.dromara.bigscreen.service.IAsyncMessageHandlerService;
import org.dromara.common.websocket.dto.WebSocketMessageDto;
import org.dromara.common.websocket.holder.WebSocketSessionHolder;
import org.dromara.common.websocket.utils.WebSocketUtils;
import org.dromara.drone.domain.DroProjectDrone;
import org.dromara.drone.mapper.DroProjectDroneMapper;
import org.springframework.data.redis.connection.Message;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import java.util.Collections;
import java.util.Objects;
import java.util.Set;
/**
* 异步消息处理实现核心原onMessage逻辑迁移至此
*/
@Slf4j
@Service
public class AsyncMessageHandlerServiceImpl implements IAsyncMessageHandlerService {
@Resource
private StringRedisTemplate stringRedisTemplate;
@Resource
private DroProjectDroneMapper droProjectDroneMapper; // 直接注入Mapper减少Service层调用开销
/**
* 异步处理消息指定线程池messageAsyncExecutor
*/
@Override
@Async("messageAsyncExecutor")
public void handleRedisMessageAsync(Message message) {
try {
// 1. 解析消息体(只解析一次,避免重复操作)
byte[] messageBody = message.getBody();
if (messageBody == null || messageBody.length == 0) {
log.warn("【异步消息处理】消息体为空,忽略处理");
return;
}
String messageContent = stringRedisTemplate.getStringSerializer().deserialize(messageBody);
if (messageContent == null) {
log.warn("【异步消息处理】消息反序列化失败,内容:{}", new String(messageBody));
return;
}
// 2. 解析JSON使用框架兼容的JSONUtil处理可能的解析异常
JSONObject messageJson;
try {
messageJson = JSONUtil.parseObj(messageContent);
} catch (Exception e) {
log.error("【异步消息处理】JSON解析失败内容{}", messageContent, e);
return;
}
// 3. 获取gateway字段核心业务参数
String gateway = messageJson.getStr("gateway");
if (gateway == null) {
log.warn("【异步消息处理】消息缺少gateway字段内容{}", messageContent);
return;
}
// 4. 解析data字段判断key类型
JSONObject dataJson = messageJson.getJSONObject("data");
if (dataJson == null) {
log.warn("【异步消息处理】消息缺少data字段gateway{}", gateway);
return;
}
String key = "";
DroProjectDrone droProjectDrone = null;
boolean isOsd4 = false;
// 5. 根据data字段内容判断key类型并查询无人机信息
if (dataJson.get("job_number") != null) {
key = "wrj:osd1:" + gateway;
} else if (dataJson.get("wireless_link") != null) {
key = "wrj:osd2:" + gateway;
} else if (dataJson.get("network_state") != null) {
key = "wrj:osd3:" + gateway;
// 查询无人机信息使用LambdaQueryWrapper框架规范
droProjectDrone = droProjectDroneMapper.selectOne(
new LambdaQueryWrapper<DroProjectDrone>()
.eq(DroProjectDrone::getDroneSn, gateway)
);
log.info("【异步消息处理】osd3类型消息gateway{}", gateway);
// 调用setWs方法非osd4类型
setWs(messageContent, gateway, droProjectDrone, false);
} else {
key = "wrj:osd4:" + gateway;
droProjectDrone = droProjectDroneMapper.selectOne(
new LambdaQueryWrapper<DroProjectDrone>()
.eq(DroProjectDrone::getAirplaneSn, gateway)
);
log.info("【异步消息处理】osd4类型消息gateway{}", gateway);
isOsd4 = true;
// 调用setWs方法osd4类型
setWs(messageContent, gateway, droProjectDrone, true);
}
// 6. 存储消息到Redis使用Objects.requireNonNull避免空指针
stringRedisTemplate.opsForValue().set(key, Objects.requireNonNull(messageContent));
log.info("【异步消息处理】Redis存储成功key{}", key);
} catch (Exception e) {
// 捕获所有异常,避免线程终止
log.error("【异步消息处理】整体逻辑异常", e);
}
}
private void setWs(String message, String gateway, DroProjectDrone droProjectDrone, boolean b) {
//判断是否有连接
Set<Long> sessionsAll = WebSocketSessionHolder.getSessionsAll();
if (!sessionsAll.isEmpty()) {
String pushContent = buildPushMessage(gateway, message, droProjectDrone.getProjectId());
// 发送给指定用户equipment.getUserId()
WebSocketMessageDto messageDto = new WebSocketMessageDto();
messageDto.setMessage(pushContent);
messageDto.setSessionKeys(Collections.singletonList(droProjectDrone.getProjectId()));
WebSocketUtils.publishMessage(messageDto);
System.out.println("大屏已推送消息");
}
int onlineCount = InitOnStartWebSocketServer.getOnlineCount();
if (onlineCount > 0){
String modleId = b ? droProjectDrone.getAirplaneModleId() : droProjectDrone.getDroneModleId();
if (modleId != null) {
String ued = ueStructureJsonMessage(message, modleId);
InitOnStartWebSocketServer.sendToAll(ued);
System.out.println("ue已推送消息");
}
}
}
private String buildPushMessage(String key, String message, Long projectId) {
JSONObject messageObj = new JSONObject();
messageObj.put("type", "wrj_DATA_UPDATE");
messageObj.put("projectId",projectId.toString());
messageObj.put("clientId",key);
// 位置信息
JSONObject locationObj = new JSONObject();
locationObj.put("latitude", JSONUtil.parseObj(message).getJSONObject("data").get("latitude").toString()); // 纬度
locationObj.put("longitude", JSONUtil.parseObj(message).getJSONObject("data").get("longitude").toString()); // 经度
messageObj.put("location", locationObj);
return messageObj.toString();
}
/**
* 构建推送消息内容String类型
*/
private String ueStructureJsonMessage(String message, String modelId) {
// 构造消息对象(包含关键信息)
JSONObject messageObj = new JSONObject();
messageObj.put("type", "location"); // 消息类型
JSONObject data = new JSONObject();
data.put("id", modelId);
// 位置信息
JSONObject position = new JSONObject();
position.put("lat", JSONUtil.parseObj(message).getJSONObject("data").get("latitude").toString()); // 纬度
position.put("lng", JSONUtil.parseObj(message).getJSONObject("data").get("longitude").toString()); // 经度
position.put("alt", JSONUtil.parseObj(message).getJSONObject("data").get("height").toString()); // 海拔
data.put("position", position);
messageObj.put("data", data); // 设备唯一标识
// 转换为String类型返回
return messageObj.toString();
}
}

View File

@ -0,0 +1,110 @@
package org.dromara.bigscreen.service.impl;// 路径com.ruoyi.web.websocket.InitOnStartWebSocketServer
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import jakarta.annotation.Resource;
import jakarta.websocket.*;
import jakarta.websocket.server.ServerEndpoint;
import lombok.extern.slf4j.Slf4j;
import org.dromara.bigscreen.manager.RedisSubscribeManager;
import org.dromara.common.redis.utils.RedisUtils;
import org.dromara.drone.domain.DroProjectDrone;
import org.dromara.drone.service.IDroProjectDroneService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
import java.io.IOException;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
/**
* 项目启动即自动启动的 WebSocket 服务端
* 端点路径:/websocket/init-on-start可自定义
*/
@Slf4j
@ServerEndpoint("/websocket/ue") // 定义 WebSocket 端点路径
@Component // 交给 Spring 管理,确保启动时被扫描
public class InitOnStartWebSocketServer {
// 2. 静态会话存储(线程安全,项目启动时即初始化)
private static final Map<String, Session> ONLINE_SESSIONS = new ConcurrentHashMap<>();
// 3. 静态代码块:项目启动时执行(初始化资源、打印启动日志)
static {
// 此处可添加启动时的初始化逻辑(如加载配置、连接外部资源等)
log.info("✅ WebSocket 服务端已随项目启动初始化!端点路径:/websocket/ue");
}
/**
* 客户端连接时触发(无需手动启动,有客户端连接时自动调用)
*/
@OnOpen
public void onOpen(Session session) {
// 存储新会话
ONLINE_SESSIONS.put(session.getId(), session);
// RedisUtils.setCacheObject("xmjdap:ws",System.currentTimeMillis() );
log.info("📌 客户端连接成功会话ID{},当前在线数:{}", session.getId(), ONLINE_SESSIONS.size());
}
/**
* 接收客户端消息
*/
@OnMessage
public void onMessage(String message, Session session) {
log.info("📥 收到会话[{}]消息:{}", session.getId(), message);
// 可选:回复客户端(示例)
try {
session.getBasicRemote().sendText("服务端已收到消息:" + message);
} catch (IOException e) {
log.error("📤 回复会话[{}]失败:{}", session.getId(), e.getMessage());
}
}
/**
* 客户端断开连接
*/
@OnClose
public void onClose(Session session, CloseReason reason) {
ONLINE_SESSIONS.remove(session.getId());
log.info("🔌 客户端断开连接会话ID{},原因:{},当前在线数:{}",
session.getId(), reason.getReasonPhrase(), ONLINE_SESSIONS.size());
}
/**
* 连接异常
*/
@OnError
public void onError(Session session, Throwable error) {
log.error("⚠️ 会话[{}]异常:{}", session.getId(), error.getMessage(), error);
}
// ------------------------------ 工具方法(可选,供其他服务调用) ------------------------------
/**
* 向所有在线客户端发送消息(项目启动后,其他服务可直接调用)
*/
public static void sendToAll(String message) {
if (ONLINE_SESSIONS.isEmpty()) {
log.warn("⚠️ 无在线客户端,无需发送消息");
return;
}
ONLINE_SESSIONS.values().forEach(session -> {
if (session.isOpen()) {
try {
session.getBasicRemote().sendText(message);
} catch (IOException e) {
log.error("📤 向会话[{}]发送消息失败:{}", session.getId(), e.getMessage());
}
}
});
}
/**
* 获取当前在线数(供外部查询)
*/
public static int getOnlineCount() {
return ONLINE_SESSIONS.size();
}
}

View File

@ -318,11 +318,13 @@ public class BusMrpBaseServiceImpl extends ServiceImpl<BusMrpBaseMapper, BusMrpB
.reduce(BigDecimal.ZERO, BigDecimal::add); .reduce(BigDecimal.ZERO, BigDecimal::add);
} }
map.put("remainingQuantity",quantity.subtract(reduce)); map.put("remainingQuantity",quantity.subtract(reduce));
map.put("specification",busBillofquantities.getSpecification());
map.put("suppliespriceId",limitListId); map.put("suppliespriceId",limitListId);
map.put("unit",busBillofquantities.getUnit()); if (busBillofquantities != null) {
map.put("remark",busBillofquantities.getRemark()); map.put("specification",busBillofquantities.getSpecification());
map.put("name",busBillofquantities.getName()); map.put("unit",busBillofquantities.getUnit());
map.put("remark",busBillofquantities.getRemark());
map.put("name",busBillofquantities.getName());
}
return map; return map;
} }

View File

@ -0,0 +1,93 @@
package org.dromara.common.utils;
import java.util.HashMap;
import java.util.Map;
/**
* 接口错误码匹配工具(用户展示用)
*/
public class ErrorCodeMatcher {
private static final Map<String, String> ERROR_MAP = new HashMap<>();
static {
// 基础错误码
ERROR_MAP.put("1", "抱歉,出现未知错误,请您重试。若问题持续,请联系管理员。");
ERROR_MAP.put("2", "服务暂时不可用,请稍后重试。若多次尝试仍有问题,请联系管理员。");
ERROR_MAP.put("3", "调用的接口不存在,请检查请求地址是否正确(建议避免特殊字符,如手动输入重试)。");
ERROR_MAP.put("4", "当前集群请求量超限,请稍后再试。持续异常请联系管理员。");
ERROR_MAP.put("6", "暂无该接口调用权限,请联系管理员开通相关权限。");
ERROR_MAP.put("14", "鉴权失败,请检查密钥是否正确,或联系管理员协助处理。");
ERROR_MAP.put("17", "今日免费额度已用完。如需继续使用,请联系管理员获取更多资源。");
ERROR_MAP.put("18", "请求频率超限QPS限制。如需提高限制请联系管理员咨询。");
ERROR_MAP.put("19", "总请求量已达限额,请联系管理员获取更多资源。");
ERROR_MAP.put("100", "访问令牌无效,请联系管理员重新获取。");
ERROR_MAP.put("110", "访问令牌无效有效期30天请联系管理员更新。");
ERROR_MAP.put("111", "访问令牌已过期,请联系管理员重新获取。");
// 216xxx系列错误码
ERROR_MAP.put("216100", "请求中包含无效参数,请检查后重新提交。");
ERROR_MAP.put("216101", "缺少必要的请求参数,请核对后补充完整。");
ERROR_MAP.put("216102", "请求的服务暂不支持,请检查调用地址是否正确,或联系管理员确认。");
ERROR_MAP.put("216103", "部分参数长度超限,请缩短后重新尝试。");
ERROR_MAP.put("216110", "应用ID不存在请核对是否为正确ID或联系管理员确认。");
ERROR_MAP.put("216200", "上传的图片为空,请检查图片是否正确上传。");
ERROR_MAP.put("216201", "图片格式不支持目前仅支持PNG、JPG、JPEG、BMP格式请转码后重试。");
ERROR_MAP.put("216202", "图片大小不符合要求,请参考接口文档的限制重新上传,或联系管理员咨询。");
ERROR_MAP.put("216205", "请求体过大base64编码后需小于10M请压缩内容后重试。");
ERROR_MAP.put("216306", "文件上传失败,请检查请求参数是否符合要求。");
ERROR_MAP.put("216307", "图片解析失败,若多次尝试仍有问题,请联系管理员。");
ERROR_MAP.put("216308", "PDF页码参数超过实际页数请核对后修改。");
ERROR_MAP.put("216401", "请求提交失败,请稍后重试。");
ERROR_MAP.put("216402", "获取结果失败,请稍后重试或检查任务状态。");
ERROR_MAP.put("216603", "无法获取PDF页数请检查文件是否完整或编码是否正确。");
ERROR_MAP.put("216604", "请求总量已达限额,请联系管理员获取更多额度。");
ERROR_MAP.put("216630", "识别失败,请重试。若问题持续,请联系管理员。");
ERROR_MAP.put("216631", "银行卡识别失败,请确保上传的是银行卡正面完整清晰的图片(异形卡可能无法识别)。");
ERROR_MAP.put("216633", "身份证识别失败,请确保上传的是身份证完整清晰的图片(非身份证或模糊图片无法识别)。");
ERROR_MAP.put("216634", "检测失败,请重试。若持续异常,请联系管理员。");
ERROR_MAP.put("216600", "企业核验服务请求失败,请重试。持续问题请联系管理员(适用于工商信息查询、要素核验等)。");
ERROR_MAP.put("216601", "企业核验查询成功,但未找到相关结果,请确认信息是否正确后重试。");
ERROR_MAP.put("216602", "企业核验服务超时,请稍后重试。持续问题请联系管理员。");
// 282xxx系列错误码
ERROR_MAP.put("282000", "服务器内部错误。若使用通用文字识别,可能因图片文字过多导致超时,建议分割图片后重试;其他情况可稍后再试,持续问题请联系管理员。");
ERROR_MAP.put("282003", "请求缺少必要参数,请核对后补充。");
ERROR_MAP.put("282005", "批量任务处理出错,请根据具体错误提示排查。");
ERROR_MAP.put("282006", "批量任务数量超限单次最多处理10个任务请减少数量后重试。");
ERROR_MAP.put("282100", "图片压缩转码失败,请更换图片后重试。");
ERROR_MAP.put("282102", "未检测到可识别的卡证/票据,请确保图片包含清晰完整的目标(非卡证或模糊图片无法识别)。");
ERROR_MAP.put("282103", "卡证/票据识别失败,请确保图片包含清晰完整的目标(非对应类型或模糊图片无法识别)。");
ERROR_MAP.put("282110", "图片URL不存在请核对地址后重新提交。");
ERROR_MAP.put("282111", "图片URL格式错误请检查是否符合接口要求的格式。");
ERROR_MAP.put("282112", "图片URL下载超时可能因图片过大超过3M、地址无效或存在防盗链。建议更换图片地址或下载后直接上传。");
ERROR_MAP.put("282113", "图片URL返回无效内容请检查地址是否正确。");
ERROR_MAP.put("282114", "图片URL长度不符合要求需1-1024字节请修改后重试。");
ERROR_MAP.put("282134", "增值税发票验真失败,可能因系统维护,建议次日再试。持续问题请联系管理员。");
ERROR_MAP.put("282808", "请求ID不存在请核对ID是否正确。");
ERROR_MAP.put("282809", "返回结果格式错误需为excel或json请稍后重试。");
ERROR_MAP.put("282810", "图像识别失败,请重试。若问题持续,请联系管理员。");
ERROR_MAP.put("282160", "行驶证核验资源超限,请联系管理员咨询。");
ERROR_MAP.put("282161", "行驶证核验请求过于频繁,请稍后再试。");
}
/**
* 根据错误码获取用户友好的错误信息
* @param errorCode 错误码(字符串/整数)
* @return 优化后的错误提示,未匹配时返回"未知错误错误码xxx"
*/
public static String getFriendlyMessage(String errorCode) {
return ERROR_MAP.getOrDefault(errorCode, "未知错误(错误码:" + errorCode + "),请重试或联系管理员。");
}
public static String getFriendlyMessage(int errorCode) {
return getFriendlyMessage(String.valueOf(errorCode));
}
// 测试示例
public static void main(String[] args) {
System.out.println(getFriendlyMessage(1)); // 输出:抱歉,出现未知错误,请您重试...
System.out.println(getFriendlyMessage("216201")); // 输出图片格式不支持目前仅支持PNG...
System.out.println(getFriendlyMessage(9999)); // 输出未知错误错误码9999...
}
}

View File

@ -3,6 +3,7 @@ package org.dromara.common.utils.baiduUtil;
import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.dromara.common.utils.ErrorCodeMatcher;
import org.dromara.common.utils.baiduUtil.entity.face.ComparisonRes; import org.dromara.common.utils.baiduUtil.entity.face.ComparisonRes;
import org.dromara.common.utils.baiduUtil.entity.face.HumanFaceReq; import org.dromara.common.utils.baiduUtil.entity.face.HumanFaceReq;
import org.dromara.common.utils.baiduUtil.entity.face.HumanFaceRes; import org.dromara.common.utils.baiduUtil.entity.face.HumanFaceRes;
@ -208,8 +209,7 @@ public class BaiDuFace {
// 8. 处理API返回错误 // 8. 处理API返回错误
if (comparisonRep.getErrorCode() != 0) { if (comparisonRep.getErrorCode() != 0) {
throw new RuntimeException("人脸对比失败:" + comparisonRep.getErrorMsg() throw new RuntimeException("人脸对比失败:" + ErrorCodeMatcher.getFriendlyMessage(comparisonRep.getErrorCode()));
+ "(错误码:" + comparisonRep.getErrorCode() + "");
} }
// 9. 校验对比结果 // 9. 校验对比结果

View File

@ -36,7 +36,7 @@ public class SubConstructionUserAuthenticationReq implements Serializable {
private String phone; private String phone;
/** /**
* 0:保密 1:男 2女 * 0男1女2未知
*/ */
@NotBlank(message = "性别不能为空") @NotBlank(message = "性别不能为空")
private String sex; private String sex;

View File

@ -0,0 +1,159 @@
package org.dromara.dataTransmission;
import cn.hutool.crypto.asymmetric.KeyType;
import cn.hutool.crypto.asymmetric.RSA;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson2.JSONObject;
import org.dromara.common.redis.utils.RedisUtils;
import javax.crypto.Cipher;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.spec.X509EncodedKeySpec;
import java.time.Duration;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
import org.springframework.http.HttpHeaders;
import org.springframework.stereotype.Component;
import java.io.ByteArrayOutputStream;
@Component
public class TokenUtils {
// Token过期时间25分钟与Redis存储过期时间保持一致
private static final long TOKEN_EXPIRE_SECONDS = 1500;
// HTTP请求超时时间10秒
private static final Duration HTTP_TIMEOUT = Duration.ofSeconds(10);
//通用字符
private static final String USER_NAME = "username";
private static final String PASSWORD = "password";
private static final String PUBLIC_KEY = "publicKey";
private static final String URL = "url";
//clientId
public static final String CLARITYPM = "claritypm";
//接口信息及参数
private static final Map<String, Map<String, String>> tokenMap = new HashMap<>(){
{
put(CLARITYPM, new HashMap<>(){{
put(URL, "https://claritypm.powerchina.cn/neSmartsite-api/loginCli");
put(USER_NAME, "zhangweiwei");
put(PASSWORD, "Hkrsoft@#2023");
put(PUBLIC_KEY, "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCoaejbjbttHyHuEzHL8lIX5GZZ6zIYrqJpEDlPM4V5LHn19rSAYp2FyAr8y5Ctny9uUdaYbkoFiVQgxWrAYo4X/3O0OFDsowE25FMOLQY0Mn5B6CvVR7Sdt3DqzIzM1tUnJCIbVGNfDMgxLrLwFN8RvOW8MPlB6LgOvlGMDbj+OQIDAQAB");
}});
}
};
//字段参数
private static final Map<String, Map<String, String>> paramMap = new HashMap<>(){
{
put(CLARITYPM, new HashMap<>(){{
put(USER_NAME, "username");
put(PASSWORD, "password");
}});
}
};
/**
* 优先从Redis获取Token不存在则调用接口获取并存储
* @param clientId 客户端ID
* @return 有效的Token字符串
* @throws Exception 当获取过程发生异常时抛出
*/
public static String getToken(String clientId) throws Exception {
// 1. 定义Redis中的Token键名
// String redisKey = "data_auth:token:" + clientId;
//
// // 2. 尝试从Redis获取Token
// String token = RedisUtils.getCacheObject(redisKey);
// if (token != null && !token.isBlank()) {
// // 缓存命中,直接返回
// return token;
// }
// 3. 缓存未命中调用接口获取Token
Map<String, String> map = tokenMap.get(clientId);
String token = fetchTokenFromApi(clientId,map.get(URL), map.get(USER_NAME), rsaEncrypt(map.get(PASSWORD),map.get(PUBLIC_KEY)));
// 4. 存储到Redis设置25分钟过期
// RedisUtils.setCacheObject(redisKey, token,Duration.ofSeconds(TOKEN_EXPIRE_SECONDS) );
return token;
}
/**
* 从接口获取Token的内部方法
*/
private static String fetchTokenFromApi(String clientId, String authUrl, String username, String encryptedPassword) throws Exception {
// 构建JSON请求体包含加密后的密码
// 1. 构建请求头(与原 Go 代码一致的头信息)
// Map<String, String> headers = new HashMap<>();
// headers.put(HttpHeaders.CONTENT_TYPE, "application/json; charset=UTF-8");
// headers.put("User-Agent", "Mozilla/5.0");
// headers.put(HttpHeaders.ACCEPT, "application/json");
// headers.put("Origin", "https://claritypm.powerchina.cn");
// headers.put("Referer", "https://claritypm.powerchina.cn/");
// 2. 构建请求体(保持你的原有逻辑)
Map<String, String> map = paramMap.get(clientId);
JSONObject requestBody = new JSONObject();
requestBody.put(map.get(USER_NAME), username); // 从 map 中获取接口要求的用户名参数名
requestBody.put(map.get(PASSWORD), encryptedPassword); // 加密后的密码
String jsonBody = requestBody.toJSONString();
System.out.println("请求体:" + jsonBody);
// 3. 带 Header 发送 POST 请求Hutool HttpUtil 重载方法)
String post = HttpUtil.createPost(authUrl)
// .addHeaders(headers) // 设置所有请求头
.body(jsonBody) // 设置请求体
.execute() // 执行请求
.body(); // 获取响应体
System.out.println("响应结果:" + post);
// 3. 解析响应(核心解析逻辑)
JSONObject responseJson = JSONObject.parseObject(post);
int code = responseJson.getIntValue("code");
String msg = responseJson.getString("msg");
String token = responseJson.getString("token");
// 4. 校验响应有效性
if (code != 200) {
throw new RuntimeException("获取 Token 失败,响应信息:" + msg + ",状态码:" + code);
}
if (token == null || token.trim().isEmpty()) {
throw new RuntimeException("响应中未包含有效 Token响应内容" + post);
}
return token;
}
/**
* RSA公钥加密核心加密方法
* @param content 待加密内容(原始密码)
* @param publicKeyStr 公钥字符串Base64编码
* @return 加密后的Base64字符串
* @throws Exception 加密过程异常
*/
public static String rsaEncrypt(String content, String publicKeyStr) throws Exception {
RSA rsa = new RSA(null, publicKeyStr);
byte[] encryptedBytes = rsa.encrypt(content, KeyType.PublicKey);
return Base64.getEncoder().encodeToString(encryptedBytes);
}
public static void main(String[] args) throws Exception {
String claritypm = getToken(CLARITYPM);
System.out.println(claritypm);
}
}

View File

@ -0,0 +1,79 @@
package org.dromara.dataTransmission.claritypm;
import org.dromara.dataTransmission.TokenUtils;
import org.dromara.dataTransmission.claritypm.dto.RealUser;
import org.springframework.stereotype.Component;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.List;
import java.util.Map;
import java.util.HashMap;
@Component
public class ClaritypmClient {
// 接口地址
private static final String INSERT_REAL_USER_URL = "https://claritypm.powerchina.cn/neSmartsite-api/realUser/tiandong/insertRealUser";
@Autowired
private TokenUtils tokenUtils; // 依赖之前的Token获取服务
/**
* 批量新增实名制用户信息
* @param userList 用户信息列表建议单次不超过10条
* @return 接口响应结果JSON格式
* @throws Exception 调用异常
*/
public static String batchInsertRealUser(List<RealUser> userList) throws Exception {
// 1. 校验列表大小建议不超过10条
if (userList == null || userList.isEmpty()) {
throw new IllegalArgumentException("用户列表不能为空");
}
if (userList.size() > 10) {
throw new IllegalArgumentException("单次批量新增不能超过10条数据");
}
// 2. 获取Token从之前的TokenService获取
String token = TokenUtils.getToken(TokenUtils.CLARITYPM);
if ( token.trim().isEmpty()) {
throw new RuntimeException("获取Token失败无法调用接口");
}
// 3. 构建请求头包含Token认证
Map<String, String> headers = new HashMap<>();
headers.put("Content-Type", "application/json; charset=UTF-8");
headers.put("User-Agent", "Mozilla/5.0");
headers.put("Accept", "application/json");
headers.put("Origin", "https://claritypm.powerchina.cn");
headers.put("Referer", "https://claritypm.powerchina.cn/");
headers.put("Authorization", "Bearer " + token); // 假设接口使用Bearer Token认证
// 4. 构建请求体JSONArray格式
JSONArray requestBody = JSONArray.parseArray(JSON.toJSONString(userList));
String jsonBody = requestBody.toJSONString();
System.out.println("批量新增用户请求体:" + jsonBody);
// 5. 发送POST请求
String response = HttpUtil.createPost(INSERT_REAL_USER_URL)
.addHeaders(headers)
.body(jsonBody)
.execute()
.body();
System.out.println("批量新增用户响应:" + response);
// 6. 解析响应(根据实际响应结构调整,此处假设与登录接口类似)
JSONObject responseJson = JSONObject.parseObject(response);
int code = responseJson.getIntValue("code");
String msg = responseJson.getString("msg");
if (code != 200) {
throw new RuntimeException("批量新增用户失败:" + msg + "(状态码:" + code + "");
}
return response;
}
}

View File

@ -0,0 +1,92 @@
package org.dromara.dataTransmission.claritypm.dto;
import lombok.Data;
import java.time.LocalDate;
/**
* 实名制用户信息实体类(对应接口参数)
*/
@Data
public class RealUser {
// 人员姓名(必填)
private String userName;
// 是否班组长非必填0-否 1-是)
private String classManagerFlag;
// 手机号码(必填)
private String phone;
// 性别必填1.男 2.女 3.未知)
private String sex;
// 证件类型必填0.身份证)
private String cardType;
// 证件号码(必填,身份证号码)
private String cardNumber;
// 人员类型必填0.作业人员 1.管理人员)
private String userType;
// 发证机关(非必填)
private String cardDept;
// 民族(非必填)
private String nation;
// 生日非必填datetime格式
private LocalDate birthday;
// 住址(非必填)
private String address;
// 头像非必填http地址
private String avatar;
// 采集相片非必填http地址
private String pic;
// 安全教育表非必填http地址
private String safetyEdu;
// 技术交底表非必填http地址
private String technology;
// 证件有效期始非必填datetime格式
private LocalDate cardStartTime;
// 证件有效期至非必填datetime格式
private LocalDate cardEndTime;
// 学历非必填1-9对应小学至其他
private String studyLevel;
// 政治面貌非必填0-3对应党员至民主党派
private String politicsType;
// 购买保险非必填0-否 1-是)
private String insuranceFlag;
// 重大病史非必填0-否 1-是)
private String diseaseFlag;
// 劳动合同非必填0-未签订 1-已签订)
private String laborContractFlag;
// 紧急联系人(非必填)
private String contacts;
// 紧急联系人电话(非必填)
private String contactsPhone;
// 婚姻状况非必填0-3对应未婚至丧偶
private String marryRemark;
// 企业名称(非必填)
private String companyName;
}

View File

@ -129,7 +129,7 @@ public class DesExtractController extends BaseController {
@SaCheckPermission("design:extract:userMajor") @SaCheckPermission("design:extract:userMajor")
@GetMapping("/userMajor") @GetMapping("/userMajor")
public R<List<DesUserVo>> selectUserMajor(DesUserBo bo) { public R<List<DesUserVo>> selectUserMajor(DesUserBo bo) {
bo.setUserType("1"); bo.setUserType("2");
return R.ok( deUserService.queryList(bo)); return R.ok( deUserService.queryList(bo));
} }

View File

@ -40,6 +40,18 @@ public class DroProjectDrone implements Serializable {
* 备注 * 备注
*/ */
private String remark; private String remark;
/**
* 飞机sn
*/
private String airplaneSn;
/**
* 无人机机场模型id
*/
private String droneModleId;
/**
* 飞机模型id
*/
private String airplaneModleId;
} }

View File

@ -43,5 +43,18 @@ public class DroProjectDroneBo extends BaseEntity {
*/ */
private String remark; private String remark;
/**
* 飞机sn
*/
private String airplaneSn;
/**
* 无人机机场模型id
*/
private String droneModleId;
/**
* 飞机模型id
*/
private String airplaneModleId;
} }

View File

@ -51,6 +51,17 @@ public class DroProjectDroneVo implements Serializable {
*/ */
@ExcelProperty(value = "备注") @ExcelProperty(value = "备注")
private String remark; private String remark;
/**
* 飞机sn
*/
private String airplaneSn;
/**
* 无人机机场模型id
*/
private String droneModleId;
/**
* 飞机模型id
*/
private String airplaneModleId;
} }

View File

@ -23,7 +23,10 @@ import org.dromara.facility.constant.FacRedisKeyConstant;
import org.dromara.facility.domain.FacMatrix; import org.dromara.facility.domain.FacMatrix;
import org.dromara.facility.domain.FacPhotovoltaicPanel; import org.dromara.facility.domain.FacPhotovoltaicPanel;
import org.dromara.facility.domain.dto.geojson.*; import org.dromara.facility.domain.dto.geojson.*;
import org.dromara.facility.domain.dto.photovoltaicpanel.*; import org.dromara.facility.domain.dto.photovoltaicpanel.FacPhotovoltaicPanelCreateByGeoJsonReq;
import org.dromara.facility.domain.dto.photovoltaicpanel.FacPhotovoltaicPanelCreateReq;
import org.dromara.facility.domain.dto.photovoltaicpanel.FacPhotovoltaicPanelQueryReq;
import org.dromara.facility.domain.dto.photovoltaicpanel.FacPhotovoltaicPanelUpdateReq;
import org.dromara.facility.domain.enums.FacFinishStatusEnum; import org.dromara.facility.domain.enums.FacFinishStatusEnum;
import org.dromara.facility.domain.enums.FacFinishTypeEnum; import org.dromara.facility.domain.enums.FacFinishTypeEnum;
import org.dromara.facility.domain.vo.photovoltaicpanel.FacPhotovoltaicPanelVo; import org.dromara.facility.domain.vo.photovoltaicpanel.FacPhotovoltaicPanelVo;
@ -329,10 +332,10 @@ public class FacPhotovoltaicPanelServiceImpl extends ServiceImpl<FacPhotovoltaic
} }
Long matrixId = matrix.getId();*/ Long matrixId = matrix.getId();*/
// 去掉首字母 (T/G) // 去掉首字母 (T/G)
String withoutPrefix = name.substring(2); String withoutPrefix = name.substring(1);
// 如果包含".",只取第一个"."前的数字 // 如果包含".",只取第一个"."前的数字
int dotIndex = withoutPrefix.indexOf("#"); int dotIndex = withoutPrefix.indexOf(".");
if (dotIndex != -1) { if (dotIndex != -1) {
withoutPrefix = withoutPrefix.substring(0, dotIndex); withoutPrefix = withoutPrefix.substring(0, dotIndex);
} }

View File

@ -88,5 +88,10 @@ public class GpsEquipment extends BaseEntity {
*/ */
private Integer gpsType; private Integer gpsType;
/**
* 模型id
*/
private String modelId;
} }

View File

@ -87,4 +87,9 @@ public class GpsEquipmentBo extends BaseEntity {
*/ */
private Integer gpsType; private Integer gpsType;
/**
* 模型id
*/
private String modelId;
} }

View File

@ -106,5 +106,10 @@ public class GpsEquipmentVo implements Serializable {
*/ */
private Integer gpsType; private Integer gpsType;
/**
* 模型id
*/
private String modelId;
} }

View File

@ -6,6 +6,7 @@ import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.dromara.bigscreen.service.impl.InitOnStartWebSocketServer;
import org.dromara.common.core.exception.ServiceException; import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.utils.MapstructUtils; import org.dromara.common.core.utils.MapstructUtils;
import org.dromara.common.core.utils.StringUtils; import org.dromara.common.core.utils.StringUtils;
@ -17,6 +18,7 @@ import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.dromara.common.redis.utils.RedisUtils; import org.dromara.common.redis.utils.RedisUtils;
import org.dromara.common.websocket.dto.WebSocketMessageDto; import org.dromara.common.websocket.dto.WebSocketMessageDto;
import org.dromara.common.websocket.holder.WebSocketSessionHolder;
import org.dromara.common.websocket.utils.WebSocketUtils; import org.dromara.common.websocket.utils.WebSocketUtils;
import org.dromara.gps.domain.GpsManmachine; import org.dromara.gps.domain.GpsManmachine;
import org.dromara.gps.domain.bo.GpsEquipmentSonBo; import org.dromara.gps.domain.bo.GpsEquipmentSonBo;
@ -103,7 +105,7 @@ public class GpsEquipmentServiceImpl extends ServiceImpl<GpsEquipmentMapper, Gps
} }
SysUserVo sysUserVo = userService.queryById(item.getUserId()); SysUserVo sysUserVo = userService.queryById(item.getUserId());
if (sysUserVo != null) { if (sysUserVo != null) {
item.setUserName(sysUserVo.getUserName()); item.setUserName(sysUserVo.getNickName());
} }
BusProjectVo busProjectVo = projectService.selectById(item.getProjectId()); BusProjectVo busProjectVo = projectService.selectById(item.getProjectId());
if (busProjectVo != null) { if (busProjectVo != null) {
@ -143,7 +145,7 @@ public class GpsEquipmentServiceImpl extends ServiceImpl<GpsEquipmentMapper, Gps
private LambdaQueryWrapper<GpsEquipment> buildQueryWrapper(GpsEquipmentBo bo) { private LambdaQueryWrapper<GpsEquipment> buildQueryWrapper(GpsEquipmentBo bo) {
Map<String, Object> params = bo.getParams(); Map<String, Object> params = bo.getParams();
LambdaQueryWrapper<GpsEquipment> lqw = Wrappers.lambdaQuery(); LambdaQueryWrapper<GpsEquipment> lqw = Wrappers.lambdaQuery();
lqw.orderByDesc(GpsEquipment::getId); lqw.orderByDesc(GpsEquipment::getCreateTime);
lqw.eq(bo.getGpsType() != null, GpsEquipment::getGpsType, bo.getGpsType()); lqw.eq(bo.getGpsType() != null, GpsEquipment::getGpsType, bo.getGpsType());
lqw.eq(bo.getProjectId() != null, GpsEquipment::getProjectId, bo.getProjectId()); lqw.eq(bo.getProjectId() != null, GpsEquipment::getProjectId, bo.getProjectId());
lqw.eq(bo.getUserId() != null, GpsEquipment::getUserId, bo.getUserId()); lqw.eq(bo.getUserId() != null, GpsEquipment::getUserId, bo.getUserId());
@ -245,19 +247,30 @@ public class GpsEquipmentServiceImpl extends ServiceImpl<GpsEquipmentMapper, Gps
// 发布String类型订阅消息 // 发布String类型订阅消息
// -------------------------- // --------------------------
// 1. 构造需要推送的消息内容String类型 // 1. 构造需要推送的消息内容String类型
String pushContent = buildPushMessage(gpsEquipmentSon); //判断是否有连接
int onlineCount = InitOnStartWebSocketServer.getOnlineCount();
if (onlineCount > 0){
if (equipment != null && StringUtils.isNotEmpty(equipment.getModelId())) {
String ued = ueStructureJsonMessage(gpsEquipmentSon, equipment.getModelId());
InitOnStartWebSocketServer.sendToAll(ued);
}
}
Set<Long> sessionsAll = WebSocketSessionHolder.getSessionsAll();
if (!sessionsAll.isEmpty()) {
String pushContent = buildPushMessage(gpsEquipmentSon);
// WebSocketUtils.publishAll(pushContent); // WebSocketUtils.publishAll(pushContent);
// 2. 发布消息根据是否有用户ID决定发送给指定用户或广播 // 2. 发布消息根据是否有用户ID决定发送给指定用户或广播
if (equipment != null && equipment.getProjectId() != null) { if (equipment != null && equipment.getProjectId() != null) {
// 发送给指定用户equipment.getUserId() // 发送给指定用户equipment.getUserId()
WebSocketMessageDto messageDto = new WebSocketMessageDto(); WebSocketMessageDto messageDto = new WebSocketMessageDto();
messageDto.setMessage(pushContent); messageDto.setMessage(pushContent);
messageDto.setSessionKeys(Collections.singletonList(equipment.getProjectId())); messageDto.setSessionKeys(Collections.singletonList(equipment.getProjectId()));
WebSocketUtils.publishMessage(messageDto); WebSocketUtils.publishMessage(messageDto);
} else { } else {
// 无用户ID则广播给所有在线客户端 // 无用户ID则广播给所有在线客户端
WebSocketUtils.publishAll(pushContent); WebSocketUtils.publishAll(pushContent);
}
} }
} }
// 保存到Redis并设置过期监听 // 保存到Redis并设置过期监听
@ -286,6 +299,30 @@ public class GpsEquipmentServiceImpl extends ServiceImpl<GpsEquipmentMapper, Gps
return messageObj.toString(); return messageObj.toString();
} }
/**
* 构建推送消息内容String类型
*/
private String ueStructureJsonMessage(GpsEquipmentSonBo sonBo,String modelId) {
// 构造消息对象(包含关键信息)
JSONObject messageObj = new JSONObject();
messageObj.put("type", "location"); // 消息类型
JSONObject data = new JSONObject();
data.put("id", modelId);
// 位置信息
JSONObject position = new JSONObject();
position.put("lat", sonBo.getLocLatitude().toString()); // 纬度
position.put("lng", sonBo.getLocLongitude().toString()); // 经度
position.put("alt", sonBo.getLocAltitude().toString()); // 海拔
data.put("position", position);
messageObj.put("data", data); // 设备唯一标识
// 转换为String类型返回
return messageObj.toString();
}
private static final int DEVICE_ALIVE_TIMEOUT = 120; // 5分钟 private static final int DEVICE_ALIVE_TIMEOUT = 120; // 5分钟
/** /**
@ -459,19 +496,29 @@ public class GpsEquipmentServiceImpl extends ServiceImpl<GpsEquipmentMapper, Gps
gpsEquipmentSonService.insertByBo(gpsEquipmentSonBo); gpsEquipmentSonService.insertByBo(gpsEquipmentSonBo);
String pushContent = buildPushMessage(gpsEquipmentSonBo); //判断是否有连接
int onlineCount = InitOnStartWebSocketServer.getOnlineCount();
if (onlineCount > 0){
if (equipment != null && StringUtils.isNotEmpty(equipment.getModelId())) {
String ued = ueStructureJsonMessage(gpsEquipmentSonBo, equipment.getModelId());
InitOnStartWebSocketServer.sendToAll(ued);
}
}
Set<Long> sessionsAll = WebSocketSessionHolder.getSessionsAll();
if (!sessionsAll.isEmpty()) {
String pushContent = buildPushMessage(gpsEquipmentSonBo);
// WebSocketUtils.publishAll(pushContent); // WebSocketUtils.publishAll(pushContent);
// 2. 发布消息根据是否有用户ID决定发送给指定用户或广播 // 2. 发布消息根据是否有用户ID决定发送给指定用户或广播
if (equipment != null && equipment.getProjectId() != null) { if (equipment != null && equipment.getProjectId() != null) {
// 发送给指定用户equipment.getUserId() // 发送给指定用户equipment.getUserId()
WebSocketMessageDto messageDto = new WebSocketMessageDto(); WebSocketMessageDto messageDto = new WebSocketMessageDto();
messageDto.setMessage(pushContent); messageDto.setMessage(pushContent);
messageDto.setSessionKeys(Collections.singletonList(equipment.getProjectId())); messageDto.setSessionKeys(Collections.singletonList(equipment.getProjectId()));
WebSocketUtils.publishMessage(messageDto); WebSocketUtils.publishMessage(messageDto);
} else { } else {
// 无用户ID则广播给所有在线客户端 // 无用户ID则广播给所有在线客户端
WebSocketUtils.publishAll(pushContent); WebSocketUtils.publishAll(pushContent);
}
} }
// 保存到Redis并设置过期监听 // 保存到Redis并设置过期监听
updateDeviceAliveStatus(gpsEquipment.getClientId()); updateDeviceAliveStatus(gpsEquipment.getClientId());

View File

@ -87,8 +87,7 @@ public class AttendanceJob {
.lt(BusAttendanceRule::getClockInResultTime, end)); .lt(BusAttendanceRule::getClockInResultTime, end));
} }
//获取当前日期
LocalDate date = LocalDate.now();
//管理员关联多个项目,需要记录是否已生成缺卡记录 //管理员关联多个项目,需要记录是否已生成缺卡记录
// HashSet<Long> manageUserIds = new HashSet<>(); // HashSet<Long> manageUserIds = new HashSet<>();
@ -97,6 +96,8 @@ public class AttendanceJob {
for (BusAttendanceRule rule : list) { for (BusAttendanceRule rule : list) {
LocalTime clockInTime = rule.getClockInTime(); LocalTime clockInTime = rule.getClockInTime();
LocalTime clockInResultTime = rule.getClockInResultTime(); LocalTime clockInResultTime = rule.getClockInResultTime();
//获取当前日期
LocalDate date = LocalDate.now();
//计算考勤日期 //计算考勤日期
if (start.isAfter(end)) { // 跨天情况 if (start.isAfter(end)) { // 跨天情况
@ -210,8 +211,7 @@ public class AttendanceJob {
.lt(BusAttendanceRule::getClockOutResultTime, end)); .lt(BusAttendanceRule::getClockOutResultTime, end));
} }
//获取当前日期
LocalDate date = LocalDate.now();
//管理员关联多个项目,需要记录是否已生成缺卡记录 //管理员关联多个项目,需要记录是否已生成缺卡记录
// HashSet<Long> manageUserIds = new HashSet<>(); // HashSet<Long> manageUserIds = new HashSet<>();
@ -220,6 +220,8 @@ public class AttendanceJob {
List<BusAttendance> missList = new ArrayList<>(); List<BusAttendance> missList = new ArrayList<>();
for (BusAttendanceRule rule : list) { for (BusAttendanceRule rule : list) {
//获取当前日期
LocalDate date = LocalDate.now();
LocalTime clockOutTime = rule.getClockOutTime(); LocalTime clockOutTime = rule.getClockOutTime();
LocalTime clockOutResultTime = rule.getClockOutResultTime(); LocalTime clockOutResultTime = rule.getClockOutResultTime();

View File

@ -1,5 +1,6 @@
package org.dromara.job.cycle; package org.dromara.job.cycle;
import cn.hutool.core.collection.CollUtil;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.dromara.manager.ys7manager.Ys7Manager; import org.dromara.manager.ys7manager.Ys7Manager;
@ -10,6 +11,8 @@ import org.dromara.other.domain.enums.OthDeviceStatusEnum;
import org.dromara.other.service.IOthDevicePresetService; import org.dromara.other.service.IOthDevicePresetService;
import org.dromara.other.service.IOthYs7DeviceImgService; import org.dromara.other.service.IOthYs7DeviceImgService;
import org.dromara.other.service.IOthYs7DeviceService; import org.dromara.other.service.IOthYs7DeviceService;
import org.dromara.safety.domain.HseViolationLevel;
import org.dromara.safety.service.IHseViolationLevelService;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.scheduling.annotation.Scheduled; import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@ -40,6 +43,9 @@ public class IncSyncYs7DeviceCapturePicData {
@Resource @Resource
private IOthYs7DeviceImgService ys7DeviceImgService; private IOthYs7DeviceImgService ys7DeviceImgService;
@Resource
private IHseViolationLevelService violationLevelService;
@Resource @Resource
private Ys7Manager ys7Manager; private Ys7Manager ys7Manager;
@ -49,11 +55,21 @@ public class IncSyncYs7DeviceCapturePicData {
@Scheduled(cron = "0 */10 7-19 * * ?") @Scheduled(cron = "0 */10 7-19 * * ?")
public void run() { public void run() {
log.info("执行萤石设备抓拍图片"); log.info("执行萤石设备抓拍图片");
// 获取所有项目的等级信息
List<HseViolationLevel> allLevel = violationLevelService.lambdaQuery()
.select(HseViolationLevel::getId, HseViolationLevel::getProjectId)
.list();
Set<Long> projectIds = allLevel.stream().map(HseViolationLevel::getProjectId).collect(Collectors.toSet());
// 查询所有在线的摄像头设备,仅获取必要字段 // 查询所有在线的摄像头设备,仅获取必要字段
List<OthYs7Device> deviceList = ys7DeviceService.lambdaQuery() List<OthYs7Device> deviceList = ys7DeviceService.lambdaQuery()
.select(OthYs7Device::getId, OthYs7Device::getDeviceSerial, OthYs7Device::getDeviceName) .select(OthYs7Device::getId, OthYs7Device::getDeviceSerial, OthYs7Device::getDeviceName)
.in(OthYs7Device::getProjectId, projectIds) // 仅获取设置了安全等级项目的摄像头
.eq(OthYs7Device::getStatus, OthDeviceStatusEnum.ONLINE.getValue()) .eq(OthYs7Device::getStatus, OthDeviceStatusEnum.ONLINE.getValue())
.list(); .list();
if (CollUtil.isEmpty(deviceList)) {
log.info("没有可拍摄的摄像头设备");
return;
}
// 提取设备序列号用于后续查询预置位 // 提取设备序列号用于后续查询预置位
List<String> deviceSerialList = deviceList.stream() List<String> deviceSerialList = deviceList.stream()
.map(OthYs7Device::getDeviceSerial).toList(); .map(OthYs7Device::getDeviceSerial).toList();

View File

@ -3,6 +3,7 @@ package org.dromara.manager.recognizermanager.enums;
import lombok.Getter; import lombok.Getter;
import java.util.List; import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors; import java.util.stream.Collectors;
/** /**
@ -48,6 +49,15 @@ public enum RecognizerTypeEnum {
return null; return null;
} }
public static RecognizerTypeEnum fromCode(String code) {
for (RecognizerTypeEnum type : RecognizerTypeEnum.values()) {
if (type.getCode().equals(code)) {
return type;
}
}
return null;
}
/** /**
* 将多个 RecognizerTypeEnum 拼接为接口识别参数字符串(","分隔) * 将多个 RecognizerTypeEnum 拼接为接口识别参数字符串(","分隔)
*/ */
@ -60,4 +70,18 @@ public enum RecognizerTypeEnum {
.collect(Collectors.joining(",")); .collect(Collectors.joining(","));
} }
/**
* 将多个 code 转换为 RecognizerTypeEnum
*
* @param codes code列表
* @return RecognizerTypeEnum列表
*/
public static List<RecognizerTypeEnum> listFromCodes(List<String> codes) {
return codes.stream()
.map(RecognizerTypeEnum::fromCode)
.filter(Objects::nonNull)
.distinct()
.toList();
}
} }

View File

@ -0,0 +1,106 @@
package org.dromara.materials.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import lombok.RequiredArgsConstructor;
import org.dromara.common.core.domain.R;
import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.core.validate.EditGroup;
import org.dromara.common.excel.utils.ExcelUtil;
import org.dromara.common.idempotent.annotation.RepeatSubmit;
import org.dromara.common.log.annotation.Log;
import org.dromara.common.log.enums.BusinessType;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.web.core.BaseController;
import org.dromara.materials.domain.bo.MatWarehouseBo;
import org.dromara.materials.domain.vo.MatWarehouseVo;
import org.dromara.materials.service.IMatWarehouseService;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* 物资仓库
*
* @author lilemy
* @date 2025-10-29
*/
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/materials/warehouse")
public class MatWarehouseController extends BaseController {
private final IMatWarehouseService matWarehouseService;
/**
* 查询物资仓库列表
*/
@SaCheckPermission("materials:warehouse:list")
@GetMapping("/list")
public TableDataInfo<MatWarehouseVo> list(MatWarehouseBo bo, PageQuery pageQuery) {
return matWarehouseService.queryPageList(bo, pageQuery);
}
/**
* 导出物资仓库列表
*/
@SaCheckPermission("materials:warehouse:export")
@Log(title = "物资仓库", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(MatWarehouseBo bo, HttpServletResponse response) {
List<MatWarehouseVo> list = matWarehouseService.queryList(bo);
ExcelUtil.exportExcel(list, "物资仓库", MatWarehouseVo.class, response);
}
/**
* 获取物资仓库详细信息
*
* @param id 主键
*/
@SaCheckPermission("materials:warehouse:query")
@GetMapping("/{id}")
public R<MatWarehouseVo> getInfo(@NotNull(message = "主键不能为空")
@PathVariable Long id) {
return R.ok(matWarehouseService.queryById(id));
}
/**
* 新增物资仓库
*/
@SaCheckPermission("materials:warehouse:add")
@Log(title = "物资仓库", businessType = BusinessType.INSERT)
@RepeatSubmit()
@PostMapping()
public R<Void> add(@Validated(AddGroup.class) @RequestBody MatWarehouseBo bo) {
return toAjax(matWarehouseService.insertByBo(bo));
}
/**
* 修改物资仓库
*/
@SaCheckPermission("materials:warehouse:edit")
@Log(title = "物资仓库", businessType = BusinessType.UPDATE)
@RepeatSubmit()
@PutMapping()
public R<Void> edit(@Validated(EditGroup.class) @RequestBody MatWarehouseBo bo) {
return toAjax(matWarehouseService.updateByBo(bo));
}
/**
* 删除物资仓库
*
* @param ids 主键串
*/
@SaCheckPermission("materials:warehouse:remove")
@Log(title = "物资仓库", businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}")
public R<Void> remove(@NotEmpty(message = "主键不能为空")
@PathVariable Long[] ids) {
return toAjax(matWarehouseService.deleteWithValidByIds(List.of(ids), true));
}
}

View File

@ -68,6 +68,16 @@ public class MatMaterialIssue extends BaseEntity {
*/ */
private String issueUnit; private String issueUnit;
/**
* 领料单位id
*/
private Long issueUnitId;
/**
* 领用人
*/
private String shipper;
/** /**
* 保管单位 * 保管单位
*/ */

View File

@ -38,6 +38,11 @@ public class MatMaterials extends BaseEntity {
*/ */
private Long companyId; private Long companyId;
/**
* 仓库id
*/
private Long warehouseId;
/** /**
* 项目id * 项目id
*/ */

View File

@ -0,0 +1,91 @@
package org.dromara.materials.domain;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.dromara.common.mybatis.core.domain.BaseEntity;
import java.io.Serial;
/**
* 物资仓库对象 mat_warehouse
*
* @author lilemy
* @date 2025-10-29
*/
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("mat_warehouse")
public class MatWarehouse extends BaseEntity {
@Serial
private static final long serialVersionUID = 1L;
/**
* 主键id
*/
@TableId(value = "id")
private Long id;
/**
* 项目id
*/
private Long projectId;
/**
* 仓库编号
*/
private String warehouseCode;
/**
* 仓库名称
*/
private String warehouseName;
/**
* 仓库类型
*/
private String warehouseType;
/**
* 仓库地址
*/
private String address;
/**
* 经度
*/
private String lng;
/**
* 纬度
*/
private String lat;
/**
* 仓库面积
*/
private Long area;
/**
* 存放容量
*/
private Long capacity;
/**
* 负责人
*/
private String manager;
/**
* 负责人电话
*/
private String managerPhone;
/**
* 备注
*/
private String remark;
}

View File

@ -0,0 +1,94 @@
package org.dromara.materials.domain.bo;
import io.github.linpeilie.annotations.AutoMapper;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.core.validate.EditGroup;
import org.dromara.common.mybatis.core.domain.BaseEntity;
import org.dromara.materials.domain.MatWarehouse;
/**
* 物资仓库业务对象 mat_warehouse
*
* @author lilemy
* @date 2025-10-29
*/
@Data
@EqualsAndHashCode(callSuper = true)
@AutoMapper(target = MatWarehouse.class, reverseConvertGenerate = false)
public class MatWarehouseBo extends BaseEntity {
/**
* 主键id
*/
@NotNull(message = "主键id不能为空", groups = {EditGroup.class})
private Long id;
/**
* 项目id
*/
@NotNull(message = "项目id不能为空", groups = {AddGroup.class, EditGroup.class})
private Long projectId;
/**
* 仓库编号
*/
@NotBlank(message = "仓库编号不能为空", groups = {AddGroup.class, EditGroup.class})
private String warehouseCode;
/**
* 仓库名称
*/
@NotBlank(message = "仓库名称不能为空", groups = {AddGroup.class, EditGroup.class})
private String warehouseName;
/**
* 仓库类型
*/
@NotBlank(message = "仓库类型不能为空", groups = {AddGroup.class, EditGroup.class})
private String warehouseType;
/**
* 仓库地址
*/
private String address;
/**
* 经度
*/
private String lng;
/**
* 纬度
*/
private String lat;
/**
* 仓库面积
*/
private Long area;
/**
* 存放容量
*/
private Long capacity;
/**
* 负责人
*/
private String manager;
/**
* 负责人电话
*/
private String managerPhone;
/**
* 备注
*/
private String remark;
}

View File

@ -63,6 +63,12 @@ public class MatMaterialIssueCreateReq implements Serializable {
@NotBlank(message = "领料单位不能为空") @NotBlank(message = "领料单位不能为空")
private String issueUnit; private String issueUnit;
/**
* 领用人
*/
@NotBlank(message = "领用人不能为空")
private String shipper;
/** /**
* 保管单位 * 保管单位
*/ */

View File

@ -57,6 +57,11 @@ public class MatMaterialIssueUpdateReq implements Serializable {
*/ */
private String issueUnit; private String issueUnit;
/**
* 领用人
*/
private String shipper;
/** /**
* 保管单位 * 保管单位
*/ */

View File

@ -61,4 +61,9 @@ public class MatMaterialReceiveItemDto {
*/ */
private Long planId; private Long planId;
/**
* 仓库id
*/
private Long warehouseId;
} }

View File

@ -26,6 +26,11 @@ public class MatMaterialsCreateReq implements Serializable {
*/ */
private Long companyId; private Long companyId;
/**
* 仓库id
*/
private Long warehouseId;
/** /**
* 项目id * 项目id
*/ */

View File

@ -25,6 +25,11 @@ public class MatMaterialsQueryReq implements Serializable {
*/ */
private Long companyId; private Long companyId;
/**
* 仓库id
*/
private Long warehouseId;
/** /**
* 项目id * 项目id
*/ */

View File

@ -31,6 +31,11 @@ public class MatMaterialsUpdateReq implements Serializable {
*/ */
private Long companyId; private Long companyId;
/**
* 仓库id
*/
private Long warehouseId;
/** /**
* 项目id * 项目id
*/ */

View File

@ -0,0 +1,108 @@
package org.dromara.materials.domain.vo;
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import com.alibaba.excel.annotation.ExcelProperty;
import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data;
import org.dromara.common.excel.annotation.ExcelDictFormat;
import org.dromara.common.excel.convert.ExcelDictConvert;
import org.dromara.materials.domain.MatWarehouse;
import java.io.Serial;
import java.io.Serializable;
/**
* 物资仓库视图对象 mat_warehouse
*
* @author lilemy
* @date 2025-10-29
*/
@Data
@ExcelIgnoreUnannotated
@AutoMapper(target = MatWarehouse.class)
public class MatWarehouseVo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 主键id
*/
@ExcelProperty(value = "主键id")
private Long id;
/**
* 项目id
*/
@ExcelProperty(value = "项目id")
private Long projectId;
/**
* 仓库编号
*/
@ExcelProperty(value = "仓库编号")
private String warehouseCode;
/**
* 仓库名称
*/
@ExcelProperty(value = "仓库名称")
private String warehouseName;
/**
* 仓库类型
*/
@ExcelProperty(value = "仓库类型", converter = ExcelDictConvert.class)
@ExcelDictFormat(dictType = "mat_warehouse_type")
private String warehouseType;
/**
* 仓库地址
*/
@ExcelProperty(value = "仓库地址")
private String address;
/**
* 经度
*/
@ExcelProperty(value = "经度")
private String lng;
/**
* 纬度
*/
@ExcelProperty(value = "纬度")
private String lat;
/**
* 仓库面积
*/
@ExcelProperty(value = "仓库面积")
private Long area;
/**
* 存放容量
*/
@ExcelProperty(value = "存放容量")
private Long capacity;
/**
* 负责人
*/
@ExcelProperty(value = "负责人")
private String manager;
/**
* 负责人电话
*/
@ExcelProperty(value = "负责人电话")
private String managerPhone;
/**
* 备注
*/
@ExcelProperty(value = "备注")
private String remark;
}

View File

@ -69,6 +69,11 @@ public class MatMaterialIssueVo implements Serializable {
*/ */
private String issueUnit; private String issueUnit;
/**
* 领用人
*/
private String shipper;
/** /**
* 保管单位 * 保管单位
*/ */

View File

@ -1,13 +1,11 @@
package org.dromara.materials.domain.vo.materials; package org.dromara.materials.domain.vo.materials;
import lombok.Data; import lombok.Data;
import org.dromara.materials.domain.vo.materialsinventory.MatMaterialsInventoryOutVo;
import java.io.Serial; import java.io.Serial;
import java.io.Serializable; import java.io.Serializable;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.util.Date; import java.util.Date;
import java.util.List;
/** /**
* @author lilemy * @author lilemy
@ -48,9 +46,4 @@ public class MatMaterialsNumberVo implements Serializable {
* 创建时间 * 创建时间
*/ */
private Date createTime; private Date createTime;
/**
* 出库列表
*/
private List<MatMaterialsInventoryOutVo> outList;
} }

View File

@ -28,6 +28,16 @@ public class MatMaterialsUseDetailVo implements Serializable {
*/ */
private String materialsName; private String materialsName;
/**
* 仓库id
*/
private Long warehouseId;
/**
* 仓库名称
*/
private String warehouseName;
/** /**
* 计划数量 * 计划数量
*/ */

View File

@ -47,6 +47,11 @@ public class MatMaterialsVo implements Serializable {
@ExcelProperty(value = "公司id") @ExcelProperty(value = "公司id")
private Long companyId; private Long companyId;
/**
* 仓库id
*/
private Long warehouseId;
/** /**
* 公司信息 * 公司信息
*/ */

View File

@ -0,0 +1,15 @@
package org.dromara.materials.mapper;
import org.dromara.materials.domain.MatWarehouse;
import org.dromara.materials.domain.vo.MatWarehouseVo;
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
/**
* 物资仓库Mapper接口
*
* @author lilemy
* @date 2025-10-29
*/
public interface MatWarehouseMapper extends BaseMapperPlus<MatWarehouse, MatWarehouseVo> {
}

View File

@ -8,6 +8,7 @@ import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.materials.domain.MatMaterials; import org.dromara.materials.domain.MatMaterials;
import org.dromara.materials.domain.MatMaterialsInventory; import org.dromara.materials.domain.MatMaterialsInventory;
import org.dromara.materials.domain.MatMaterialsUseRecord; import org.dromara.materials.domain.MatMaterialsUseRecord;
import org.dromara.materials.domain.MatWarehouse;
import org.dromara.materials.domain.dto.materialreceiveitem.MatMaterialReceiveItemDto; import org.dromara.materials.domain.dto.materialreceiveitem.MatMaterialReceiveItemDto;
import org.dromara.materials.domain.dto.materials.MatMaterialsCreateReq; import org.dromara.materials.domain.dto.materials.MatMaterialsCreateReq;
import org.dromara.materials.domain.dto.materials.MatMaterialsGisReq; import org.dromara.materials.domain.dto.materials.MatMaterialsGisReq;
@ -117,11 +118,8 @@ public interface IMatMaterialsService extends IService<MatMaterials> {
* @param supplierUnit 供应商单位 * @param supplierUnit 供应商单位
* @param nickname 操作用户昵称 * @param nickname 操作用户昵称
*/ */
void create(Long projectId, void create(Long projectId, List<MatMaterialReceiveItemDto> itemList,
List<MatMaterialReceiveItemDto> itemList, String formCode, String supplierUnit, String nickname
String formCode,
String supplierUnit,
String nickname
); );
/** /**
@ -169,16 +167,18 @@ public interface IMatMaterialsService extends IService<MatMaterials> {
/** /**
* 获取材料使用详情列表 * 获取材料使用详情列表
* *
* @param materials 材料 * @param materials 材料
* @param putList 材料入库列表 * @param putList 材料入库列表
* @param outList 材料出库列表 * @param outList 材料出库列表
* @param useList 材料使用列表 * @param useList 材料使用列表
* @param warehouseList 仓库列表
* @return 材料使用详情列表 * @return 材料使用详情列表
*/ */
List<MatMaterialsUseDetailVo> getUseDetailList(List<MatMaterials> materials, List<MatMaterialsUseDetailVo> getUseDetailList(List<MatMaterials> materials,
List<MatMaterialsInventory> putList, List<MatMaterialsInventory> putList,
List<MatMaterialsInventory> outList, List<MatMaterialsInventory> outList,
List<MatMaterialsUseRecord> useList); List<MatMaterialsUseRecord> useList,
List<MatWarehouse> warehouseList);
/** /**
* 获取材料库存数据列表 * 获取材料库存数据列表

View File

@ -0,0 +1,70 @@
package org.dromara.materials.service;
import com.baomidou.mybatisplus.extension.service.IService;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.materials.domain.MatWarehouse;
import org.dromara.materials.domain.bo.MatWarehouseBo;
import org.dromara.materials.domain.vo.MatWarehouseVo;
import java.util.Collection;
import java.util.List;
/**
* 物资仓库Service接口
*
* @author lilemy
* @date 2025-10-29
*/
public interface IMatWarehouseService extends IService<MatWarehouse> {
/**
* 查询物资仓库
*
* @param id 主键
* @return 物资仓库
*/
MatWarehouseVo queryById(Long id);
/**
* 分页查询物资仓库列表
*
* @param bo 查询条件
* @param pageQuery 分页参数
* @return 物资仓库分页列表
*/
TableDataInfo<MatWarehouseVo> queryPageList(MatWarehouseBo bo, PageQuery pageQuery);
/**
* 查询符合条件的物资仓库列表
*
* @param bo 查询条件
* @return 物资仓库列表
*/
List<MatWarehouseVo> queryList(MatWarehouseBo bo);
/**
* 新增物资仓库
*
* @param bo 物资仓库
* @return 是否新增成功
*/
Boolean insertByBo(MatWarehouseBo bo);
/**
* 修改物资仓库
*
* @param bo 物资仓库
* @return 是否修改成功
*/
Boolean updateByBo(MatWarehouseBo bo);
/**
* 校验并批量删除物资仓库信息
*
* @param ids 待删除的主键集合
* @param isValid 是否进行有效性校验
* @return 是否删除成功
*/
Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
}

View File

@ -23,6 +23,7 @@ import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.oss.core.OssClient; import org.dromara.common.oss.core.OssClient;
import org.dromara.common.oss.exception.OssException; import org.dromara.common.oss.exception.OssException;
import org.dromara.common.oss.factory.OssFactory; import org.dromara.common.oss.factory.OssFactory;
import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.common.utils.DocumentUtil; import org.dromara.common.utils.DocumentUtil;
import org.dromara.materials.constants.MatMaterialsConstant; import org.dromara.materials.constants.MatMaterialsConstant;
import org.dromara.materials.domain.MatMaterialIssue; import org.dromara.materials.domain.MatMaterialIssue;
@ -35,6 +36,7 @@ import org.dromara.materials.domain.dto.materialissue.MatMaterialIssueUpdateReq;
import org.dromara.materials.domain.dto.materialissue.MatMaterialIssueWordDto; import org.dromara.materials.domain.dto.materialissue.MatMaterialIssueWordDto;
import org.dromara.materials.domain.dto.materialissueitem.MatMaterialIssueItemDto; import org.dromara.materials.domain.dto.materialissueitem.MatMaterialIssueItemDto;
import org.dromara.materials.domain.dto.materialissueitem.MatMaterialIssueItemWordDto; import org.dromara.materials.domain.dto.materialissueitem.MatMaterialIssueItemWordDto;
import org.dromara.materials.domain.enums.MatMaterialsInventoryOutPutEnum;
import org.dromara.materials.domain.vo.materialissue.MatMaterialIssueVo; import org.dromara.materials.domain.vo.materialissue.MatMaterialIssueVo;
import org.dromara.materials.domain.vo.materialsinventory.MatMaterialsInventoryListVo; import org.dromara.materials.domain.vo.materialsinventory.MatMaterialsInventoryListVo;
import org.dromara.materials.mapper.MatMaterialIssueMapper; import org.dromara.materials.mapper.MatMaterialIssueMapper;
@ -238,14 +240,12 @@ public class MatMaterialIssueServiceImpl extends ServiceImpl<MatMaterialIssueMap
@Override @Override
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public Boolean insertByBo(MatMaterialIssueCreateReq req) { public Boolean insertByBo(MatMaterialIssueCreateReq req) {
String nickname = Objects.requireNonNull(LoginHelper.getLoginUser()).getNickname();
MatMaterialIssue materialIssue = new MatMaterialIssue(); MatMaterialIssue materialIssue = new MatMaterialIssue();
BeanUtils.copyProperties(req, materialIssue); BeanUtils.copyProperties(req, materialIssue);
validEntityBeforeSave(materialIssue, true); validEntityBeforeSave(materialIssue);
getFileSize(materialIssue, getFileSize(materialIssue, req.getLicenseCountFileId(),
req.getLicenseCountFileId(), req.getReportCountFileId(), req.getTechDocCountFileId(), req.getCertCountFileId());
req.getReportCountFileId(),
req.getTechDocCountFileId(),
req.getCertCountFileId());
boolean save = this.save(materialIssue); boolean save = this.save(materialIssue);
if (!save) { if (!save) {
throw new ServiceException("物料领料单新增失败", HttpStatus.ERROR); throw new ServiceException("物料领料单新增失败", HttpStatus.ERROR);
@ -263,15 +263,17 @@ public class MatMaterialIssueServiceImpl extends ServiceImpl<MatMaterialIssueMap
if (!result) { if (!result) {
throw new ServiceException("物料领料单明细项新增失败", HttpStatus.ERROR); throw new ServiceException("物料领料单明细项新增失败", HttpStatus.ERROR);
} }
/* // 创建设备材料出库记录 // 创建设备材料出库记录
List<MatMaterialsInventory> inventoryList = itemList.stream().map(item -> { List<MatMaterialsInventory> inventoryList = itemList.stream().map(item -> {
MatMaterialsInventory inventory = new MatMaterialsInventory(); MatMaterialsInventory inventory = new MatMaterialsInventory();
inventory.setNumber(item.getIssuedQuantity().longValue()); inventory.setNumber(item.getIssuedQuantity().longValue());
inventory.setOutPutTime(new Date()); inventory.setOutPutTime(new Date());
inventory.setResidue(item.getRemainingQuantity().longValue()); inventory.setResidue(item.getRemainingQuantity().longValue());
inventory.setOperator(nickname); inventory.setOperator(nickname);
inventory.setOperatorId(LoginHelper.getUserId());
inventory.setRecipient(materialIssue.getIssueUnit()); inventory.setRecipient(materialIssue.getIssueUnit());
inventory.setShipper(nickname); inventory.setRecipientId(materialIssue.getIssueUnitId());
inventory.setShipper(materialIssue.getShipper());
inventory.setMaterialsId(item.getMaterialsId()); inventory.setMaterialsId(item.getMaterialsId());
inventory.setProjectId(materialIssue.getProjectId()); inventory.setProjectId(materialIssue.getProjectId());
inventory.setOutPut(MatMaterialsInventoryOutPutEnum.OUT.getValue()); inventory.setOutPut(MatMaterialsInventoryOutPutEnum.OUT.getValue());
@ -281,7 +283,7 @@ public class MatMaterialIssueServiceImpl extends ServiceImpl<MatMaterialIssueMap
boolean saved = materialsInventoryService.saveBatch(inventoryList); boolean saved = materialsInventoryService.saveBatch(inventoryList);
if (!saved) { if (!saved) {
throw new ServiceException("物料出库记录新增失败", HttpStatus.ERROR); throw new ServiceException("物料出库记录新增失败", HttpStatus.ERROR);
}*/ }
} }
return true; return true;
} }
@ -302,7 +304,7 @@ public class MatMaterialIssueServiceImpl extends ServiceImpl<MatMaterialIssueMap
} }
MatMaterialIssue materialIssue = new MatMaterialIssue(); MatMaterialIssue materialIssue = new MatMaterialIssue();
BeanUtils.copyProperties(req, materialIssue); BeanUtils.copyProperties(req, materialIssue);
validEntityBeforeSave(materialIssue, false); validEntityBeforeSave(materialIssue);
getFileSize(materialIssue, getFileSize(materialIssue,
req.getLicenseCountFileId(), req.getLicenseCountFileId(),
req.getReportCountFileId(), req.getReportCountFileId(),
@ -341,18 +343,9 @@ public class MatMaterialIssueServiceImpl extends ServiceImpl<MatMaterialIssueMap
/** /**
* 保存前的数据校验 * 保存前的数据校验
*/ */
private void validEntityBeforeSave(MatMaterialIssue entity, Boolean create) { private void validEntityBeforeSave(MatMaterialIssue entity) {
// 做一些数据校验,如唯一约束 // 做一些数据校验,如唯一约束
Long projectId = entity.getProjectId(); Long projectId = entity.getProjectId();
String materialSource = entity.getMaterialSource();
if (create) {
if (projectId == null) {
throw new ServiceException("项目 id 不能为空", HttpStatus.BAD_REQUEST);
}
if (StringUtils.isEmpty(materialSource)) {
throw new ServiceException("物料来源不能为空", HttpStatus.BAD_REQUEST);
}
}
// 查询项目是否存在 // 查询项目是否存在
if (projectId != null && projectService.getById(projectId) == null) { if (projectId != null && projectService.getById(projectId) == null) {
throw new ServiceException("对应项目不存在", HttpStatus.NOT_FOUND); throw new ServiceException("对应项目不存在", HttpStatus.NOT_FOUND);

View File

@ -26,16 +26,20 @@ import org.dromara.common.utils.DocumentUtil;
import org.dromara.materials.constants.MatMaterialsConstant; import org.dromara.materials.constants.MatMaterialsConstant;
import org.dromara.materials.domain.MatMaterialReceive; import org.dromara.materials.domain.MatMaterialReceive;
import org.dromara.materials.domain.MatMaterialReceiveItem; import org.dromara.materials.domain.MatMaterialReceiveItem;
import org.dromara.materials.domain.MatMaterials;
import org.dromara.materials.domain.MatMaterialsInventory;
import org.dromara.materials.domain.dto.materialreceive.MatMaterialReceiveCreateReq; import org.dromara.materials.domain.dto.materialreceive.MatMaterialReceiveCreateReq;
import org.dromara.materials.domain.dto.materialreceive.MatMaterialReceiveQueryReq; import org.dromara.materials.domain.dto.materialreceive.MatMaterialReceiveQueryReq;
import org.dromara.materials.domain.dto.materialreceive.MatMaterialReceiveUpdateReq; import org.dromara.materials.domain.dto.materialreceive.MatMaterialReceiveUpdateReq;
import org.dromara.materials.domain.dto.materialreceive.MatMaterialReceiveWordDto; import org.dromara.materials.domain.dto.materialreceive.MatMaterialReceiveWordDto;
import org.dromara.materials.domain.dto.materialreceiveitem.MatMaterialReceiveItemDto; import org.dromara.materials.domain.dto.materialreceiveitem.MatMaterialReceiveItemDto;
import org.dromara.materials.domain.dto.materialreceiveitem.MatMaterialReceiveItemWordDto; import org.dromara.materials.domain.dto.materialreceiveitem.MatMaterialReceiveItemWordDto;
import org.dromara.materials.domain.enums.MatMaterialsInventoryOutPutEnum;
import org.dromara.materials.domain.vo.materialreceive.MatMaterialReceiveVo; import org.dromara.materials.domain.vo.materialreceive.MatMaterialReceiveVo;
import org.dromara.materials.mapper.MatMaterialReceiveMapper; import org.dromara.materials.mapper.MatMaterialReceiveMapper;
import org.dromara.materials.service.IMatMaterialReceiveItemService; import org.dromara.materials.service.IMatMaterialReceiveItemService;
import org.dromara.materials.service.IMatMaterialReceiveService; import org.dromara.materials.service.IMatMaterialReceiveService;
import org.dromara.materials.service.IMatMaterialsInventoryService;
import org.dromara.materials.service.IMatMaterialsService; import org.dromara.materials.service.IMatMaterialsService;
import org.dromara.project.service.IBusProjectService; import org.dromara.project.service.IBusProjectService;
import org.dromara.system.domain.vo.SysOssVo; import org.dromara.system.domain.vo.SysOssVo;
@ -53,6 +57,7 @@ import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.util.*; import java.util.*;
import java.util.stream.Collectors;
import java.util.zip.ZipOutputStream; import java.util.zip.ZipOutputStream;
/** /**
@ -81,6 +86,9 @@ public class MatMaterialReceiveServiceImpl extends ServiceImpl<MatMaterialReceiv
@Resource @Resource
private IMatMaterialsService materialsService; private IMatMaterialsService materialsService;
@Resource
private IMatMaterialsInventoryService materialsInventoryService;
/** /**
* 查询物料接收单 * 查询物料接收单
* *
@ -233,7 +241,7 @@ public class MatMaterialReceiveServiceImpl extends ServiceImpl<MatMaterialReceiv
@Override @Override
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public Boolean insertByBo(MatMaterialReceiveCreateReq req) { public Boolean insertByBo(MatMaterialReceiveCreateReq req) {
String nickname = LoginHelper.getLoginUser().getNickname(); String nickname = Objects.requireNonNull(LoginHelper.getLoginUser()).getNickname();
MatMaterialReceive materialReceive = new MatMaterialReceive(); MatMaterialReceive materialReceive = new MatMaterialReceive();
BeanUtils.copyProperties(req, materialReceive); BeanUtils.copyProperties(req, materialReceive);
validEntityBeforeSave(materialReceive, true); validEntityBeforeSave(materialReceive, true);
@ -274,11 +282,8 @@ public class MatMaterialReceiveServiceImpl extends ServiceImpl<MatMaterialReceiv
} }
//生成库存 //生成库存
materialsService.create( materialsService.create(
materialReceive.getProjectId(), materialReceive.getProjectId(), itemList, materialReceive.getFormCode(),
itemList, materialReceive.getSupplierUnit(), nickname
materialReceive.getFormCode(),
materialReceive.getSupplierUnit(),
nickname
); );
} }
return true; return true;
@ -366,12 +371,37 @@ public class MatMaterialReceiveServiceImpl extends ServiceImpl<MatMaterialReceiv
@Override @Override
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public Boolean deleteByIds(Collection<Long> ids) { public Boolean deleteByIds(Collection<Long> ids) {
List<MatMaterialReceive> receiveList = this.listByIds(ids);
List<MatMaterialReceiveItem> itemList = materialReceiveItemService.lambdaQuery() List<MatMaterialReceiveItem> itemList = materialReceiveItemService.lambdaQuery()
.in(MatMaterialReceiveItem::getReceiveId, ids) .in(MatMaterialReceiveItem::getReceiveId, ids)
.list(); .list();
if (CollUtil.isNotEmpty(itemList)) { if (CollUtil.isNotEmpty(itemList)) {
materialReceiveItemService.removeBatchByIds(itemList); materialReceiveItemService.removeBatchByIds(itemList);
} }
// 关联删除材料数据
Set<String> formCode = receiveList.stream().map(MatMaterialReceive::getFormCode).collect(Collectors.toSet());
List<MatMaterials> materials = materialsService.lambdaQuery()
.in(MatMaterials::getFormCode, formCode)
.list();
if (CollUtil.isNotEmpty(materials)) {
for (MatMaterials material : materials) {
List<MatMaterialsInventory> inventoryList = materialsInventoryService.lambdaQuery()
.eq(MatMaterialsInventory::getMaterialsId, material.getId())
.eq(MatMaterialsInventory::getOutPut, MatMaterialsInventoryOutPutEnum.OUT.getValue())
.list();
if (CollUtil.isNotEmpty(inventoryList)) {
throw new ServiceException("存在关联的库存数据,无法删除", HttpStatus.BAD_REQUEST);
}
Set<Long> materialIds = materials.stream().map(MatMaterials::getId).collect(Collectors.toSet());
List<MatMaterialsInventory> allInventoryList = materialsInventoryService.lambdaQuery()
.in(MatMaterialsInventory::getMaterialsId, materialIds)
.list();
if (CollUtil.isNotEmpty(allInventoryList)) {
materialsInventoryService.removeBatchByIds(allInventoryList);
}
}
materialsService.removeBatchByIds(materials);
}
return this.removeBatchByIds(ids); return this.removeBatchByIds(ids);
} }
@ -484,7 +514,7 @@ public class MatMaterialReceiveServiceImpl extends ServiceImpl<MatMaterialReceiv
* @param reportCountFileId 报表文件id * @param reportCountFileId 报表文件id
* @param techDocCountFileId 技术文档文件id * @param techDocCountFileId 技术文档文件id
* @param certCountFileId 证书文件id * @param certCountFileId 证书文件id
* @param attachmentId * @param attachmentId 附件id
*/ */
private void getFileSize(MatMaterialReceive materialReceive, String licenseCountFileId, private void getFileSize(MatMaterialReceive materialReceive, String licenseCountFileId,
String reportCountFileId, String techDocCountFileId, String certCountFileId, String attachmentId) { String reportCountFileId, String techDocCountFileId, String certCountFileId, String attachmentId) {

View File

@ -1,7 +1,6 @@
package org.dromara.materials.service.impl; package org.dromara.materials.service.impl;
import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.json.JSONObject; import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil; import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
@ -18,10 +17,7 @@ import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.json.utils.JsonUtils; import org.dromara.common.json.utils.JsonUtils;
import org.dromara.common.mybatis.core.page.PageQuery; import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo; import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.materials.domain.MatMaterialReceive; import org.dromara.materials.domain.*;
import org.dromara.materials.domain.MatMaterials;
import org.dromara.materials.domain.MatMaterialsInventory;
import org.dromara.materials.domain.MatMaterialsUseRecord;
import org.dromara.materials.domain.dto.materialreceiveitem.MatMaterialReceiveItemDto; import org.dromara.materials.domain.dto.materialreceiveitem.MatMaterialReceiveItemDto;
import org.dromara.materials.domain.dto.materials.MatMaterialsCreateReq; import org.dromara.materials.domain.dto.materials.MatMaterialsCreateReq;
import org.dromara.materials.domain.dto.materials.MatMaterialsGisReq; import org.dromara.materials.domain.dto.materials.MatMaterialsGisReq;
@ -29,10 +25,8 @@ import org.dromara.materials.domain.dto.materials.MatMaterialsQueryReq;
import org.dromara.materials.domain.dto.materials.MatMaterialsUpdateReq; import org.dromara.materials.domain.dto.materials.MatMaterialsUpdateReq;
import org.dromara.materials.domain.dto.materialsinventory.MatMaterialsInventoryCreateReq; import org.dromara.materials.domain.dto.materialsinventory.MatMaterialsInventoryCreateReq;
import org.dromara.materials.domain.enums.MatMaterialsInventoryOutPutEnum; import org.dromara.materials.domain.enums.MatMaterialsInventoryOutPutEnum;
import org.dromara.materials.domain.enums.MatMaterialsInventoryReceiveStatusEnum;
import org.dromara.materials.domain.vo.materials.*; import org.dromara.materials.domain.vo.materials.*;
import org.dromara.materials.domain.vo.materialsinventory.MatMaterialsInventoryOutUseVo; import org.dromara.materials.domain.vo.materialsinventory.MatMaterialsInventoryOutUseVo;
import org.dromara.materials.domain.vo.materialsinventory.MatMaterialsInventoryOutVo;
import org.dromara.materials.domain.vo.materialsinventory.MatMaterialsInventoryVo; import org.dromara.materials.domain.vo.materialsinventory.MatMaterialsInventoryVo;
import org.dromara.materials.domain.vo.materialsuserecord.MatMaterialsUseRecordByOutVo; import org.dromara.materials.domain.vo.materialsuserecord.MatMaterialsUseRecordByOutVo;
import org.dromara.materials.mapper.MatMaterialsMapper; import org.dromara.materials.mapper.MatMaterialsMapper;
@ -81,6 +75,9 @@ public class MatMaterialsServiceImpl extends ServiceImpl<MatMaterialsMapper, Mat
@Resource @Resource
private IMatCompanyService companyService; private IMatCompanyService companyService;
@Resource
private IMatWarehouseService warehouseService;
/** /**
* 查询材料名称 * 查询材料名称
@ -317,6 +314,7 @@ public class MatMaterialsServiceImpl extends ServiceImpl<MatMaterialsMapper, Mat
// 从对象中取值 // 从对象中取值
String materialsName = req.getMaterialsName(); String materialsName = req.getMaterialsName();
Long companyId = req.getCompanyId(); Long companyId = req.getCompanyId();
Long warehouseId = req.getWarehouseId();
Long projectId = req.getProjectId(); Long projectId = req.getProjectId();
String typeSpecificationName = req.getTypeSpecificationName(); String typeSpecificationName = req.getTypeSpecificationName();
String weightId = req.getWeightId(); String weightId = req.getWeightId();
@ -331,7 +329,7 @@ public class MatMaterialsServiceImpl extends ServiceImpl<MatMaterialsMapper, Mat
lqw.eq(ObjectUtils.isNotEmpty(status), MatMaterials::getStatus, status); lqw.eq(ObjectUtils.isNotEmpty(status), MatMaterials::getStatus, status);
lqw.eq(ObjectUtils.isNotEmpty(projectId), MatMaterials::getProjectId, projectId); lqw.eq(ObjectUtils.isNotEmpty(projectId), MatMaterials::getProjectId, projectId);
lqw.eq(ObjectUtils.isNotEmpty(companyId), MatMaterials::getCompanyId, companyId); lqw.eq(ObjectUtils.isNotEmpty(companyId), MatMaterials::getCompanyId, companyId);
lqw.eq(ObjectUtils.isNotEmpty(warehouseId), MatMaterials::getWarehouseId, warehouseId);
lqw.orderByDesc(MatMaterials::getCreateTime); lqw.orderByDesc(MatMaterials::getCreateTime);
return lqw; return lqw;
} }
@ -368,9 +366,10 @@ public class MatMaterialsServiceImpl extends ServiceImpl<MatMaterialsMapper, Mat
*/ */
@Async @Async
@Override @Override
public void create(Long projectId, List<MatMaterialReceiveItemDto> itemList, String formCode, String supplierUnit, String nickname) { public void create(Long projectId, List<MatMaterialReceiveItemDto> itemList,
String formCode, String supplierUnit, String nickname) {
for (MatMaterialReceiveItemDto item : itemList) { for (MatMaterialReceiveItemDto item : itemList) {
if(item.getAcceptedQuantity().compareTo(BigDecimal.ZERO) <= 0){ if (item.getAcceptedQuantity().compareTo(BigDecimal.ZERO) <= 0) {
continue; continue;
} }
MatMaterials matMaterials = new MatMaterials(); MatMaterials matMaterials = new MatMaterials();
@ -380,6 +379,7 @@ public class MatMaterialsServiceImpl extends ServiceImpl<MatMaterialsMapper, Mat
matMaterials.setWeightId(item.getUnit()); matMaterials.setWeightId(item.getUnit());
matMaterials.setQuantityCount(item.getQuantity().toString()); matMaterials.setQuantityCount(item.getQuantity().toString());
matMaterials.setFormCode(formCode); matMaterials.setFormCode(formCode);
matMaterials.setWarehouseId(item.getWarehouseId());
save(matMaterials); save(matMaterials);
Long materialsId = matMaterials.getId(); Long materialsId = matMaterials.getId();
MatMaterialsInventoryCreateReq req = new MatMaterialsInventoryCreateReq(); MatMaterialsInventoryCreateReq req = new MatMaterialsInventoryCreateReq();
@ -448,18 +448,6 @@ public class MatMaterialsServiceImpl extends ServiceImpl<MatMaterialsMapper, Mat
.selectLatestByMaterialIds(materialIds) .selectLatestByMaterialIds(materialIds)
.stream() .stream()
.collect(Collectors.toMap(MatMaterialsInventory::getMaterialsId, Function.identity(), (a, b) -> a)); .collect(Collectors.toMap(MatMaterialsInventory::getMaterialsId, Function.identity(), (a, b) -> a));
// 查询出库记录
List<MatMaterialsInventory> outList = materialsInventoryService.lambdaQuery()
.in(MatMaterialsInventory::getMaterialsId, materialIds)
.eq(MatMaterialsInventory::getOutPut, MatMaterialsInventoryOutPutEnum.OUT.getValue())
.eq(MatMaterialsInventory::getIsReceive, MatMaterialsInventoryReceiveStatusEnum.NOT.getValue())
.list();
Map<Long, List<MatMaterialsInventory>> outMap = new HashMap<>();
if (CollUtil.isNotEmpty(outList)) {
outMap = outList.stream()
.collect(Collectors.groupingBy(MatMaterialsInventory::getMaterialsId));
}
Map<Long, List<MatMaterialsInventory>> finalOutMap = outMap;
// 按 formCode 分组 // 按 formCode 分组
Map<String, List<MatMaterials>> formCodeMap = materials.stream() Map<String, List<MatMaterials>> formCodeMap = materials.stream()
.collect(Collectors.groupingBy(MatMaterials::getFormCode)); .collect(Collectors.groupingBy(MatMaterials::getFormCode));
@ -474,11 +462,18 @@ public class MatMaterialsServiceImpl extends ServiceImpl<MatMaterialsMapper, Mat
for (Map.Entry<String, List<MatMaterials>> entry : formCodeMap.entrySet()) { for (Map.Entry<String, List<MatMaterials>> entry : formCodeMap.entrySet()) {
String formCode = entry.getKey(); String formCode = entry.getKey();
// 过滤库存为 0 的材料 // 过滤库存为 0 的材料
List<MatMaterials> validMaterials = entry.getValue().stream() List<MatMaterialsNumberVo> validMaterials = entry.getValue().stream().map(m -> {
.filter(m -> { MatMaterialsNumberVo numberVo = new MatMaterialsNumberVo();
MatMaterialsInventory inv = inventoryMap.get(m.getId()); MatMaterialsInventory inv = inventoryMap.get(m.getId());
return inv != null && inv.getResidue() > 0; if (inv == null) {
return null;
}
BeanUtils.copyProperties(m, numberVo);
numberVo.setInventoryNumber(BigDecimal.valueOf(inv.getResidue()));
return numberVo;
}) })
.filter(Objects::nonNull)
.filter(m -> m.getInventoryNumber().compareTo(BigDecimal.ZERO) > 0)
.toList(); .toList();
if (CollUtil.isEmpty(validMaterials)) { if (CollUtil.isEmpty(validMaterials)) {
continue; continue;
@ -490,29 +485,7 @@ public class MatMaterialsServiceImpl extends ServiceImpl<MatMaterialsMapper, Mat
if (receive != null) { if (receive != null) {
BeanUtils.copyProperties(receive, vo); BeanUtils.copyProperties(receive, vo);
} }
vo.setMaterials(validMaterials);
List<MatMaterialsNumberVo> numberVos = new ArrayList<>();
for (MatMaterials m : validMaterials) {
MatMaterialsNumberVo numberVo = new MatMaterialsNumberVo();
BeanUtils.copyProperties(m, numberVo);
MatMaterialsInventory inv = inventoryMap.get(m.getId());
if (inv != null) {
numberVo.setInventoryNumber(BigDecimal.valueOf(inv.getNumber()));
}
if (CollUtil.isNotEmpty(finalOutMap) && finalOutMap.containsKey(m.getId())) {
List<MatMaterialsInventory> outs = finalOutMap.get(m.getId());
numberVo.setOutList(outs.stream().map(out -> {
MatMaterialsInventoryOutVo outVo = new MatMaterialsInventoryOutVo();
BeanUtils.copyProperties(out, outVo);
return outVo;
}).toList());
}
if(CollectionUtil.isNotEmpty(numberVo.getOutList())){
numberVos.add(numberVo);
}
}
vo.setMaterials(numberVos);
resultList.add(vo); resultList.add(vo);
} }
return resultList; return resultList;
@ -641,7 +614,15 @@ public class MatMaterialsServiceImpl extends ServiceImpl<MatMaterialsMapper, Mat
.in(MatMaterialsUseRecord::getInventoryId, outIds) .in(MatMaterialsUseRecord::getInventoryId, outIds)
.list(); .list();
} }
List<MatMaterialsUseDetailVo> useDetailList = this.getUseDetailList(materialsList, putList, outList, useList); // 查询仓库列表
Set<Long> warehouseIds = materialsList.stream().map(MatMaterials::getWarehouseId).collect(Collectors.toSet());
List<MatWarehouse> warehouseList = new ArrayList<>();
if (CollUtil.isNotEmpty(warehouseIds)) {
warehouseList = warehouseService.lambdaQuery()
.in(MatWarehouse::getId, warehouseIds)
.list();
}
List<MatMaterialsUseDetailVo> useDetailList = this.getUseDetailList(materialsList, putList, outList, useList, warehouseList);
materialsVoPage.setRecords(useDetailList); materialsVoPage.setRecords(useDetailList);
return TableDataInfo.build(materialsVoPage); return TableDataInfo.build(materialsVoPage);
} }
@ -649,17 +630,19 @@ public class MatMaterialsServiceImpl extends ServiceImpl<MatMaterialsMapper, Mat
/** /**
* 获取材料使用详情列表 * 获取材料使用详情列表
* *
* @param materials 材料 * @param materials 材料
* @param putList 材料入库列表 * @param putList 材料入库列表
* @param outList 材料出库列表 * @param outList 材料出库列表
* @param useList 材料使用列表 * @param useList 材料使用列表
* @param warehouseList 仓库列表
* @return 材料使用详情列表 * @return 材料使用详情列表
*/ */
@Override @Override
public List<MatMaterialsUseDetailVo> getUseDetailList(List<MatMaterials> materials, public List<MatMaterialsUseDetailVo> getUseDetailList(List<MatMaterials> materials,
List<MatMaterialsInventory> putList, List<MatMaterialsInventory> putList,
List<MatMaterialsInventory> outList, List<MatMaterialsInventory> outList,
List<MatMaterialsUseRecord> useList) { List<MatMaterialsUseRecord> useList,
List<MatWarehouse> warehouseList) {
Map<Long, MatMaterialsInventory> putMap = putList.stream() Map<Long, MatMaterialsInventory> putMap = putList.stream()
.collect(Collectors.toMap(MatMaterialsInventory::getMaterialsId, inventory -> inventory, .collect(Collectors.toMap(MatMaterialsInventory::getMaterialsId, inventory -> inventory,
(existing, replacement) -> replacement)); (existing, replacement) -> replacement));
@ -668,11 +651,13 @@ public class MatMaterialsServiceImpl extends ServiceImpl<MatMaterialsMapper, Mat
BeanUtils.copyProperties(material, vo); BeanUtils.copyProperties(material, vo);
Long id = material.getId(); Long id = material.getId();
MatMaterialsInventory put = putMap.get(id); MatMaterialsInventory put = putMap.get(id);
vo.setSupplier(put.getRecipient()); if (put != null) {
vo.setInventoryId(put.getId()); vo.setSupplier(put.getRecipient());
vo.setNumber(put.getNumber()); vo.setInventoryId(put.getId());
vo.setOperator(put.getOperator()); vo.setNumber(put.getNumber());
vo.setEnterTime(put.getCreateTime()); vo.setOperator(put.getOperator());
vo.setEnterTime(put.getCreateTime());
}
if (CollUtil.isNotEmpty(outList)) { if (CollUtil.isNotEmpty(outList)) {
List<MatMaterialsInventory> outs = outList.stream() List<MatMaterialsInventory> outs = outList.stream()
.filter(inventory -> inventory.getMaterialsId().equals(id)) .filter(inventory -> inventory.getMaterialsId().equals(id))
@ -698,6 +683,15 @@ public class MatMaterialsServiceImpl extends ServiceImpl<MatMaterialsMapper, Mat
} }
} }
// 获取仓库名称
Long warehouseId = material.getWarehouseId();
if (CollUtil.isNotEmpty(warehouseList) && warehouseId != null) {
MatWarehouse warehouse = warehouseList.stream()
.filter(w -> w.getId().equals(warehouseId))
.findFirst()
.orElse(null);
vo.setWarehouseName(warehouse != null ? warehouse.getWarehouseName() : null);
}
return vo; return vo;
}).toList(); }).toList();
} }

View File

@ -0,0 +1,140 @@
package org.dromara.materials.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import org.dromara.common.core.utils.MapstructUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.materials.domain.MatWarehouse;
import org.dromara.materials.domain.bo.MatWarehouseBo;
import org.dromara.materials.domain.vo.MatWarehouseVo;
import org.dromara.materials.mapper.MatWarehouseMapper;
import org.dromara.materials.service.IMatWarehouseService;
import org.springframework.stereotype.Service;
import java.util.Collection;
import java.util.List;
import java.util.Map;
/**
* 物资仓库Service业务层处理
*
* @author lilemy
* @date 2025-10-29
*/
@RequiredArgsConstructor
@Service
public class MatWarehouseServiceImpl extends ServiceImpl<MatWarehouseMapper, MatWarehouse>
implements IMatWarehouseService {
/**
* 查询物资仓库
*
* @param id 主键
* @return 物资仓库
*/
@Override
public MatWarehouseVo queryById(Long id) {
return baseMapper.selectVoById(id);
}
/**
* 分页查询物资仓库列表
*
* @param bo 查询条件
* @param pageQuery 分页参数
* @return 物资仓库分页列表
*/
@Override
public TableDataInfo<MatWarehouseVo> queryPageList(MatWarehouseBo bo, PageQuery pageQuery) {
LambdaQueryWrapper<MatWarehouse> lqw = buildQueryWrapper(bo);
Page<MatWarehouseVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
return TableDataInfo.build(result);
}
/**
* 查询符合条件的物资仓库列表
*
* @param bo 查询条件
* @return 物资仓库列表
*/
@Override
public List<MatWarehouseVo> queryList(MatWarehouseBo bo) {
LambdaQueryWrapper<MatWarehouse> lqw = buildQueryWrapper(bo);
return baseMapper.selectVoList(lqw);
}
private LambdaQueryWrapper<MatWarehouse> buildQueryWrapper(MatWarehouseBo bo) {
Map<String, Object> params = bo.getParams();
LambdaQueryWrapper<MatWarehouse> lqw = Wrappers.lambdaQuery();
lqw.orderByDesc(MatWarehouse::getId);
lqw.eq(bo.getProjectId() != null, MatWarehouse::getProjectId, bo.getProjectId());
lqw.eq(StringUtils.isNotBlank(bo.getWarehouseCode()), MatWarehouse::getWarehouseCode, bo.getWarehouseCode());
lqw.like(StringUtils.isNotBlank(bo.getWarehouseName()), MatWarehouse::getWarehouseName, bo.getWarehouseName());
lqw.eq(StringUtils.isNotBlank(bo.getWarehouseType()), MatWarehouse::getWarehouseType, bo.getWarehouseType());
lqw.eq(StringUtils.isNotBlank(bo.getAddress()), MatWarehouse::getAddress, bo.getAddress());
lqw.eq(StringUtils.isNotBlank(bo.getLng()), MatWarehouse::getLng, bo.getLng());
lqw.eq(StringUtils.isNotBlank(bo.getLat()), MatWarehouse::getLat, bo.getLat());
lqw.eq(bo.getArea() != null, MatWarehouse::getArea, bo.getArea());
lqw.eq(bo.getCapacity() != null, MatWarehouse::getCapacity, bo.getCapacity());
lqw.eq(StringUtils.isNotBlank(bo.getManager()), MatWarehouse::getManager, bo.getManager());
lqw.eq(StringUtils.isNotBlank(bo.getManagerPhone()), MatWarehouse::getManagerPhone, bo.getManagerPhone());
return lqw;
}
/**
* 新增物资仓库
*
* @param bo 物资仓库
* @return 是否新增成功
*/
@Override
public Boolean insertByBo(MatWarehouseBo bo) {
MatWarehouse add = MapstructUtils.convert(bo, MatWarehouse.class);
validEntityBeforeSave(add);
boolean flag = baseMapper.insert(add) > 0;
if (flag) {
bo.setId(add.getId());
}
return flag;
}
/**
* 修改物资仓库
*
* @param bo 物资仓库
* @return 是否修改成功
*/
@Override
public Boolean updateByBo(MatWarehouseBo bo) {
MatWarehouse update = MapstructUtils.convert(bo, MatWarehouse.class);
validEntityBeforeSave(update);
return baseMapper.updateById(update) > 0;
}
/**
* 保存前的数据校验
*/
private void validEntityBeforeSave(MatWarehouse entity) {
//TODO 做一些数据校验,如唯一约束
}
/**
* 校验并批量删除物资仓库信息
*
* @param ids 待删除的主键集合
* @param isValid 是否进行有效性校验
* @return 是否删除成功
*/
@Override
public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
if (isValid) {
//TODO 做一些业务上的校验,判断是否需要校验
}
return baseMapper.deleteByIds(ids) > 0;
}
}

View File

@ -5,6 +5,8 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter; import org.springframework.web.socket.server.standard.ServerEndpointExporter;
import java.util.stream.Stream;
//@Configuration //@Configuration
public class WebSocketConfig { public class WebSocketConfig {

View File

@ -31,9 +31,11 @@ import org.dromara.other.domain.vo.ys7deviceimg.OthYs7DeviceImgVo;
import org.dromara.other.mapper.OthYs7DeviceImgMapper; import org.dromara.other.mapper.OthYs7DeviceImgMapper;
import org.dromara.other.service.IOthYs7DeviceImgService; import org.dromara.other.service.IOthYs7DeviceImgService;
import org.dromara.other.service.IOthYs7DeviceService; import org.dromara.other.service.IOthYs7DeviceService;
import org.dromara.safety.domain.HseViolationLevel;
import org.dromara.safety.domain.dto.recognizerecord.HseRecognizeRecordCreateDto; import org.dromara.safety.domain.dto.recognizerecord.HseRecognizeRecordCreateDto;
import org.dromara.safety.domain.enums.HseRecordCategoryEnum; import org.dromara.safety.domain.enums.HseRecordCategoryEnum;
import org.dromara.safety.service.IHseRecognizeRecordService; import org.dromara.safety.service.IHseRecognizeRecordService;
import org.dromara.safety.service.IHseViolationLevelService;
import org.dromara.system.domain.vo.SysOssUploadVo; import org.dromara.system.domain.vo.SysOssUploadVo;
import org.dromara.system.service.ISysOssService; import org.dromara.system.service.ISysOssService;
import org.springframework.beans.BeanUtils; import org.springframework.beans.BeanUtils;
@ -45,6 +47,7 @@ import java.time.LocalDate;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.ZoneId; import java.time.ZoneId;
import java.util.*; import java.util.*;
import java.util.stream.Collectors;
/** /**
* 萤石摄像头图片Service业务层处理 * 萤石摄像头图片Service业务层处理
@ -66,6 +69,9 @@ public class OthYs7DeviceImgServiceImpl extends ServiceImpl<OthYs7DeviceImgMappe
@Resource @Resource
private IHseRecognizeRecordService recognizeRecordService; private IHseRecognizeRecordService recognizeRecordService;
@Resource
private IHseViolationLevelService violationLevelService;
@Resource @Resource
private IOthYs7DeviceService ys7DeviceService; private IOthYs7DeviceService ys7DeviceService;
@ -206,6 +212,33 @@ public class OthYs7DeviceImgServiceImpl extends ServiceImpl<OthYs7DeviceImgMappe
public void saveCapturePic(List<OthYs7DeviceImgCreateByCapture> imgList) { public void saveCapturePic(List<OthYs7DeviceImgCreateByCapture> imgList) {
List<OthYs7DeviceImg> saveList = new ArrayList<>(); List<OthYs7DeviceImg> saveList = new ArrayList<>();
List<HseRecognizeRecordCreateDto> recordList = new ArrayList<>(); List<HseRecognizeRecordCreateDto> recordList = new ArrayList<>();
// 获取项目id
Set<Long> projectIds = imgList.stream().map(OthYs7DeviceImgCreateByCapture::getProjectId).collect(Collectors.toSet());
// 获取安全等级设置
List<HseViolationLevel> levelList = violationLevelService.lambdaQuery()
.in(HseViolationLevel::getProjectId, projectIds)
.list();
if (CollUtil.isEmpty(levelList)) {
log.error("未设置安全等级");
return;
}
Map<Long, List<RecognizerTypeEnum>> level = new HashMap<>();
Map<Long, List<HseViolationLevel>> levelMap = levelList.stream()
.collect(Collectors.groupingBy(HseViolationLevel::getProjectId));
for (Map.Entry<Long, List<HseViolationLevel>> entry : levelMap.entrySet()) {
List<RecognizerTypeEnum> recognizerTypeEnums = entry.getValue().stream().map(l -> {
List<String> levels = StringUtils.splitList(l.getViolationType());
return RecognizerTypeEnum.listFromCodes(levels);
}).filter(CollUtil::isNotEmpty)
.flatMap(Collection::stream)
.distinct()
.toList();
level.put(entry.getKey(), recognizerTypeEnums);
}
if (CollUtil.isEmpty(level)) {
log.error("未设置安全等级");
return;
}
for (OthYs7DeviceImgCreateByCapture img : imgList) { for (OthYs7DeviceImgCreateByCapture img : imgList) {
OthYs7DeviceImg othYs7DeviceImg = new OthYs7DeviceImg(); OthYs7DeviceImg othYs7DeviceImg = new OthYs7DeviceImg();
String url = img.getUrl(); String url = img.getUrl();
@ -220,17 +253,11 @@ public class OthYs7DeviceImgServiceImpl extends ServiceImpl<OthYs7DeviceImgMappe
othYs7DeviceImg.setDeviceName(img.getDeviceName()); othYs7DeviceImg.setDeviceName(img.getDeviceName());
othYs7DeviceImg.setUrl(ossUrl); othYs7DeviceImg.setUrl(ossUrl);
// 将抓取的图片进行识别 // 将抓取的图片进行识别
// List<RecognizerTypeEnum> recTypes = List.of(RecognizerTypeEnum.NO_EQUIPMENT, List<RecognizerTypeEnum> recTypes = level.get(img.getProjectId());
// RecognizerTypeEnum.NO_HELMET, if (CollUtil.isEmpty(recTypes)) {
// RecognizerTypeEnum.NO_EQUIPMENT, log.error("未设置安全等级");
// RecognizerTypeEnum.NO_VEST, continue;
// RecognizerTypeEnum.SMOKE, }
// RecognizerTypeEnum.FIRE);
List<RecognizerTypeEnum> recTypes = List.of(
RecognizerTypeEnum.COLUMN,
RecognizerTypeEnum.PANEL,
RecognizerTypeEnum.BRACKET,
RecognizerTypeEnum.HOLE);
RecognizeVo recognizeVo = null; RecognizeVo recognizeVo = null;
try { try {
recognizeVo = recognizerManager.recognize(ossUrl, recTypes); recognizeVo = recognizerManager.recognize(ossUrl, recTypes);

View File

@ -41,6 +41,12 @@ public class OutConstructionValueRangeVo implements Serializable {
@ExcelProperty(value = "项目ID") @ExcelProperty(value = "项目ID")
private Long projectId; private Long projectId;
/**
* 第N周
*/
@ExcelProperty(value = "第N周")
private int week;
/** /**
* 开始日期 * 开始日期
*/ */

View File

@ -31,15 +31,15 @@ import org.dromara.progress.service.IPgsProgressPlanDetailService;
import org.springframework.context.annotation.Lazy; import org.springframework.context.annotation.Lazy;
import org.springframework.context.event.EventListener; import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.time.format.DateTimeFormatter;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.math.RoundingMode; import java.math.RoundingMode;
import java.time.DayOfWeek; import java.time.DayOfWeek;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.temporal.TemporalAdjusters; import java.time.temporal.TemporalAdjusters;
import java.util.Collection; import java.time.temporal.WeekFields;
import java.util.List; import java.util.*;
import java.util.Map;
/** /**
* 施工产值范围Service业务层处理 * 施工产值范围Service业务层处理
@ -61,6 +61,22 @@ public class OutConstructionValueRangeServiceImpl extends ServiceImpl<OutConstru
@Resource @Resource
private IPgsProgressPlanDetailService progressPlanDetailService; private IPgsProgressPlanDetailService progressPlanDetailService;
/**
* 获取指定日期所在的周数(根据中国周规则)
*
* @param dateStr 日期字符串格式yyyy-MM-dd
* @return 指定日期所在的周数1-52
*/
public Integer getCurrentWeekOfYear(String dateStr) {
// 解析为 LocalDate可根据需要调整日期格式
LocalDate specifiedDate = LocalDate.parse(dateStr, DateTimeFormatter.ISO_LOCAL_DATE);
// 2. 定义周规则(可选,默认使用系统区域,也可指定)
// 示例2中国规则周一为第一天第一周包含1月1日即可
WeekFields chinaWeekFields = WeekFields.of(Locale.CHINA);
// 3. 获取指定时间在当年的周数(根据需要选择周规则)
return specifiedDate.get(chinaWeekFields.weekOfYear());
}
/** /**
* 查询施工产值范围 * 查询施工产值范围
* *
@ -87,6 +103,10 @@ public class OutConstructionValueRangeServiceImpl extends ServiceImpl<OutConstru
public TableDataInfo<OutConstructionValueRangeVo> queryPageList(OutConstructionValueRangeBo bo, PageQuery pageQuery) { public TableDataInfo<OutConstructionValueRangeVo> queryPageList(OutConstructionValueRangeBo bo, PageQuery pageQuery) {
LambdaQueryWrapper<OutConstructionValueRange> lqw = buildQueryWrapper(bo); LambdaQueryWrapper<OutConstructionValueRange> lqw = buildQueryWrapper(bo);
Page<OutConstructionValueRangeVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw); Page<OutConstructionValueRangeVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
//增加第N周
result.getRecords().forEach(item -> {
item.setWeek(getCurrentWeekOfYear(item.getStartDate().toString()));
});
return TableDataInfo.build(result); return TableDataInfo.build(result);
} }

View File

@ -15,6 +15,7 @@ import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.constant.HttpStatus; import org.dromara.common.core.constant.HttpStatus;
import org.dromara.common.core.domain.R; import org.dromara.common.core.domain.R;
import org.dromara.common.core.exception.ServiceException; import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.excel.core.DefaultExcelListener; import org.dromara.common.excel.core.DefaultExcelListener;
import org.dromara.common.excel.utils.ExcelUtil; import org.dromara.common.excel.utils.ExcelUtil;
import org.dromara.common.idempotent.annotation.RepeatSubmit; import org.dromara.common.idempotent.annotation.RepeatSubmit;
@ -39,6 +40,7 @@ import java.math.RoundingMode;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors; import java.util.stream.Collectors;
/** /**
@ -257,21 +259,30 @@ public class PgsProgressCategoryController extends BaseController {
List<PgsProgressCategoryVo> list = sheetListener.getExcelResult().getList(); List<PgsProgressCategoryVo> list = sheetListener.getExcelResult().getList();
List<PgsProgressCategoryVo> newList = list.stream().filter(vo -> vo.getId() == null).toList(); List<PgsProgressCategoryVo> newList = list.stream().filter(vo -> vo.getId() == null).toList();
List<PgsProgressCategoryVo> oldList = list.stream().filter(vo -> vo.getId() != null).toList(); List<PgsProgressCategoryVo> oldList = list.stream().filter(vo -> vo.getId() != null).toList();
// 将当前sheet的数据添加到总数据中 Set<Long> ids = oldList.stream().map(PgsProgressCategoryVo::getId).collect(Collectors.toSet());
allData.addAll(oldList); List<PgsProgressCategory> categoryList = pgsProgressCategoryService.listByIds(ids);
if (CollUtil.isNotEmpty(newList)) { // 筛选出关联设计图的数据
if (CollUtil.isNotEmpty(oldList)) { List<PgsProgressCategoryVo> oldListVo = oldList.stream().filter(vo -> {
PgsProgressCategoryVo first = oldList.getFirst(); PgsProgressCategory category = categoryList.stream().filter(c -> c.getId().equals(vo.getId())).findFirst().orElse(null);
PgsProgressCategory category = pgsProgressCategoryService.getById(first.getId()); if (category == null) {
newList.forEach(vo -> { return true;
vo.setParentId(category.getParentId());
vo.setProjectId(category.getProjectId());
vo.setMatrixId(category.getMatrixId());
vo.setAncestors(category.getAncestors());
vo.setRelevancyStructure(category.getRelevancyStructure());
});
allData.addAll(newList);
} }
String workType = category.getWorkType();
return StringUtils.isBlank(workType);
}).toList();
// 将当前sheet的数据添加到总数据中
allData.addAll(oldListVo);
if (CollUtil.isNotEmpty(newList) && CollUtil.isNotEmpty(oldList)) {
PgsProgressCategoryVo first = oldList.getFirst();
PgsProgressCategory category = pgsProgressCategoryService.getById(first.getId());
newList.forEach(vo -> {
vo.setParentId(category.getParentId());
vo.setProjectId(category.getProjectId());
vo.setMatrixId(category.getMatrixId());
vo.setAncestors(category.getAncestors());
vo.setRelevancyStructure(category.getRelevancyStructure());
});
allData.addAll(newList);
} }
} }

View File

@ -1,10 +1,13 @@
package org.dromara.project.controller; package org.dromara.project.controller;
import cn.dev33.satoken.annotation.SaIgnore; import cn.dev33.satoken.annotation.SaIgnore;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64; import org.apache.commons.codec.binary.Base64;
import org.dromara.common.core.domain.R;
import org.dromara.common.web.core.BaseController; import org.dromara.common.web.core.BaseController;
import org.dromara.project.domain.BusAttendanceMachine; import org.dromara.project.domain.BusAttendanceMachine;
import org.dromara.project.domain.dto.attendance.*; import org.dromara.project.domain.dto.attendance.*;
@ -13,10 +16,15 @@ import org.dromara.project.service.IBusAttendanceMachineService;
import org.dromara.project.service.IBusAttendanceService; import org.dromara.project.service.IBusAttendanceService;
import org.springframework.mock.web.MockMultipartFile; import org.springframework.mock.web.MockMultipartFile;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
@ -70,6 +78,7 @@ public class BusAttendanceDeviceController extends BaseController {
req.setUserId(userId); req.setUserId(userId);
req.setPunchTime(localDateTime); req.setPunchTime(localDateTime);
req.setSource("1"); req.setSource("1");
req.setSn(dto.getSn());
//打印req //打印req
log.info("请求参数:{}", req); log.info("请求参数:{}", req);
//base64转MultipartFile //base64转MultipartFile
@ -84,6 +93,43 @@ public class BusAttendanceDeviceController extends BaseController {
} }
} }
@PostMapping("/api/v1/recordvisit")
@SaIgnore
public DeviceResult recordvisit(@RequestBody DeviceUserInfo dto) {
try {
log.info("设备上报数据:{}", dto);
// 1. 将对象转为 JSON 字符串(带格式)
String json = JSONUtil.toJsonPrettyStr(dto);
log.info("JSON数据{}", json);
// 2. 构建保存路径(按日期区分文件)
String dateStr = DateUtil.format(DateUtil.date(), "yyyyMMdd");
String fileName = StrUtil.format("device_record_{}.txt", dateStr);
// 3. 路径:项目根目录 /logs/device-data/
File dir = FileUtil.mkdir("logs/device-data");
File file = FileUtil.file(dir, fileName);
// 4. 生成时间戳+分隔符
String timePrefix = DateUtil.format(DateUtil.date(), "yyyy-MM-dd HH:mm:ss");
String data = StrUtil.format(
"[{}]\n{}\n{}\n",
timePrefix,
json,
StrUtil.repeat("-", 80)
);
// 5. 追加到文件中
FileUtil.appendString(data, file, StandardCharsets.UTF_8);
log.info("✅ 设备上报数据已保存: {}", file.getAbsolutePath());
return new DeviceResult(0, "保存成功");
} catch (Exception e) {
log.error("❌ 保存设备数据失败", e);
return new DeviceResult(-2, "保存失败:" + e.getMessage());
}
}
public static MultipartFile convert(String base64String, String fileName) throws IOException { public static MultipartFile convert(String base64String, String fileName) throws IOException {
// 先进行URL解码如果是URL编码过的数据 // 先进行URL解码如果是URL编码过的数据

View File

@ -118,4 +118,9 @@ public class BusAttendance extends BaseEntity {
* 来源 * 来源
*/ */
private String source; private String source;
/**
* 设备序列号
*/
private String sn;
} }

View File

@ -53,4 +53,6 @@ public class BusAttendancePunchCardByFaceReq implements Serializable {
*/ */
private String source; private String source;
private String sn;
} }

View File

@ -0,0 +1,77 @@
package org.dromara.project.domain.dto.attendance;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import java.math.BigDecimal;
import java.util.Map;
@Data
public class DeviceUserInfo {
/**
* 设备序列号
*/
@JsonProperty("dev_sn")
private String devSn;
/**
* 用户编号
*/
@JsonProperty("user_number")
private String userNumber;
/**
* 用户姓名
*/
@JsonProperty("user_name")
private String userName;
@JsonProperty("user_photo")
private String userPhoto;
@JsonProperty("photo")
private String photo;
@JsonProperty("recog_time")
private Long recogTime;
@JsonProperty("user_birthday")
private Long userBirthday;
@JsonProperty("user_gender")
private String userGender;
@JsonProperty("user_ethnic")
private String userEthnic;
@JsonProperty("user_address")
private String userAddress;
@JsonProperty("user_issue")
private String userIssue;
@JsonProperty("user_valid_start")
private Long userValidStart;
@JsonProperty("user_valid_end")
private Long userValidEnd;
@JsonProperty("confidence")
private String confidence;
@JsonProperty("body_temperature")
private BigDecimal bodyTemperature;
@JsonProperty("reflectivity")
private Integer reflectivity;
@JsonProperty("room_temperature")
private BigDecimal roomTemperature;
@JsonProperty("health_code_color")
private String healthCodeColor;
@JsonProperty("health_code_info")
private Map<String, Object> healthCodeInfo;
}

View File

@ -333,10 +333,11 @@ public class BusAttendanceServiceImpl extends ServiceImpl<BusAttendanceMapper, B
} }
//打卡范围 //打卡范围
if (!checkInRange(req)) { if (!"1".equals(req.getSource())){
throw new ServiceException("打卡位置不在范围内", HttpStatus.ERROR); if (!checkInRange(req)) {
throw new ServiceException("打卡位置不在范围内", HttpStatus.ERROR);
}
} }
//用户信息校验 //用户信息校验
SubConstructionUser constructionUser = constructionUserService.getBySysUserId(userId); SubConstructionUser constructionUser = constructionUserService.getBySysUserId(userId);
if ("1".equals(constructionUser.getStatus())) { if ("1".equals(constructionUser.getStatus())) {
@ -360,7 +361,7 @@ public class BusAttendanceServiceImpl extends ServiceImpl<BusAttendanceMapper, B
try { try {
result = constructionUserService.faceComparison(file, userId); result = constructionUserService.faceComparison(file, userId);
} catch (Exception e) { } catch (Exception e) {
throw new ServiceException("人脸识别失败,请重新识别", HttpStatus.ERROR); throw new ServiceException(e.getMessage(), HttpStatus.ERROR);
} }
if (!result) { if (!result) {
@ -419,6 +420,7 @@ public class BusAttendanceServiceImpl extends ServiceImpl<BusAttendanceMapper, B
attendance.setReplaceId(replaceId); attendance.setReplaceId(replaceId);
if(req.getSource() != null){ if(req.getSource() != null){
attendance.setSource(req.getSource()); attendance.setSource(req.getSource());
attendance.setSn(req.getSn());
} }
// 记录打卡坐标 // 记录打卡坐标
attendance.setLat(req.getLat()); attendance.setLat(req.getLat());
@ -464,6 +466,7 @@ public class BusAttendanceServiceImpl extends ServiceImpl<BusAttendanceMapper, B
} }
busAttendance.setSource(req.getSource()); busAttendance.setSource(req.getSource());
busAttendance.setSn(req.getSn());
//如果是缺卡需要上传人脸 //如果是缺卡需要上传人脸
if(oldStatus.equals(BusAttendanceClockStatusEnum.UNCLOCK.getValue())){ if(oldStatus.equals(BusAttendanceClockStatusEnum.UNCLOCK.getValue())){
SysOssVo upload = ossService.upload(file); SysOssVo upload = ossService.upload(file);
@ -479,6 +482,7 @@ public class BusAttendanceServiceImpl extends ServiceImpl<BusAttendanceMapper, B
BusAttendance busAttendance = outAttendances.getFirst(); BusAttendance busAttendance = outAttendances.getFirst();
if("1".equals(req.getSource())){ if("1".equals(req.getSource())){
busAttendance.setSource(req.getSource()); busAttendance.setSource(req.getSource());
busAttendance.setSn(req.getSn());
if(busAttendance.getClockStatus().equals(BusAttendanceClockStatusEnum.UNCLOCK.getValue())){ if(busAttendance.getClockStatus().equals(BusAttendanceClockStatusEnum.UNCLOCK.getValue())){
SysOssVo upload = ossService.upload(file); SysOssVo upload = ossService.upload(file);
busAttendance.setFacePic(upload.getOssId().toString()); busAttendance.setFacePic(upload.getOssId().toString());
@ -531,6 +535,7 @@ public class BusAttendanceServiceImpl extends ServiceImpl<BusAttendanceMapper, B
attendance.setReplaceId(replaceId); attendance.setReplaceId(replaceId);
if(req.getSource() != null){ if(req.getSource() != null){
attendance.setSource(req.getSource()); attendance.setSource(req.getSource());
attendance.setSn(req.getSn());
} }
// 记录打卡坐标 // 记录打卡坐标
attendance.setLat(req.getLat()); attendance.setLat(req.getLat());

View File

@ -0,0 +1,137 @@
package org.dromara.vehicle.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import lombok.RequiredArgsConstructor;
import org.dromara.common.core.domain.R;
import org.dromara.common.excel.utils.ExcelUtil;
import org.dromara.common.idempotent.annotation.RepeatSubmit;
import org.dromara.common.log.annotation.Log;
import org.dromara.common.log.enums.BusinessType;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.web.core.BaseController;
import org.dromara.vehicle.domain.dto.vehicleapply.*;
import org.dromara.vehicle.domain.vo.VehVehicleApplyVo;
import org.dromara.vehicle.service.IVehVehicleApplyService;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* 乘车申请
*
* @author lilemy
* @date 2025-10-25
*/
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/vehicle/vehicleApply")
public class VehVehicleApplyController extends BaseController {
private final IVehVehicleApplyService vehVehicleApplyService;
/**
* 查询乘车申请列表
*/
@SaCheckPermission("vehicle:vehicleApply:list")
@GetMapping("/list")
public TableDataInfo<VehVehicleApplyVo> list(VehVehicleApplyQueryReq req, PageQuery pageQuery) {
return vehVehicleApplyService.queryPageList(req, pageQuery);
}
/**
* 导出乘车申请列表
*/
@SaCheckPermission("vehicle:vehicleApply:export")
@Log(title = "乘车申请", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(VehVehicleApplyQueryReq req, HttpServletResponse response) {
List<VehVehicleApplyVo> list = vehVehicleApplyService.queryList(req);
ExcelUtil.exportExcel(list, "乘车申请", VehVehicleApplyVo.class, response);
}
/**
* 获取乘车申请详细信息
*
* @param id 主键
*/
@SaCheckPermission("vehicle:vehicleApply:query")
@GetMapping("/{id}")
public R<VehVehicleApplyVo> getInfo(@NotNull(message = "主键不能为空")
@PathVariable Long id) {
return R.ok(vehVehicleApplyService.queryById(id));
}
/**
* 新增乘车申请
*/
@SaCheckPermission("vehicle:vehicleApply:add")
@Log(title = "乘车申请", businessType = BusinessType.INSERT)
@RepeatSubmit()
@PostMapping()
public R<Void> add(@Validated @RequestBody VehVehicleApplyCreateReq req) {
return toAjax(vehVehicleApplyService.insertByBo(req));
}
/**
* 修改乘车申请
*/
@SaCheckPermission("vehicle:vehicleApply:edit")
@Log(title = "乘车申请", businessType = BusinessType.UPDATE)
@RepeatSubmit()
@PutMapping()
public R<Void> edit(@Validated @RequestBody VehVehicleApplyUpdateReq req) {
return toAjax(vehVehicleApplyService.updateByBo(req));
}
/**
* 车主审核
*/
@SaCheckPermission("vehicle:vehicleApply:edit")
@Log(title = "乘车申请", businessType = BusinessType.UPDATE)
@RepeatSubmit()
@PutMapping("/driverReview")
public R<Void> driverReview(@Validated @RequestBody VehVehicleApplyReviewReq req) {
return toAjax(vehVehicleApplyService.driverReview(req));
}
/**
* 乘客确认上车状态
*/
@SaCheckPermission("vehicle:vehicleApply:edit")
@Log(title = "乘车申请", businessType = BusinessType.UPDATE)
@RepeatSubmit()
@PutMapping("/changeStatus")
public R<Void> changeStatus(@Validated @RequestBody VehVehicleApplyChangeStatusReq req) {
return toAjax(vehVehicleApplyService.changeStatus(req));
}
/**
* 乘客取消申请
*/
@SaCheckPermission("vehicle:vehicleApply:edit")
@Log(title = "乘车申请", businessType = BusinessType.UPDATE)
@RepeatSubmit()
@PutMapping("/cancel")
public R<Void> cancel(@Validated @RequestBody VehVehicleApplyCancelReq req) {
return toAjax(vehVehicleApplyService.cancelApply(req));
}
/**
* 删除乘车申请
*
* @param ids 主键串
*/
@SaCheckPermission("vehicle:vehicleApply:remove")
@Log(title = "乘车申请", businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}")
public R<Void> remove(@NotEmpty(message = "主键不能为空")
@PathVariable Long[] ids) {
return toAjax(vehVehicleApplyService.deleteWithValidByIds(List.of(ids), true));
}
}

View File

@ -0,0 +1,136 @@
package org.dromara.vehicle.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import lombok.RequiredArgsConstructor;
import org.dromara.common.core.domain.R;
import org.dromara.common.excel.utils.ExcelUtil;
import org.dromara.common.idempotent.annotation.RepeatSubmit;
import org.dromara.common.log.annotation.Log;
import org.dromara.common.log.enums.BusinessType;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.web.core.BaseController;
import org.dromara.vehicle.domain.dto.vehicletrip.*;
import org.dromara.vehicle.domain.vo.VehVehicleTripMyVo;
import org.dromara.vehicle.domain.vo.VehVehicleTripVo;
import org.dromara.vehicle.service.IVehVehicleTripService;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* 车辆出行记录
*
* @author lilemy
* @date 2025-10-25
*/
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/vehicle/vehicleTrip")
public class VehVehicleTripController extends BaseController {
private final IVehVehicleTripService vehVehicleTripService;
/**
* 查询车辆出行记录列表
*/
@SaCheckPermission("vehicle:vehicleTrip:list")
@GetMapping("/list")
public TableDataInfo<VehVehicleTripVo> list(VehVehicleTripQueryReq req, PageQuery pageQuery) {
return vehVehicleTripService.queryPageList(req, pageQuery);
}
/**
* 查询当前用户车辆出行记录列表
*/
@SaCheckPermission("vehicle:vehicleTrip:list")
@GetMapping("/myList")
public R<List<VehVehicleTripMyVo>> queryMyList(VehVehicleTripMyQueryReq req) {
return R.ok(vehVehicleTripService.queryMyList(req));
}
/**
* 导出车辆出行记录列表
*/
@SaCheckPermission("vehicle:vehicleTrip:export")
@Log(title = "车辆出行记录", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(VehVehicleTripQueryReq req, HttpServletResponse response) {
List<VehVehicleTripVo> list = vehVehicleTripService.queryList(req);
ExcelUtil.exportExcel(list, "车辆出行记录", VehVehicleTripVo.class, response);
}
/**
* 获取车辆出行记录详细信息
*
* @param id 主键
*/
@SaCheckPermission("vehicle:vehicleTrip:query")
@GetMapping("/{id}")
public R<VehVehicleTripVo> getInfo(@NotNull(message = "主键不能为空")
@PathVariable Long id) {
return R.ok(vehVehicleTripService.queryById(id));
}
/**
* 新增车辆出行记录
*/
@SaCheckPermission("vehicle:vehicleTrip:add")
@Log(title = "车辆出行记录", businessType = BusinessType.INSERT)
@RepeatSubmit()
@PostMapping()
public R<Void> add(@Validated @RequestBody VehVehicleTripCreateReq req) {
return toAjax(vehVehicleTripService.insertByBo(req));
}
/**
* 修改车辆出行记录
*/
@SaCheckPermission("vehicle:vehicleTrip:edit")
@Log(title = "车辆出行记录", businessType = BusinessType.UPDATE)
@RepeatSubmit()
@PutMapping()
public R<Void> edit(@Validated @RequestBody VehVehicleTripUpdateReq req) {
return toAjax(vehVehicleTripService.updateByBo(req));
}
/**
* 修改车辆出行记录状态
*/
@SaCheckPermission("vehicle:vehicleTrip:edit")
@Log(title = "车辆出行记录", businessType = BusinessType.UPDATE)
@RepeatSubmit()
@PutMapping("/changeStatus")
public R<Void> changeStatus(@Validated @RequestBody VehVehicleTripChangeStatusReq req) {
return toAjax(vehVehicleTripService.changeStatus(req));
}
/**
* 取消车辆出行记录
*/
@SaCheckPermission("vehicle:vehicleTrip:edit")
@Log(title = "车辆出行记录", businessType = BusinessType.UPDATE)
@RepeatSubmit()
@PutMapping("/cancel")
public R<Void> cancel(@Validated @RequestBody VehVehicleTripCancelReq req) {
return toAjax(vehVehicleTripService.cancel(req));
}
/**
* 删除车辆出行记录
*
* @param ids 主键串
*/
@SaCheckPermission("vehicle:vehicleTrip:remove")
@Log(title = "车辆出行记录", businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}")
public R<Void> remove(@NotEmpty(message = "主键不能为空")
@PathVariable Long[] ids) {
return toAjax(vehVehicleTripService.deleteWithValidByIds(List.of(ids), true));
}
}

View File

@ -0,0 +1,115 @@
package org.dromara.vehicle.controller.app;
import jakarta.annotation.Resource;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import org.dromara.common.core.domain.R;
import org.dromara.common.idempotent.annotation.RepeatSubmit;
import org.dromara.common.log.annotation.Log;
import org.dromara.common.log.enums.BusinessType;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.web.core.BaseController;
import org.dromara.vehicle.domain.dto.vehicleapply.*;
import org.dromara.vehicle.domain.vo.VehVehicleApplyVo;
import org.dromara.vehicle.service.IVehVehicleApplyService;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* 乘车申请 app 接口
*
* @author lilemy
* @date 2025-10-28 10:16
*/
@Validated
@RestController
@RequestMapping("/app/vehicle/vehicleApply")
public class VehVehicleApplyAppController extends BaseController {
@Resource
private IVehVehicleApplyService vehicleApplyService;
/**
* 查询乘车申请列表
*/
@GetMapping("/list")
public TableDataInfo<VehVehicleApplyVo> list(VehVehicleApplyQueryReq req, PageQuery pageQuery) {
return vehicleApplyService.queryPageList(req, pageQuery);
}
/**
* 获取乘车申请详细信息
*
* @param id 主键
*/
@GetMapping("/{id}")
public R<VehVehicleApplyVo> getInfo(@NotNull(message = "主键不能为空")
@PathVariable Long id) {
return R.ok(vehicleApplyService.queryById(id));
}
/**
* 新增乘车申请
*/
@Log(title = "乘车申请", businessType = BusinessType.INSERT)
@RepeatSubmit()
@PostMapping()
public R<Void> add(@Validated @RequestBody VehVehicleApplyCreateReq req) {
return toAjax(vehicleApplyService.insertByBo(req));
}
/**
* 修改乘车申请
*/
@Log(title = "乘车申请", businessType = BusinessType.UPDATE)
@RepeatSubmit()
@PutMapping()
public R<Void> edit(@Validated @RequestBody VehVehicleApplyUpdateReq req) {
return toAjax(vehicleApplyService.updateByBo(req));
}
/**
* 车主审核
*/
@Log(title = "乘车申请", businessType = BusinessType.UPDATE)
@RepeatSubmit()
@PutMapping("/driverReview")
public R<Void> driverReview(@Validated @RequestBody VehVehicleApplyReviewReq req) {
return toAjax(vehicleApplyService.driverReview(req));
}
/**
* 乘客确认上车状态
*/
@Log(title = "乘车申请", businessType = BusinessType.UPDATE)
@RepeatSubmit()
@PutMapping("/changeStatus")
public R<Void> changeStatus(@Validated @RequestBody VehVehicleApplyChangeStatusReq req) {
return toAjax(vehicleApplyService.changeStatus(req));
}
/**
* 乘客取消申请
*/
@Log(title = "乘车申请", businessType = BusinessType.UPDATE)
@RepeatSubmit()
@PutMapping("/cancel")
public R<Void> cancel(@Validated @RequestBody VehVehicleApplyCancelReq req) {
return toAjax(vehicleApplyService.cancelApply(req));
}
/**
* 删除乘车申请
*
* @param ids 主键串
*/
@Log(title = "乘车申请", businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}")
public R<Void> remove(@NotEmpty(message = "主键不能为空")
@PathVariable Long[] ids) {
return toAjax(vehicleApplyService.deleteWithValidByIds(List.of(ids), true));
}
}

View File

@ -0,0 +1,85 @@
package org.dromara.vehicle.controller.app;
import jakarta.annotation.Resource;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import org.dromara.common.core.domain.R;
import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.core.validate.EditGroup;
import org.dromara.common.idempotent.annotation.RepeatSubmit;
import org.dromara.common.log.annotation.Log;
import org.dromara.common.log.enums.BusinessType;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.web.core.BaseController;
import org.dromara.vehicle.domain.bo.VehVehicleInfoBo;
import org.dromara.vehicle.domain.vo.VehVehicleInfoVo;
import org.dromara.vehicle.service.IVehVehicleInfoService;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* @author lilemy
* @date 2025-10-30 11:51
*/
@Validated
@RestController
@RequestMapping("/app/vehicle/vehicleInfo")
public class VehVehicleInfoAppController extends BaseController {
@Resource
private IVehVehicleInfoService vehicleInfoService;
/**
* 查询车辆信息列表
*/
@GetMapping("/list")
public TableDataInfo<VehVehicleInfoVo> list(VehVehicleInfoBo bo, PageQuery pageQuery) {
return vehicleInfoService.queryPageList(bo, pageQuery);
}
/**
* 获取车辆信息详细信息
*
* @param id 主键
*/
@GetMapping("/{id}")
public R<VehVehicleInfoVo> getInfo(@NotNull(message = "主键不能为空")
@PathVariable Long id) {
return R.ok(vehicleInfoService.queryById(id));
}
/**
* 新增车辆信息
*/
@Log(title = "车辆信息", businessType = BusinessType.INSERT)
@RepeatSubmit()
@PostMapping()
public R<Void> add(@Validated(AddGroup.class) @RequestBody VehVehicleInfoBo bo) {
return toAjax(vehicleInfoService.insertByBo(bo));
}
/**
* 修改车辆信息
*/
@Log(title = "车辆信息", businessType = BusinessType.UPDATE)
@RepeatSubmit()
@PutMapping()
public R<Void> edit(@Validated(EditGroup.class) @RequestBody VehVehicleInfoBo bo) {
return toAjax(vehicleInfoService.updateByBo(bo));
}
/**
* 删除车辆信息
*
* @param ids 主键串
*/
@Log(title = "车辆信息", businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}")
public R<Void> remove(@NotEmpty(message = "主键不能为空")
@PathVariable Long[] ids) {
return toAjax(vehicleInfoService.deleteWithValidByIds(List.of(ids), true));
}
}

View File

@ -0,0 +1,114 @@
package org.dromara.vehicle.controller.app;
import jakarta.annotation.Resource;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import org.dromara.common.core.domain.R;
import org.dromara.common.idempotent.annotation.RepeatSubmit;
import org.dromara.common.log.annotation.Log;
import org.dromara.common.log.enums.BusinessType;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.web.core.BaseController;
import org.dromara.vehicle.domain.dto.vehicletrip.*;
import org.dromara.vehicle.domain.vo.VehVehicleTripMyVo;
import org.dromara.vehicle.domain.vo.VehVehicleTripVo;
import org.dromara.vehicle.service.IVehVehicleTripService;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* 车辆出行记录 app 接口
*
* @author lilemy
* @date 2025-10-28 10:19
*/
@Validated
@RestController
@RequestMapping("/app/vehicle/vehicleTrip")
public class VehVehicleTripAppController extends BaseController {
@Resource
private IVehVehicleTripService vehicleTripService;
/**
* 查询车辆出行记录列表
*/
@GetMapping("/list")
public TableDataInfo<VehVehicleTripVo> list(VehVehicleTripQueryReq req, PageQuery pageQuery) {
return vehicleTripService.queryPageList(req, pageQuery);
}
/**
* 查询当前用户车辆出行记录列表
*/
@GetMapping("/myList")
public R<List<VehVehicleTripMyVo>> queryMyList(VehVehicleTripMyQueryReq req) {
return R.ok(vehicleTripService.queryMyAppList(req));
}
/**
* 获取车辆出行记录详细信息
*
* @param id 主键
*/
@GetMapping("/{id}")
public R<VehVehicleTripVo> getInfo(@NotNull(message = "主键不能为空")
@PathVariable Long id) {
return R.ok(vehicleTripService.queryById(id));
}
/**
* 新增车辆出行记录
*/
@Log(title = "车辆出行记录", businessType = BusinessType.INSERT)
@RepeatSubmit()
@PostMapping()
public R<Void> add(@Validated @RequestBody VehVehicleTripCreateReq req) {
return toAjax(vehicleTripService.insertByBo(req));
}
/**
* 修改车辆出行记录
*/
@Log(title = "车辆出行记录", businessType = BusinessType.UPDATE)
@RepeatSubmit()
@PutMapping()
public R<Void> edit(@Validated @RequestBody VehVehicleTripUpdateReq req) {
return toAjax(vehicleTripService.updateByBo(req));
}
/**
* 修改车辆出行记录状态
*/
@Log(title = "车辆出行记录", businessType = BusinessType.UPDATE)
@RepeatSubmit()
@PutMapping("/changeStatus")
public R<Void> changeStatus(@Validated @RequestBody VehVehicleTripChangeStatusReq req) {
return toAjax(vehicleTripService.changeStatus(req));
}
/**
* 取消车辆出行记录
*/
@Log(title = "车辆出行记录", businessType = BusinessType.UPDATE)
@RepeatSubmit()
@PutMapping("/cancel")
public R<Void> cancel(@Validated @RequestBody VehVehicleTripCancelReq req) {
return toAjax(vehicleTripService.cancel(req));
}
/**
* 删除车辆出行记录
*
* @param ids 主键串
*/
@Log(title = "车辆出行记录", businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}")
public R<Void> remove(@NotEmpty(message = "主键不能为空")
@PathVariable Long[] ids) {
return toAjax(vehicleTripService.deleteWithValidByIds(List.of(ids), true));
}
}

View File

@ -0,0 +1,91 @@
package org.dromara.vehicle.domain;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.dromara.common.mybatis.core.domain.BaseEntity;
import java.io.Serial;
/**
* 乘车申请对象 veh_vehicle_apply
*
* @author lilemy
* @date 2025-10-25
*/
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("veh_vehicle_apply")
public class VehVehicleApply extends BaseEntity {
@Serial
private static final long serialVersionUID = 1L;
/**
* 主键ID
*/
@TableId(value = "id")
private Long id;
/**
* 项目ID
*/
private Long projectId;
/**
* 关联行程ID
*/
private Long tripId;
/**
* 申请人数
*/
private Integer peopleNum;
/**
* 联系电话
*/
private String passengerPhone;
/**
* 出发地
*/
private String startPlace;
/**
* 目的地
*/
private String endPlace;
/**
* 出发地经度
*/
private String startLng;
/**
* 出发地纬度
*/
private String startLat;
/**
* 目的地经度
*/
private String endLng;
/**
* 目的地纬度
*/
private String endLat;
/**
* 乘车状态
*/
private String status;
/**
* 备注
*/
private String remark;
}

View File

@ -1,12 +1,13 @@
package org.dromara.vehicle.domain; package org.dromara.vehicle.domain;
import org.dromara.common.mybatis.core.domain.BaseEntity; import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.*; import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import java.math.BigDecimal; import org.dromara.common.mybatis.core.domain.BaseEntity;
import java.io.Serial; import java.io.Serial;
import java.math.BigDecimal;
import java.time.LocalDate; import java.time.LocalDate;
/** /**
@ -144,5 +145,4 @@ public class VehVehicleInfo extends BaseEntity {
*/ */
private String remark; private String remark;
} }

View File

@ -0,0 +1,122 @@
package org.dromara.vehicle.domain;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.dromara.common.mybatis.core.domain.BaseEntity;
import java.io.Serial;
import java.util.Date;
/**
* 车辆出行记录对象 veh_vehicle_trip
*
* @author lilemy
* @date 2025-10-25
*/
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("veh_vehicle_trip")
public class VehVehicleTrip extends BaseEntity {
@Serial
private static final long serialVersionUID = 1L;
/**
* 主键ID
*/
@TableId(value = "id")
private Long id;
/**
* 项目ID
*/
private Long projectId;
/**
* 车辆ID
*/
private Long vehicleId;
/**
* 车牌号
*/
private String plateNumber;
/**
* 联系电话
*/
private String passengerPhone;
/**
* 出行事由
*/
private String tripReason;
/**
* 出发地
*/
private String startPlace;
/**
* 目的地
*/
private String endPlace;
/**
* 出发地纬度
*/
private String startLat;
/**
* 出发地经度
*/
private String startLng;
/**
* 目的地纬度
*/
private String endLat;
/**
* 目的地经度
*/
private String endLng;
/**
* 计划出发时间
*/
private Date startTime;
/**
* 计划到达时间
*/
private Date endTime;
/**
* 申请人数
*/
private Integer peopleNum;
/**
* 剩余座位数
*/
private Integer leftSeat;
/**
* 审核状态
*/
private String reviewStatus;
/**
* 状态
*/
private String tripStatus;
/**
* 备注
*/
private String remark;
}

View File

@ -0,0 +1,24 @@
package org.dromara.vehicle.domain.dto.vehicleapply;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
/**
* @author lilemy
* @date 2025-10-28 09:22
*/
@Data
public class VehVehicleApplyCancelReq implements Serializable {
@Serial
private static final long serialVersionUID = -3178638460132028254L;
/**
* 主键ID
*/
@NotNull(message = "主键不能为空")
private Long id;
}

View File

@ -0,0 +1,31 @@
package org.dromara.vehicle.domain.dto.vehicleapply;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
/**
* @author lilemy
* @date 2025-10-28 09:24
*/
@Data
public class VehVehicleApplyChangeStatusReq implements Serializable {
@Serial
private static final long serialVersionUID = -7509344474542508076L;
/**
* 主键ID
*/
@NotNull(message = "主键不能为空")
private Long id;
/**
* 乘车状态
*/
@NotBlank(message = "乘车状态不能为空")
private String status;
}

View File

@ -0,0 +1,80 @@
package org.dromara.vehicle.domain.dto.vehicleapply;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
/**
* @author lilemy
* @date 2025-10-27 14:15
*/
@Data
public class VehVehicleApplyCreateReq implements Serializable {
@Serial
private static final long serialVersionUID = 1199219811382958686L;
/**
* 项目ID
*/
@NotNull(message = "项目ID不能为空")
private Long projectId;
/**
* 关联行程ID
*/
@NotNull(message = "关联行程ID不能为空")
private Long tripId;
/**
* 申请人数
*/
@NotNull(message = "申请人数不能为空")
private Integer peopleNum;
/**
* 联系电话
*/
private String passengerPhone;
/**
* 出发地
*/
private String startPlace;
/**
* 目的地
*/
@NotBlank(message = "目的地不能为空")
private String endPlace;
/**
* 出发地经度
*/
private String startLng;
/**
* 出发地纬度
*/
private String startLat;
/**
* 目的地经度
*/
@NotBlank(message = "目的地经度不能为空")
private String endLng;
/**
* 目的地纬度
*/
@NotBlank(message = "目的地纬度不能为空")
private String endLat;
/**
* 备注
*/
private String remark;
}

View File

@ -0,0 +1,52 @@
package org.dromara.vehicle.domain.dto.vehicleapply;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
/**
* @author lilemy
* @date 2025-10-27 14:48
*/
@Data
public class VehVehicleApplyQueryReq implements Serializable {
@Serial
private static final long serialVersionUID = -7148064523789231728L;
/**
* 项目ID
*/
private Long projectId;
/**
* 关联行程ID
*/
private Long tripId;
/**
* 申请人数
*/
private Integer peopleNum;
/**
* 联系电话
*/
private String passengerPhone;
/**
* 出发地
*/
private String startPlace;
/**
* 目的地
*/
private String endPlace;
/**
* 乘车状态
*/
private String status;
}

View File

@ -0,0 +1,31 @@
package org.dromara.vehicle.domain.dto.vehicleapply;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
/**
* @author lilemy
* @date 2025-10-27 15:00
*/
@Data
public class VehVehicleApplyReviewReq implements Serializable {
@Serial
private static final long serialVersionUID = -5881272503469997069L;
/**
* 主键
*/
@NotNull(message = "主键不能为空")
private Long id;
/**
* 乘车状态
*/
@NotBlank(message = "乘车状态不能为空")
private String status;
}

View File

@ -0,0 +1,69 @@
package org.dromara.vehicle.domain.dto.vehicleapply;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
/**
* @author lilemy
* @date 2025-10-27 15:30
*/
@Data
public class VehVehicleApplyUpdateReq implements Serializable {
@Serial
private static final long serialVersionUID = 7272254511495824388L;
/**
* 主键ID
*/
@NotNull(message = "主键不能为空")
private Long id;
/**
* 申请人数
*/
private Integer peopleNum;
/**
* 联系电话
*/
private String passengerPhone;
/**
* 出发地
*/
private String startPlace;
/**
* 目的地
*/
private String endPlace;
/**
* 出发地经度
*/
private String startLng;
/**
* 出发地纬度
*/
private String startLat;
/**
* 目的地经度
*/
private String endLng;
/**
* 目的地纬度
*/
private String endLat;
/**
* 备注
*/
private String remark;
}

View File

@ -0,0 +1,24 @@
package org.dromara.vehicle.domain.dto.vehicletrip;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
/**
* @author lilemy
* @date 2025-10-27 09:22
*/
@Data
public class VehVehicleTripCancelReq implements Serializable {
@Serial
private static final long serialVersionUID = 7690656986769836247L;
/**
* 主键ID
*/
@NotNull(message = "主键不能为空")
private Long id;
}

View File

@ -0,0 +1,31 @@
package org.dromara.vehicle.domain.dto.vehicletrip;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
/**
* @author lilemy
* @date 2025-10-28 18:47
*/
@Data
public class VehVehicleTripChangeStatusReq implements Serializable {
@Serial
private static final long serialVersionUID = -1139979225623034249L;
/**
* 主键
*/
@NotNull(message = "主键不能为空")
private Long id;
/**
* 状态
*/
@NotBlank(message = "状态不能为空")
private String tripStatus;
}

View File

@ -0,0 +1,109 @@
package org.dromara.vehicle.domain.dto.vehicletrip;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
/**
* @author lilemy
* @date 2025-10-27 10:05
*/
@Data
public class VehVehicleTripCreateReq implements Serializable {
@Serial
private static final long serialVersionUID = 7691668678158594767L;
/**
* 项目ID
*/
private Long projectId;
/**
* 车辆ID
*/
private Long vehicleId;
/**
* 车牌号
*/
private String plateNumber;
/**
* 联系电话
*/
private String passengerPhone;
/**
* 出行事由
*/
private String tripReason;
/**
* 出发地
*/
@NotBlank(message = "出发地不能为空")
private String startPlace;
/**
* 目的地
*/
@NotBlank(message = "目的地不能为空")
private String endPlace;
/**
* 出发地经度
*/
@NotBlank(message = "出发地经度不能为空")
private String startLng;
/**
* 出发地纬度
*/
@NotBlank(message = "出发地纬度不能为空")
private String startLat;
/**
* 目的地经度
*/
@NotBlank(message = "目的地经度不能为空")
private String endLng;
/**
* 目的地纬度
*/
@NotBlank(message = "目的地纬度不能为空")
private String endLat;
/**
* 计划出发时间
*/
@NotNull(message = "计划出发时间不能为空")
private Date startTime;
/**
* 计划到达时间
*/
private Date endTime;
/**
* 申请人数
*/
@NotNull(message = "申请人数不能为空")
private Integer peopleNum;
/**
* 剩余座位数
*/
@NotNull(message = "剩余座位数不能为空")
private Integer leftSeat;
/**
* 备注
*/
private String remark;
}

View File

@ -0,0 +1,26 @@
package org.dromara.vehicle.domain.dto.vehicletrip;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
/**
* @author lilemy
* @date 2025-10-28 16:53
*/
@Data
public class VehVehicleTripMyQueryReq implements Serializable {
@Serial
private static final long serialVersionUID = 4251601419123102085L;
/**
* 查询类型
* web(1待出行 2预约中 3已完成)
* app(0待出行 1进行中 2预约中 3已完成)
*/
@NotBlank(message = "查询类型不能为空")
private String type;
}

View File

@ -0,0 +1,59 @@
package org.dromara.vehicle.domain.dto.vehicletrip;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.time.LocalDateTime;
/**
* @author lilemy
* @date 2025-10-25 19:40
*/
@Data
public class VehVehicleTripQueryReq implements Serializable {
@Serial
private static final long serialVersionUID = -7795154504461471208L;
/**
* 项目ID
*/
private Long projectId;
/**
* 目的地
*/
private String endPlace;
/**
* 目的地经度
*/
private String endLng;
/**
* 目的地纬度
*/
private String endLat;
/**
* 计划出发时间
*/
private LocalDateTime startTime;
/**
* 出行人数
*/
private Integer peopleNum;
/**
* 审核状态
*/
private String reviewStatus;
/**
* 状态
*/
private String tripStatus;
}

View File

@ -0,0 +1,105 @@
package org.dromara.vehicle.domain.dto.vehicletrip;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
/**
* @author lilemy
* @date 2025-10-27 11:33
*/
@Data
public class VehVehicleTripUpdateReq implements Serializable {
@Serial
private static final long serialVersionUID = 3855392201273235078L;
/**
* 主键ID
*/
@NotNull(message = "主键不能为空")
private Long id;
/**
* 项目ID
*/
private Long projectId;
/**
* 车辆ID
*/
private Long vehicleId;
/**
* 车牌号
*/
private String plateNumber;
/**
* 联系电话
*/
private String passengerPhone;
/**
* 出行事由
*/
private String tripReason;
/**
* 出发地
*/
private String startPlace;
/**
* 目的地
*/
private String endPlace;
/**
* 出发地经度
*/
private String startLng;
/**
* 出发地纬度
*/
private String startLat;
/**
* 目的地经度
*/
private String endLng;
/**
* 目的地纬度
*/
private String endLat;
/**
* 计划出发时间
*/
private Date startTime;
/**
* 计划到达时间
*/
private Date endTime;
/**
* 申请人数
*/
private Integer peopleNum;
/**
* 剩余座位数
*/
private Integer leftSeat;
/**
* 备注
*/
private String remark;
}

View File

@ -0,0 +1,27 @@
package org.dromara.vehicle.domain.enums;
import lombok.Getter;
/**
* @author lilemy
* @date 2025-10-27 09:39
*/
@Getter
public enum VehApplyStatusEnum {
APPLYING("申请中", "1"),
CONFIRMED("已确认", "2"),
REJECTED("已拒绝", "3"),
ALREADY("已上车", "4"),
ARRIVED("已到达", "5"),
CANCELED("已取消", "6");
private final String text;
private final String value;
VehApplyStatusEnum(String text, String value) {
this.text = text;
this.value = value;
}
}

View File

@ -0,0 +1,25 @@
package org.dromara.vehicle.domain.enums;
import lombok.Getter;
/**
* @author lilemy
* @date 2025-10-27 09:32
*/
@Getter
public enum VehTripStatusEnum {
READY_DEPART("待出发","1"),
UNDERWAY("进行中","2"),
COMPLETED("已完成","3"),
CANCELED("已取消","4" );
private final String text;
private final String value;
VehTripStatusEnum(String text, String value) {
this.text = text;
this.value = value;
}
}

View File

@ -0,0 +1,25 @@
package org.dromara.vehicle.domain.enums;
import lombok.Getter;
/**
* @author lilemy
* @date 2025-10-27 11:42
*/
@Getter
public enum VehVehicleInfoStatusEnum {
AVAILABLE("可用", "1"),
IN_USE("使用中", "2"),
UNDER_MAINTENANCE("维护中", "3"),
DISABLE("停用", "4");
private final String text;
private final String value;
VehVehicleInfoStatusEnum(String text, String value) {
this.text = text;
this.value = value;
}
}

View File

@ -0,0 +1,127 @@
package org.dromara.vehicle.domain.vo;
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import com.alibaba.excel.annotation.ExcelProperty;
import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data;
import org.dromara.common.excel.annotation.ExcelDictFormat;
import org.dromara.common.excel.convert.ExcelDictConvert;
import org.dromara.common.translation.annotation.Translation;
import org.dromara.common.translation.constant.TransConstant;
import org.dromara.vehicle.domain.VehVehicleApply;
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
/**
* 乘车申请视图对象 veh_vehicle_apply
*
* @author lilemy
* @date 2025-10-25
*/
@Data
@ExcelIgnoreUnannotated
@AutoMapper(target = VehVehicleApply.class)
public class VehVehicleApplyVo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 主键ID
*/
@ExcelProperty(value = "主键ID")
private Long id;
/**
* 项目ID
*/
@ExcelProperty(value = "项目ID")
private Long projectId;
/**
* 关联行程ID
*/
@ExcelProperty(value = "关联行程ID")
private Long tripId;
/**
* 申请人数
*/
@ExcelProperty(value = "申请人数")
private Integer peopleNum;
/**
* 联系电话
*/
@ExcelProperty(value = "联系电话")
private String passengerPhone;
/**
* 出发地
*/
@ExcelProperty(value = "出发地")
private String startPlace;
/**
* 目的地
*/
@ExcelProperty(value = "目的地")
private String endPlace;
/**
* 出发地经度
*/
@ExcelProperty(value = "出发地经度")
private String startLng;
/**
* 出发地纬度
*/
@ExcelProperty(value = "出发地纬度")
private String startLat;
/**
* 目的地经度
*/
@ExcelProperty(value = "目的地经度")
private String endLng;
/**
* 目的地纬度
*/
@ExcelProperty(value = "目的地纬度")
private String endLat;
/**
* 乘车状态
*/
@ExcelProperty(value = "乘车状态", converter = ExcelDictConvert.class)
@ExcelDictFormat(dictType = "veh_vehicle_apply_status")
private String status;
/**
* 备注
*/
@ExcelProperty(value = "备注")
private String remark;
/**
* 创建者
*/
private Long createBy;
/**
* 创建者名称
*/
@Translation(type = TransConstant.USER_ID_TO_NICKNAME, mapper = "createBy")
private String createByName;
/**
* 创建时间
*/
private Date createTime;
}

View File

@ -1,19 +1,17 @@
package org.dromara.vehicle.domain.vo; package org.dromara.vehicle.domain.vo;
import java.math.BigDecimal;
import org.dromara.vehicle.domain.VehVehicleInfo;
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import com.alibaba.excel.annotation.ExcelProperty; import com.alibaba.excel.annotation.ExcelProperty;
import org.dromara.common.excel.annotation.ExcelDictFormat;
import org.dromara.common.excel.convert.ExcelDictConvert;
import io.github.linpeilie.annotations.AutoMapper; import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data; import lombok.Data;
import org.dromara.common.excel.annotation.ExcelDictFormat;
import org.dromara.common.excel.convert.ExcelDictConvert;
import org.dromara.vehicle.domain.VehVehicleInfo;
import java.io.Serial; import java.io.Serial;
import java.io.Serializable; import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDate; import java.time.LocalDate;
import java.util.Date;
/** /**

View File

@ -0,0 +1,142 @@
package org.dromara.vehicle.domain.vo;
import lombok.Data;
import org.dromara.common.translation.annotation.Translation;
import org.dromara.common.translation.constant.TransConstant;
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
import java.util.List;
/**
* @author lilemy
* @date 2025-10-28 16:45
*/
@Data
public class VehVehicleTripMyVo implements Serializable {
@Serial
private static final long serialVersionUID = 8905520529338036288L;
/**
* 主键ID
*/
private Long id;
/**
* 项目ID
*/
private Long projectId;
/**
* 车辆ID
*/
private Long vehicleId;
/**
* 车牌号
*/
private String plateNumber;
/**
* 联系电话
*/
private String passengerPhone;
/**
* 出行事由
*/
private String tripReason;
/**
* 出发地
*/
private String startPlace;
/**
* 目的地
*/
private String endPlace;
/**
* 出发地经度
*/
private String startLat;
/**
* 出发地纬度
*/
private String startLng;
/**
* 目的地经度
*/
private String endLat;
/**
* 目的地纬度
*/
private String endLng;
/**
* 计划出发时间
*/
private Date startTime;
/**
* 计划到达时间
*/
private Date endTime;
/**
* 申请人数
*/
private Integer peopleNum;
/**
* 剩余座位数
*/
private Integer leftSeat;
/**
* 审核状态
*/
private String reviewStatus;
/**
* 状态
*/
private String tripStatus;
/**
* 备注
*/
private String remark;
/**
* 创建者
*/
private Long createBy;
/**
* 创建者名称
*/
@Translation(type = TransConstant.USER_ID_TO_NICKNAME, mapper = "createBy")
private String createByName;
/**
* 创建时间
*/
private Date createTime;
/**
* 是否为车主(0不是 1是)
*/
private Integer isVehicleOwner;
/**
* 申请列表
*/
private List<VehVehicleApplyVo> applyList;
}

View File

@ -0,0 +1,188 @@
package org.dromara.vehicle.domain.vo;
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import com.alibaba.excel.annotation.ExcelProperty;
import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data;
import org.dromara.common.excel.annotation.ExcelDictFormat;
import org.dromara.common.excel.convert.ExcelDictConvert;
import org.dromara.common.translation.annotation.Translation;
import org.dromara.common.translation.constant.TransConstant;
import org.dromara.vehicle.domain.VehVehicleTrip;
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
/**
* 车辆出行记录视图对象 veh_vehicle_trip
*
* @author lilemy
* @date 2025-10-25
*/
@Data
@ExcelIgnoreUnannotated
@AutoMapper(target = VehVehicleTrip.class)
public class VehVehicleTripVo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 主键ID
*/
@ExcelProperty(value = "主键ID")
private Long id;
/**
* 项目ID
*/
@ExcelProperty(value = "项目ID")
private Long projectId;
/**
* 车辆ID
*/
@ExcelProperty(value = "车辆ID")
private Long vehicleId;
/**
* 车牌号
*/
@ExcelProperty(value = "车牌号")
private String plateNumber;
/**
* 联系电话
*/
@ExcelProperty(value = "联系电话")
private String passengerPhone;
/**
* 出行事由
*/
@ExcelProperty(value = "出行事由")
private String tripReason;
/**
* 出发地
*/
@ExcelProperty(value = "出发地")
private String startPlace;
/**
* 目的地
*/
@ExcelProperty(value = "目的地")
private String endPlace;
/**
* 出发地经度
*/
@ExcelProperty(value = "出发地经度")
private String startLng;
/**
* 出发地纬度
*/
@ExcelProperty(value = "出发地纬度")
private String startLat;
/**
* 目的地经度
*/
@ExcelProperty(value = "目的地经度")
private String endLng;
/**
* 目的地纬度
*/
@ExcelProperty(value = "目的地纬度")
private String endLat;
/**
* 计划出发时间
*/
@ExcelProperty(value = "计划出发时间")
private Date startTime;
/**
* 计划到达时间
*/
@ExcelProperty(value = "计划到达时间")
private Date endTime;
/**
* 申请人数
*/
@ExcelProperty(value = "申请人数")
private Integer peopleNum;
/**
* 剩余座位数
*/
@ExcelProperty(value = "剩余座位数")
private Integer leftSeat;
/**
* 审核状态
*/
@ExcelProperty(value = "审核状态", converter = ExcelDictConvert.class)
@ExcelDictFormat(dictType = "wf_business_status")
private String reviewStatus;
/**
* 状态
*/
@ExcelProperty(value = "状态", converter = ExcelDictConvert.class)
@ExcelDictFormat(dictType = "veh_vehicle_trip_status")
private String tripStatus;
/**
* 备注
*/
@ExcelProperty(value = "备注")
private String remark;
/**
* 距离(米)
*/
private String distanceM;
/**
* 距离评分
*/
private String distanceScore;
/**
* 时间
*/
private String time;
/**
* 时间评分
*/
private String timeScore;
/**
* 人数评分
*/
private String peopleScore;
/**
* 总评分
*/
private String totalScore;
/**
* 创建者
*/
private Long createBy;
/**
* 创建者名称
*/
@Translation(type = TransConstant.USER_ID_TO_NICKNAME, mapper = "createBy")
private String createByName;
}

View File

@ -0,0 +1,15 @@
package org.dromara.vehicle.mapper;
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
import org.dromara.vehicle.domain.VehVehicleApply;
import org.dromara.vehicle.domain.vo.VehVehicleApplyVo;
/**
* 乘车申请Mapper接口
*
* @author lilemy
* @date 2025-10-25
*/
public interface VehVehicleApplyMapper extends BaseMapperPlus<VehVehicleApply, VehVehicleApplyVo> {
}

View File

@ -0,0 +1,21 @@
package org.dromara.vehicle.mapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.apache.ibatis.annotations.Param;
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
import org.dromara.vehicle.domain.VehVehicleTrip;
import org.dromara.vehicle.domain.dto.vehicletrip.VehVehicleTripQueryReq;
import org.dromara.vehicle.domain.vo.VehVehicleTripVo;
/**
* 车辆出行记录Mapper接口
*
* @author lilemy
* @date 2025-10-25
*/
public interface VehVehicleTripMapper extends BaseMapperPlus<VehVehicleTrip, VehVehicleTripVo> {
Page<VehVehicleTripVo> selectVehicleTripPage(@Param("page") Page<VehVehicleTrip> page,
@Param("req") VehVehicleTripQueryReq req);
}

View File

@ -0,0 +1,94 @@
package org.dromara.vehicle.service;
import com.baomidou.mybatisplus.extension.service.IService;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.vehicle.domain.VehVehicleApply;
import org.dromara.vehicle.domain.dto.vehicleapply.*;
import org.dromara.vehicle.domain.vo.VehVehicleApplyVo;
import java.util.Collection;
import java.util.List;
/**
* 乘车申请Service接口
*
* @author lilemy
* @date 2025-10-25
*/
public interface IVehVehicleApplyService extends IService<VehVehicleApply> {
/**
* 查询乘车申请
*
* @param id 主键
* @return 乘车申请
*/
VehVehicleApplyVo queryById(Long id);
/**
* 分页查询乘车申请列表
*
* @param req 查询条件
* @param pageQuery 分页参数
* @return 乘车申请分页列表
*/
TableDataInfo<VehVehicleApplyVo> queryPageList(VehVehicleApplyQueryReq req, PageQuery pageQuery);
/**
* 查询符合条件的乘车申请列表
*
* @param req 查询条件
* @return 乘车申请列表
*/
List<VehVehicleApplyVo> queryList(VehVehicleApplyQueryReq req);
/**
* 新增乘车申请
*
* @param req 乘车申请
* @return 是否新增成功
*/
Boolean insertByBo(VehVehicleApplyCreateReq req);
/**
* 修改乘车申请
*
* @param req 乘车申请
* @return 是否修改成功
*/
Boolean updateByBo(VehVehicleApplyUpdateReq req);
/**
* 车主审核
*
* @param req 审核信息
* @return 是否审核成功
*/
Boolean driverReview(VehVehicleApplyReviewReq req);
/**
* 修改乘车状态
*
* @param req 修改乘车状态信息
* @return 是否修改成功
*/
Boolean changeStatus(VehVehicleApplyChangeStatusReq req);
/**
* 取消申请
*
* @param req 取消申请信息
* @return 是否取消成功
*/
Boolean cancelApply(VehVehicleApplyCancelReq req);
/**
* 校验并批量删除乘车申请信息
*
* @param ids 待删除的主键集合
* @param isValid 是否进行有效性校验
* @return 是否删除成功
*/
Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
}

View File

@ -0,0 +1,103 @@
package org.dromara.vehicle.service;
import com.baomidou.mybatisplus.extension.service.IService;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.vehicle.domain.VehVehicleTrip;
import org.dromara.vehicle.domain.dto.vehicletrip.*;
import org.dromara.vehicle.domain.vo.VehVehicleTripMyVo;
import org.dromara.vehicle.domain.vo.VehVehicleTripVo;
import java.util.Collection;
import java.util.List;
/**
* 车辆出行记录Service接口
*
* @author lilemy
* @date 2025-10-25
*/
public interface IVehVehicleTripService extends IService<VehVehicleTrip> {
/**
* 查询车辆出行记录
*
* @param id 主键
* @return 车辆出行记录
*/
VehVehicleTripVo queryById(Long id);
/**
* 分页查询车辆出行记录列表
*
* @param req 查询条件
* @param pageQuery 分页参数
* @return 车辆出行记录分页列表
*/
TableDataInfo<VehVehicleTripVo> queryPageList(VehVehicleTripQueryReq req, PageQuery pageQuery);
/**
* 查询当前用户车辆出行记录列表
*
* @param req 列表查询条件
* @return 当前用户车辆出行记录列表
*/
List<VehVehicleTripMyVo> queryMyList(VehVehicleTripMyQueryReq req);
/**
* 查询当前用户车辆出行记录列表
*
* @param req 列表查询条件
* @return 当前用户车辆出行记录列表
*/
List<VehVehicleTripMyVo> queryMyAppList(VehVehicleTripMyQueryReq req);
/**
* 查询符合条件的车辆出行记录列表
*
* @param req 查询条件
* @return 车辆出行记录列表
*/
List<VehVehicleTripVo> queryList(VehVehicleTripQueryReq req);
/**
* 新增车辆出行记录
*
* @param req 车辆出行记录
* @return 是否新增成功
*/
Boolean insertByBo(VehVehicleTripCreateReq req);
/**
* 修改车辆出行记录
*
* @param req 车辆出行记录
* @return 是否修改成功
*/
Boolean updateByBo(VehVehicleTripUpdateReq req);
/**
* 修改车辆出行记录状态
*
* @param req 车辆出行记录
* @return 是否修改成功
*/
Boolean changeStatus(VehVehicleTripChangeStatusReq req);
/**
* 取消车辆出行记录
*
* @param req 车辆出行记录
* @return 是否取消成功
*/
Boolean cancel(VehVehicleTripCancelReq req);
/**
* 校验并批量删除车辆出行记录信息
*
* @param ids 待删除的主键集合
* @param isValid 是否进行有效性校验
* @return 是否删除成功
*/
Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
}

View File

@ -0,0 +1,395 @@
package org.dromara.vehicle.service.impl;
import cn.hutool.core.util.PhoneUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.constant.HttpStatus;
import org.dromara.common.core.enums.BusinessStatusEnum;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.system.domain.vo.SysUserVo;
import org.dromara.system.service.ISysUserService;
import org.dromara.vehicle.domain.VehVehicleApply;
import org.dromara.vehicle.domain.VehVehicleTrip;
import org.dromara.vehicle.domain.dto.vehicleapply.*;
import org.dromara.vehicle.domain.enums.VehApplyStatusEnum;
import org.dromara.vehicle.domain.enums.VehTripStatusEnum;
import org.dromara.vehicle.domain.vo.VehVehicleApplyVo;
import org.dromara.vehicle.mapper.VehVehicleApplyMapper;
import org.dromara.vehicle.service.IVehVehicleApplyService;
import org.dromara.vehicle.service.IVehVehicleTripService;
import org.dromara.websocket.ChatServerHandler;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
/**
* 乘车申请Service业务层处理
*
* @author lilemy
* @date 2025-10-25
*/
@Slf4j
@RequiredArgsConstructor
@Service
public class VehVehicleApplyServiceImpl extends ServiceImpl<VehVehicleApplyMapper, VehVehicleApply>
implements IVehVehicleApplyService {
private final IVehVehicleTripService vehicleTripService;
private final ChatServerHandler chatServerHandler;
private final ISysUserService userService;
/**
* 查询乘车申请
*
* @param id 主键
* @return 乘车申请
*/
@Override
public VehVehicleApplyVo queryById(Long id) {
return baseMapper.selectVoById(id);
}
/**
* 分页查询乘车申请列表
*
* @param req 查询条件
* @param pageQuery 分页参数
* @return 乘车申请分页列表
*/
@Override
public TableDataInfo<VehVehicleApplyVo> queryPageList(VehVehicleApplyQueryReq req, PageQuery pageQuery) {
LambdaQueryWrapper<VehVehicleApply> lqw = buildQueryWrapper(req);
Page<VehVehicleApplyVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
return TableDataInfo.build(result);
}
/**
* 查询符合条件的乘车申请列表
*
* @param req 查询条件
* @return 乘车申请列表
*/
@Override
public List<VehVehicleApplyVo> queryList(VehVehicleApplyQueryReq req) {
LambdaQueryWrapper<VehVehicleApply> lqw = buildQueryWrapper(req);
return baseMapper.selectVoList(lqw);
}
private LambdaQueryWrapper<VehVehicleApply> buildQueryWrapper(VehVehicleApplyQueryReq req) {
LambdaQueryWrapper<VehVehicleApply> lqw = Wrappers.lambdaQuery();
lqw.orderByDesc(VehVehicleApply::getId);
lqw.eq(req.getProjectId() != null, VehVehicleApply::getProjectId, req.getProjectId());
lqw.eq(req.getTripId() != null, VehVehicleApply::getTripId, req.getTripId());
lqw.eq(req.getPeopleNum() != null, VehVehicleApply::getPeopleNum, req.getPeopleNum());
lqw.eq(StringUtils.isNotBlank(req.getPassengerPhone()), VehVehicleApply::getPassengerPhone, req.getPassengerPhone());
lqw.eq(StringUtils.isNotBlank(req.getStartPlace()), VehVehicleApply::getStartPlace, req.getStartPlace());
lqw.eq(StringUtils.isNotBlank(req.getEndPlace()), VehVehicleApply::getEndPlace, req.getEndPlace());
lqw.eq(StringUtils.isNotBlank(req.getStatus()), VehVehicleApply::getStatus, req.getStatus());
return lqw;
}
/**
* 新增乘车申请
*
* @param req 乘车申请
* @return 是否新增成功
*/
@Override
public Boolean insertByBo(VehVehicleApplyCreateReq req) {
VehVehicleApply apply = new VehVehicleApply();
BeanUtils.copyProperties(req, apply);
validEntityBeforeSave(apply);
VehVehicleTrip vehicleTrip = vehicleTripService.getById(req.getTripId());
if (vehicleTrip == null) {
throw new ServiceException("行程不存在", HttpStatus.NOT_FOUND);
}
if (vehicleTrip.getTripStatus().equals(VehTripStatusEnum.CANCELED.getValue())) {
throw new ServiceException("行程已取消,请重新选择行程", HttpStatus.BAD_REQUEST);
}
if (vehicleTrip.getTripStatus().equals(VehTripStatusEnum.COMPLETED.getValue())) {
throw new ServiceException("行程已结束,请重新选择行程", HttpStatus.BAD_REQUEST);
}
if (!vehicleTrip.getReviewStatus().equals(BusinessStatusEnum.FINISH.getStatus())) {
throw new ServiceException("行程未通过审核,请重新选择行程", HttpStatus.BAD_REQUEST);
}
boolean save = this.save(apply);
if (!save) {
throw new ServiceException("新增失败");
}
// 发送消息
CompletableFuture.runAsync(() -> {
Long createBy = vehicleTrip.getCreateBy();
String title = "有乘客申请乘车,请及时查看!";
try {
chatServerHandler.sendSystemMessageToUser(createBy, title, "4");
} catch (Exception e) {
log.error("异步发送系统消息失败用户ID: {}, 消息: {}", createBy, title, e);
}
});
return true;
}
/**
* 修改乘车申请
*
* @param req 乘车申请
* @return 是否修改成功
*/
@Override
public Boolean updateByBo(VehVehicleApplyUpdateReq req) {
// 获取旧数据
VehVehicleApply oldApply = this.getById(req.getId());
if (oldApply == null) {
throw new ServiceException("数据不存在", HttpStatus.NOT_FOUND);
}
VehVehicleApply apply = new VehVehicleApply();
BeanUtils.copyProperties(req, apply);
validEntityBeforeSave(apply);
return this.updateById(apply);
}
/**
* 车主审核
*
* @param req 审核信息
* @return 是否审核成功
*/
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean driverReview(VehVehicleApplyReviewReq req) {
Long id = req.getId();
String status = req.getStatus();
// 获取数据
VehVehicleApply apply = this.getById(id);
if (apply == null) {
throw new ServiceException("数据不存在", HttpStatus.NOT_FOUND);
}
String applyStatus = apply.getStatus();
if (Objects.equals(applyStatus, VehApplyStatusEnum.CANCELED.getValue())) {
throw new ServiceException("该申请已取消,无需审核", HttpStatus.BAD_REQUEST);
}
if (Objects.equals(applyStatus, VehApplyStatusEnum.ALREADY.getValue())
|| Objects.equals(applyStatus, VehApplyStatusEnum.ARRIVED.getValue())) {
throw new ServiceException("该申请已上车,无需审核", HttpStatus.BAD_REQUEST);
}
// 获取行程信息
VehVehicleTrip vehicleTrip = vehicleTripService.getById(apply.getTripId());
if (vehicleTrip == null) {
throw new ServiceException("行程不存在", HttpStatus.NOT_FOUND);
}
// 判断是否有审核权限
if (!Objects.equals(vehicleTrip.getCreateBy(), LoginHelper.getUserId())) {
throw new ServiceException("没有审核权限", HttpStatus.FORBIDDEN);
}
if (Objects.equals(status, VehApplyStatusEnum.CONFIRMED.getValue())) {
// 修改行程信息
int left = vehicleTrip.getLeftSeat() - apply.getPeopleNum();
if (left < 0) {
throw new ServiceException("座位不足", HttpStatus.BAD_REQUEST);
}
vehicleTrip.setLeftSeat(left);
boolean updateTrip = vehicleTripService.updateById(vehicleTrip);
if (!updateTrip) {
throw new ServiceException("行程信息更新失败", HttpStatus.ERROR);
}
}
apply.setStatus(status);
boolean updateApply = this.updateById(apply);
if (!updateApply) {
throw new ServiceException("行程信息更新失败", HttpStatus.ERROR);
}
// 通知申请人
CompletableFuture.runAsync(() -> {
Long createBy = apply.getCreateBy();
String title = Objects.equals(status, VehApplyStatusEnum.CONFIRMED.getValue()) ?
"您申请的行程已通过审核,请及时查看!" : "您申请的行程未通过审核,请及时查看!";
try {
chatServerHandler.sendSystemMessageToUser(createBy, title, "4");
} catch (Exception e) {
log.error("异步发送系统消息失败用户ID: {}, 消息: {}", createBy, title, e);
}
});
return true;
}
/**
* 修改乘车状态
*
* @param req 修改乘车状态信息
* @return 是否修改成功
*/
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean changeStatus(VehVehicleApplyChangeStatusReq req) {
Long id = req.getId();
String status = req.getStatus();
// 获取数据
VehVehicleApply apply = this.getById(id);
if (apply == null) {
throw new ServiceException("数据不存在", HttpStatus.NOT_FOUND);
}
// 判断是否为申请人
if (!Objects.equals(apply.getCreateBy(), LoginHelper.getUserId())) {
throw new ServiceException("您没有权限修改该申请", HttpStatus.FORBIDDEN);
}
String applyStatus = apply.getStatus();
// 状态校验
if (Objects.equals(applyStatus, VehApplyStatusEnum.CANCELED.getValue())) {
throw new ServiceException("该申请已取消,无法修改", HttpStatus.BAD_REQUEST);
}
if (Objects.equals(applyStatus, VehApplyStatusEnum.APPLYING.getValue())) {
throw new ServiceException("该申请正在审核中,无法修改", HttpStatus.BAD_REQUEST);
}
if (Objects.equals(applyStatus, VehApplyStatusEnum.REJECTED.getValue())) {
throw new ServiceException("该申请已被拒绝,无法修改", HttpStatus.BAD_REQUEST);
}
if (Objects.equals(applyStatus, VehApplyStatusEnum.ARRIVED.getValue())
&& !Objects.equals(status, VehApplyStatusEnum.ALREADY.getValue())) {
throw new ServiceException("请先确认上车后再确认到达目的地", HttpStatus.BAD_REQUEST);
}
apply.setStatus(status);
boolean updateApply = this.updateById(apply);
if (!updateApply) {
throw new ServiceException("行程信息更新失败", HttpStatus.ERROR);
}
// 如果状态为已到达,则修改行程信息
if (Objects.equals(status, VehApplyStatusEnum.ALREADY.getValue())) {
VehVehicleTrip vehicleTrip = vehicleTripService.getById(apply.getTripId());
if (vehicleTrip == null) {
throw new ServiceException("行程不存在", HttpStatus.NOT_FOUND);
}
if (!Objects.equals(vehicleTrip.getTripStatus(), VehTripStatusEnum.COMPLETED.getValue())) {
return true;
}
Integer leftSeat = vehicleTrip.getLeftSeat();
vehicleTrip.setLeftSeat(leftSeat + apply.getPeopleNum());
boolean updateTrip = vehicleTripService.updateById(vehicleTrip);
if (!updateTrip) {
throw new ServiceException("行程信息更新失败", HttpStatus.ERROR);
}
return true;
}
return true;
}
/**
* 取消申请
*
* @param req 取消申请信息
* @return 是否取消成功
*/
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean cancelApply(VehVehicleApplyCancelReq req) {
Long id = req.getId();
VehVehicleApply apply = this.getById(id);
if (apply == null) {
throw new ServiceException("数据不存在", HttpStatus.NOT_FOUND);
}
// 判断是否为申请人
if (!Objects.equals(apply.getCreateBy(), LoginHelper.getUserId())) {
throw new ServiceException("您没有权限取消该申请", HttpStatus.FORBIDDEN);
}
String applyStatus = apply.getStatus();
// 状态校验
if (Objects.equals(applyStatus, VehApplyStatusEnum.CANCELED.getValue())) {
throw new ServiceException("该申请已取消,无需取消", HttpStatus.BAD_REQUEST);
}
if (Objects.equals(applyStatus, VehApplyStatusEnum.ARRIVED.getValue())) {
throw new ServiceException("该申请已到达目的地,无法取消", HttpStatus.BAD_REQUEST);
}
// 获取行程信息
VehVehicleTrip vehicleTrip = vehicleTripService.getById(apply.getTripId());
if (vehicleTrip == null) {
throw new ServiceException("行程不存在", HttpStatus.NOT_FOUND);
}
// 需要通知司机的状态
if (Objects.equals(applyStatus, VehApplyStatusEnum.CONFIRMED.getValue())
|| Objects.equals(applyStatus, VehApplyStatusEnum.ALREADY.getValue())
|| Objects.equals(applyStatus, VehApplyStatusEnum.APPLYING.getValue())) {
CompletableFuture.runAsync(() -> {
Long createBy = vehicleTrip.getCreateBy();
String title = "有乘客取消预约,请及时查看!";
try {
chatServerHandler.sendSystemMessageToUser(createBy, title, "4");
} catch (Exception e) {
log.error("异步发送系统消息失败用户ID: {}, 消息: {}", createBy, title, e);
}
});
}
// 如果已确认,减少乘车人数
if (Objects.equals(applyStatus, VehApplyStatusEnum.CONFIRMED.getValue())
|| Objects.equals(applyStatus, VehApplyStatusEnum.ALREADY.getValue())) {
Integer leftSeat = vehicleTrip.getLeftSeat();
vehicleTrip.setLeftSeat(leftSeat + apply.getPeopleNum());
boolean updateTrip = vehicleTripService.updateById(vehicleTrip);
if (!updateTrip) {
throw new ServiceException("行程信息更新失败", HttpStatus.ERROR);
}
}
apply.setStatus(VehApplyStatusEnum.CANCELED.getValue());
boolean updateApply = this.updateById(apply);
if (!updateApply) {
throw new ServiceException("行程信息更新失败", HttpStatus.ERROR);
}
return true;
}
/**
* 保存前的数据校验
*/
private void validEntityBeforeSave(VehVehicleApply entity) {
//TODO 做一些数据校验,如唯一约束
String passengerPhone = entity.getPassengerPhone();
// 如果手机号为空,则使用用户手机号
if (StringUtils.isBlank(passengerPhone)) {
Long userId = LoginHelper.getUserId();
SysUserVo userVo = userService.selectUserById(userId);
passengerPhone = userVo.getPhonenumber();
}
if (StringUtils.isNotBlank(passengerPhone) && !PhoneUtil.isPhone(passengerPhone)) {
throw new ServiceException("手机号码格式不正确", HttpStatus.BAD_REQUEST);
}
}
/**
* 校验并批量删除乘车申请信息
*
* @param ids 待删除的主键集合
* @param isValid 是否进行有效性校验
* @return 是否删除成功
*/
@Override
public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
if (isValid) {
List<VehVehicleApply> applyList = this.listByIds(ids);
for (VehVehicleApply apply : applyList) {
// 判断是否能删除
if (apply.getStatus().equals(VehApplyStatusEnum.CONFIRMED.getValue())
|| apply.getStatus().equals(VehApplyStatusEnum.ALREADY.getValue())) {
throw new ServiceException("该申请已确认,无法删除", HttpStatus.BAD_REQUEST);
}
// 判断是否为申请人
if (!Objects.equals(apply.getCreateBy(), LoginHelper.getUserId())) {
throw new ServiceException("您没有权限删除该申请", HttpStatus.FORBIDDEN);
}
}
}
return baseMapper.deleteByIds(ids) > 0;
}
}

View File

@ -5,6 +5,7 @@ import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.utils.MapstructUtils; import org.dromara.common.core.utils.MapstructUtils;
import org.dromara.common.core.utils.StringUtils; import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.mybatis.core.page.PageQuery; import org.dromara.common.mybatis.core.page.PageQuery;
@ -26,6 +27,7 @@ import java.util.Map;
* @author lilemy * @author lilemy
* @date 2025-10-25 * @date 2025-10-25
*/ */
@Slf4j
@RequiredArgsConstructor @RequiredArgsConstructor
@Service @Service
public class VehVehicleInfoServiceImpl extends ServiceImpl<VehVehicleInfoMapper, VehVehicleInfo> public class VehVehicleInfoServiceImpl extends ServiceImpl<VehVehicleInfoMapper, VehVehicleInfo>
@ -38,7 +40,7 @@ public class VehVehicleInfoServiceImpl extends ServiceImpl<VehVehicleInfoMapper,
* @return 车辆信息 * @return 车辆信息
*/ */
@Override @Override
public VehVehicleInfoVo queryById(Long id){ public VehVehicleInfoVo queryById(Long id) {
return baseMapper.selectVoById(id); return baseMapper.selectVoById(id);
} }
@ -130,7 +132,7 @@ public class VehVehicleInfoServiceImpl extends ServiceImpl<VehVehicleInfoMapper,
/** /**
* 保存前的数据校验 * 保存前的数据校验
*/ */
private void validEntityBeforeSave(VehVehicleInfo entity){ private void validEntityBeforeSave(VehVehicleInfo entity) {
//TODO 做一些数据校验,如唯一约束 //TODO 做一些数据校验,如唯一约束
} }
@ -143,7 +145,7 @@ public class VehVehicleInfoServiceImpl extends ServiceImpl<VehVehicleInfoMapper,
*/ */
@Override @Override
public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) { public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
if(isValid){ if (isValid) {
//TODO 做一些业务上的校验,判断是否需要校验 //TODO 做一些业务上的校验,判断是否需要校验
} }
return baseMapper.deleteByIds(ids) > 0; return baseMapper.deleteByIds(ids) > 0;

Some files were not shown because too many files have changed in this diff Show More