Compare commits

...

128 Commits

Author SHA1 Message Date
0b29401112 09-10-netty发送时添加name字段 2025-09-11 11:14:22 +08:00
lcj
e8c78865cb 接go数据 2025-09-11 09:23:39 +08:00
zt
a8cb16ab3f 通知 2025-09-10 20:21:01 +08:00
2c45762c66 09-10-netty优化 2025-09-10 19:41:11 +08:00
056b28af31 我的任务优化、工程量清单优化、gps消息websocket连接、物资管理:物资验收入库添加附件 2025-09-10 19:38:10 +08:00
lcj
7dd6d97a3e 进度管理app 2025-09-10 19:18:30 +08:00
zt
697beb67c4 通知 2025-09-10 17:34:49 +08:00
lcj
4d627af3a1 进度管理app、注解翻译项目名称 2025-09-10 16:43:49 +08:00
ab332c462f 09-10-netty设置系统消息type为2 2025-09-10 16:11:30 +08:00
0f3d1e38be 09-10-netty优化 2025-09-10 16:00:43 +08:00
lcj
4392a287cc 修改bug 2025-09-10 10:03:53 +08:00
lcj
f38538be33 修改进度管理 2025-09-10 00:15:55 +08:00
lcj
ede1e501b4 企业级大屏 2025-09-09 23:42:24 +08:00
4a2b62cf92 我的任务bug修改 2025-09-09 21:52:49 +08:00
zt
325f392e8f 大屏,消息 2025-09-09 20:39:00 +08:00
lcj
5b991396c2 企业级大屏 2025-09-09 19:49:14 +08:00
c75563b46a 09-09-修改 2025-09-09 18:53:28 +08:00
724ebf8dbd 09-09-netty完善修改 2025-09-09 18:47:48 +08:00
a52b9078a0 gps定位接口 2025-09-09 17:47:49 +08:00
9d682a3290 09-09-netty发送系统消息 2025-09-09 17:08:39 +08:00
lcj
abad289c2b 进度管理,企业级大屏,修改bug 2025-09-09 15:42:00 +08:00
820188863e 修改一览表单价精度 2025-09-09 15:24:17 +08:00
113b5debc9 供应商bug修改 2025-09-09 10:40:17 +08:00
6b4cd4ae0d Merge remote-tracking branch 'origin/dev' into dev 2025-09-09 09:12:54 +08:00
261dd0b643 09-09-netty消息搭建 2025-09-09 09:12:19 +08:00
zt
71f3810e51 Merge remote-tracking branch 'origin/dev' into dev 2025-09-08 20:03:11 +08:00
zt
fa835684d4 消息 2025-09-08 20:02:51 +08:00
e19ef3003a bug修改 2025-09-08 20:00:29 +08:00
zt
8a18223d06 二维码 2025-09-08 16:38:01 +08:00
c90828f98e 09-08-土地流转新增type区分完成和未完成 2025-09-08 16:02:26 +08:00
2ecb0063bf 限价、投标、招采、物资一览bug修改 2025-09-08 15:33:03 +08:00
zt
998547e63f bug 2025-09-08 11:47:14 +08:00
zt
78829ef5e7 bug 2025-09-08 11:42:36 +08:00
zt
da0dd8f78f bug 2025-09-06 19:20:40 +08:00
lcj
96d0406931 物资、里程碑进度execl导出 2025-09-06 19:07:10 +08:00
d7616960c6 gpsbug修改 2025-09-06 18:48:39 +08:00
66e8495859 bug修改 2025-09-06 18:35:51 +08:00
95c2858a64 bug修改 2025-09-06 18:33:06 +08:00
5cbb0c630b bug修改 2025-09-06 18:24:27 +08:00
lcj
e0cc521291 进度、物资管理模块 2025-09-06 15:30:57 +08:00
ff9f49b1d9 09-06-修复bug,完善功能 2025-09-06 15:16:41 +08:00
zt
b738bb821d app 2025-09-06 13:06:33 +08:00
2cea57646d 设计管理设计出图导入导出 2025-09-06 12:52:12 +08:00
lcj
ab5cd491d6 修改ai工单 2025-09-04 22:25:10 +08:00
lcj
3f3e20a64b 项目大屏进度数据统计,修改ai工单逻辑 2025-09-04 21:30:18 +08:00
zt
f5d9cb7fc1 Merge remote-tracking branch 'origin/dev' into dev 2025-09-04 21:27:26 +08:00
zt
5b05d2eb40 版本号 2025-09-04 21:27:11 +08:00
fbffc18a9f 考试卷app 2025-09-04 21:26:19 +08:00
zt
490820d080 版本号 2025-09-04 20:26:56 +08:00
zt
8e9b7c9b14 优化 2025-09-04 20:17:25 +08:00
ae3738c098 增加iText 7 核心模块 2025-09-04 19:57:50 +08:00
zt
127059e934 Merge remote-tracking branch 'origin/dev' into dev 2025-09-04 17:45:42 +08:00
zt
742e67af23 考勤 2025-09-04 17:45:21 +08:00
lcj
4e61a4afe9 人员工资、退场,巡检工单 2025-09-04 17:05:00 +08:00
43d249d68a 配置文件修改 2025-09-04 15:18:14 +08:00
dc8a89f05e gps接口 2025-09-04 11:23:50 +08:00
17d4041ef3 变更jar,增加考试卷 2025-09-04 10:56:54 +08:00
zt
d19cda78b3 考勤 2025-09-04 09:27:21 +08:00
2f0b548f20 BUG修改 2025-09-03 20:36:55 +08:00
lcj
f1aed20560 安全日志、ai工单接口 2025-09-03 15:44:13 +08:00
zt
8c412d033b 考勤 2025-09-03 15:39:13 +08:00
a929225ed5 09-03-修复供应商入库修改 2025-09-03 15:11:01 +08:00
zt
3b6b1d53a9 app,角色,菜单 2025-09-03 10:26:28 +08:00
lcj
81162852a2 修改人员实名认证 2025-09-03 10:24:09 +08:00
lcj
f584e6233c 修改bug 2025-09-02 20:30:01 +08:00
e8da350f0b 收资清单模板导出接口优化 2025-09-02 19:08:16 +08:00
e916829032 收资清单模板导出接口优化 2025-09-02 18:58:56 +08:00
8c131cb9a5 新增收资清单模板导出接口 2025-09-02 18:57:32 +08:00
lcj
9aef0d4b86 物资领料逻辑修改 app人员管理接口 2025-09-02 17:32:42 +08:00
de5c569f88 09-02-更新apifox模块 2025-09-02 16:54:01 +08:00
lcj
79edeb6ccd 安全模块、人员模块app接口 2025-09-02 15:14:06 +08:00
a3ef525ab6 修改设计出图bug 2025-09-02 09:54:26 +08:00
659e4e3d5f 09-02-完善税率字段对应sql 2025-09-02 09:11:33 +08:00
lcj
96e6c75949 修改材料设备出入库逻辑 2025-09-01 17:33:19 +08:00
711c473749 09-01-供应商入库新增导入,修改字段;物资设备清单新增按设备名称查询,新增税率字段. 2025-09-01 17:08:21 +08:00
4636aa3c05 修改投标成本清单列表查询接口 2025-08-30 06:49:46 +08:00
lcj
788f13fa7b 修改采购联系单 2025-08-30 06:29:25 +08:00
a588b94310 修改分标策划、招标计划新增接口 2025-08-30 04:59:51 +08:00
lcj
b44de3ff49 修改站班会日期查询 2025-08-30 03:58:37 +08:00
lcj
5d643fbc6a 修改班组 2025-08-30 03:47:18 +08:00
lcj
19fd73f4c8 修改施工人员身份证转换 2025-08-30 03:32:36 +08:00
lcj
a07792c8e3 修改进度、产值 2025-08-30 03:25:03 +08:00
zt
8a29ffdd2e 优化 2025-08-30 02:05:48 +08:00
d46f672b9a 修改联系单排序 2025-08-30 00:53:31 +08:00
lcj
d6528845e4 修改项目结构,施工产值 2025-08-30 00:33:17 +08:00
lcj
0ff805683f 修改物质供货总计划逻辑 2025-08-29 23:18:22 +08:00
3f79a955b3 代码合并 2025-08-29 22:37:56 +08:00
zt
971c8c277d 优化 2025-08-29 20:52:14 +08:00
lcj
12f02638a2 修改物质供货总计划逻辑 2025-08-29 20:15:53 +08:00
b1c21b1f88 Merge remote-tracking branch 'origin/dev' into dev 2025-08-29 20:02:16 +08:00
642d5c73ca feat:增加批量新增或修改物资的权限 2025-08-29 19:59:43 +08:00
zt
d7054d6f02 优化 2025-08-29 18:17:04 +08:00
e85bbca73b 设计方案权限标识符 2025-08-29 17:11:21 +08:00
lcj
3f5396e347 修改施工产值查询 2025-08-29 14:55:23 +08:00
zt
c9041df9b8 优化 2025-08-29 04:24:59 +08:00
lcj
87e510aafc 修改权限字符 2025-08-29 03:54:05 +08:00
4e4497a07f 设计权限 2025-08-29 01:40:18 +08:00
lcj
7c4f6b8add 权限字符 2025-08-29 01:38:38 +08:00
7daf2ada66 设计权限 2025-08-29 01:30:46 +08:00
zt
073f614a21 优化 2025-08-28 23:32:19 +08:00
zt
1878a7dab3 优化 2025-08-28 22:37:32 +08:00
lcj
aa31eb97b9 修改部门和项目、分包关联 2025-08-28 21:28:25 +08:00
lcj
4f8776930e 添加分包和供应商关联 2025-08-28 19:56:44 +08:00
f171238029 分标策划、招标计划bug修改 2025-08-28 18:19:42 +08:00
zt
377892f691 优化 2025-08-28 14:41:12 +08:00
zt
7e0cd7c946 优化 2025-08-28 14:37:30 +08:00
936145138e 限价修改bug修改 2025-08-28 14:33:37 +08:00
lcj
e6f37662b0 修改权限逻辑 2025-08-28 04:20:40 +08:00
3ea6050dc3 合约规划、合规性手续bug修改 2025-08-27 18:41:22 +08:00
f7940e23fa 08-27-分项工程单价导入修改 2025-08-27 17:32:36 +08:00
zt
3d57edf374 优化 2025-08-27 16:43:36 +08:00
797e913b69 变更 2025-08-26 21:30:08 +08:00
lcj
2e40c3ac6d 修改进度计划、施工产值 2025-08-26 21:15:59 +08:00
61dc34c79e 变更 2025-08-26 21:12:56 +08:00
zt
5772b5fc83 优化 2025-08-26 21:05:13 +08:00
zt
971101d5e8 消息优化 2025-08-26 14:58:08 +08:00
lcj
508d81bedf 修bug 2025-08-25 20:20:43 +08:00
zt
57a39ad727 bug、优化 2025-08-25 20:13:30 +08:00
126fdeb6ba bug修改 2025-08-25 19:55:25 +08:00
zt
8e07ecc69b bug、优化 2025-08-25 18:32:19 +08:00
c26f23cfb0 1 2025-08-25 16:01:36 +08:00
lcj
b008a472fc 修bug 2025-08-23 15:16:49 +08:00
c2ce524ef3 变更产值 2025-08-23 10:32:14 +08:00
zt
a2b33f3b53 优化 2025-08-23 10:24:41 +08:00
551dd084b8 11 2025-08-23 10:17:28 +08:00
35323fa352 设计完工产值 2025-08-23 09:02:25 +08:00
3f6af11dff Merge remote-tracking branch 'origin/dev' into lcj 2025-08-23 09:01:23 +08:00
2fda6c8930 设计完工产值 2025-08-23 06:40:25 +08:00
654 changed files with 25963 additions and 3260 deletions

Binary file not shown.

View File

@ -307,7 +307,6 @@
<artifactId>snail-job-client-job-core</artifactId>
<version>${snailjob.version}</version>
</dependency>
<!-- 加密包引入 -->
<dependency>
<groupId>org.bouncycastle</groupId>

View File

@ -6,6 +6,7 @@ import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.codec.Base64;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson.JSONObject;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@ -41,16 +42,20 @@ import org.dromara.web.domain.vo.TenantListVo;
import org.dromara.web.service.IAuthStrategy;
import org.dromara.web.service.SysLoginService;
import org.dromara.web.service.SysRegisterService;
import org.dromara.websocket.domain.ChatGroup;
import org.dromara.websocket.service.Impl.ChatGroupServiceImpl;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
/**
* 认证
@ -72,6 +77,7 @@ public class AuthController {
private final ISysSocialService socialUserService;
private final ISysClientService clientService;
private final ScheduledExecutorService scheduledExecutorService;
private final ChatGroupServiceImpl chatGroupService;
/**
@ -103,10 +109,7 @@ public class AuthController {
// Long userId = LoginHelper.getUserId();
// scheduledExecutorService.schedule(() -> {
// SseMessageDto dto = new SseMessageDto();
// dto.setMessage("欢迎登录新能源项目管理系统");
// dto.setUserIds(List.of(userId));
// SseMessageUtils.publishMessage(dto);
// chatGroupService.createSystem(userId,client.getClientKey());
// }, 5, TimeUnit.SECONDS);
return R.ok(loginVo);
}
@ -193,6 +196,12 @@ public class AuthController {
return R.ok();
}
@PostMapping("/app/register")
public R<Void> appRegister(@Validated @RequestBody RegisterBody user) {
registerService.appRegister(user);
return R.ok();
}
/**
* 登录页面租户下拉框
*

View File

@ -55,7 +55,8 @@ public class UserActionListener implements SaTokenListener {
String username = (String) loginModel.getExtra(LoginHelper.USER_NAME_KEY);
String tenantId = (String) loginModel.getExtra(LoginHelper.TENANT_KEY);
dto.setUserName(username);
dto.setClientKey((String) loginModel.getExtra(LoginHelper.CLIENT_KEY));
String clientId = (String) loginModel.getExtra(LoginHelper.CLIENT_KEY);
dto.setClientKey(clientId);
dto.setDeviceType(loginModel.getDevice());
dto.setDeptName((String) loginModel.getExtra(LoginHelper.DEPT_NAME_KEY));
TenantHelper.dynamic(tenantId, () -> {
@ -75,7 +76,7 @@ public class UserActionListener implements SaTokenListener {
SpringUtils.context().publishEvent(logininforEvent);
// 更新登录信息
loginService.recordLoginInfo((Long) loginModel.getExtra(LoginHelper.USER_KEY), ip);
log.info("user doLogin, userId:{}, token:{}", loginId, tokenValue);
log.info("user doLogin, userId:{}, token:{}, clientid{}", loginId, tokenValue, clientId);
}
/**

View File

@ -154,6 +154,12 @@ public class SysLoginService {
loginUser.setTenantId(user.getTenantId());
loginUser.setUserId(userId);
loginUser.setDeptId(user.getDeptId());
List<Long> projectIds = user.getProjectIds();
Long projectId = null;
if (CollUtil.isNotEmpty(projectIds)) {
projectId = projectIds.getFirst();
}
loginUser.setProjectId(projectId);
loginUser.setUsername(user.getUserName());
loginUser.setNickname(user.getNickName());
loginUser.setUserType(user.getUserType());
@ -188,7 +194,7 @@ public class SysLoginService {
/**
* 登录校验
*/
public void checkLogin(LoginType loginType, String tenantId, String username, Supplier<Boolean> supplier) {
public void checkLogin(LoginType loginType, String tenantId, String username, Supplier<Boolean> supplier) {
String errorKey = CacheConstants.PWD_ERR_CNT_KEY + username;
String loginFail = Constants.LOGIN_FAIL;

View File

@ -77,6 +77,46 @@ public class SysRegisterService {
recordLogininfor(tenantId, username, Constants.REGISTER, MessageUtils.message("user.register.success"));
}
/**
* 注册
*/
public void appRegister(RegisterBody registerBody) {
String tenantId = registerBody.getTenantId();
String username = registerBody.getPhonenumber();
String password = registerBody.getPassword();
// 校验用户类型是否存在
String userType = UserType.getUserType(registerBody.getUserType()).getUserType();
// 校验密码是否符合要求
String pattern = "^(?!.*\\s)(?!^[a-zA-Z]+$)(?!^[0-9]+$)(?!^[^a-zA-Z0-9]+$)(?!^[a-zA-Z0-9]+$).{8,18}$";
boolean isValid = password.matches(pattern);
if (!isValid) {
throw new UserException("注册失败密码需满足818位包含大小写字母、数字、特殊字符中的至少三种组合");
}
// 验证码开关
SysUserBo sysUser = new SysUserBo();
sysUser.setUserName(username);
sysUser.setNickName(username);
sysUser.setPhonenumber(username);
sysUser.setPassword(BCrypt.hashpw(password));
sysUser.setUserType(userType);
sysUser.setEmail(registerBody.getEmail());
boolean exist = TenantHelper.dynamic(tenantId, () -> {
return userMapper.exists(new LambdaQueryWrapper<SysUser>()
.eq(SysUser::getPhonenumber, sysUser.getPhonenumber()));
});
if (exist) {
throw new UserException("user.register.save.error", username);
}
boolean regFlag = userService.registerUser(sysUser, tenantId);
if (!regFlag) {
throw new UserException("user.register.error");
}
recordLogininfor(tenantId, username, Constants.REGISTER, MessageUtils.message("user.register.success"));
}
/**
* 校验验证码
*

View File

@ -3,6 +3,7 @@ package org.dromara.web.service.impl;
import cn.dev33.satoken.secure.BCrypt;
import cn.dev33.satoken.stp.SaLoginModel;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import lombok.RequiredArgsConstructor;
@ -24,6 +25,8 @@ import org.dromara.common.redis.utils.RedisUtils;
import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.common.tenant.helper.TenantHelper;
import org.dromara.common.web.config.properties.CaptchaProperties;
import org.dromara.project.domain.BusUserProjectRelevancy;
import org.dromara.project.service.IBusUserProjectRelevancyService;
import org.dromara.system.domain.SysUser;
import org.dromara.system.domain.vo.SysClientVo;
import org.dromara.system.domain.vo.SysUserVo;
@ -33,6 +36,8 @@ import org.dromara.web.service.IAuthStrategy;
import org.dromara.web.service.SysLoginService;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* 密码认证策略
*
@ -46,6 +51,7 @@ public class PasswordAuthStrategy implements IAuthStrategy {
private final CaptchaProperties captchaProperties;
private final SysLoginService loginService;
private final SysUserMapper userMapper;
private final IBusUserProjectRelevancyService userProjectRelevancyService;
@Override
public LoginVo login(String body, SysClientVo client) {
@ -117,6 +123,14 @@ public class PasswordAuthStrategy implements IAuthStrategy {
log.info("登录用户:{} 已被停用.", username);
throw new UserException("user.blocked", username);
}
if (!SystemConstants.SUPER_ADMIN_ID.equals(user.getUserId())) {
List<BusUserProjectRelevancy> list = userProjectRelevancyService.lambdaQuery()
.eq(BusUserProjectRelevancy::getUserId, user.getUserId())
.list();
if (CollUtil.isNotEmpty(list)) {
user.setProjectIds(list.stream().map(BusUserProjectRelevancy::getProjectId).toList());
}
}
return user;
}

View File

@ -13,13 +13,13 @@ spring.boot.admin.client:
--- # snail-job 配置
snail-job:
enabled: false
enabled: true
# 需要在 SnailJob 后台组管理创建对应名称的组,然后创建任务的时候选择对应的组,才能正确分派任务
group: "ruoyi_group"
# SnailJob 接入验证令牌 详见 script/sql/ry_job.sql `sj_group_config` 表
token: "SJ_cKqBTPzCsWA3VyuCfFoccmuIEGXjr5KT"
server:
host: 192.168.110.119
host: 127.0.0.1
port: 17888
# 命名空间UUID 详见 script/sql/ry_job.sql `sj_namespace`表`unique_id`字段
namespace: ${spring.profiles.active}
@ -53,13 +53,13 @@ spring:
username: xinnengyuandev
password: StRWCZdZirysNSs2
# 从库数据源
# slave:
# lazy: true
# type: ${spring.datasource.type}
# driverClassName: com.mysql.cj.jdbc.Driver
# url: jdbc:mysql://192.168.110.2:13386/zmkgdev?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true
# username: zmkgdev
# password: JhYxREf25AXdy3h8
slave:
lazy: true
type: ${spring.datasource.type}
driverClassName: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://192.168.110.2:13386/zmkgdev?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true
username: zmkgdev
password: JhYxREf25AXdy3h8
# oracle:
# type: ${spring.datasource.type}
# driverClassName: oracle.jdbc.OracleDriver
@ -287,7 +287,7 @@ sparta:
id-card:
encrypt-key: 7ae260d150a14027d2238a1cf80a48ef
recognizer:
url: http://192.168.110.5:50070
url: http://192.168.110.5:50071
qrCode:
url: http://192.168.110.151:7788

View File

@ -286,7 +286,7 @@ sparta:
id-card:
encrypt-key: 7ae260d150a14027d2238a1cf80a48ef
recognizer:
url: http://192.168.110.5:50070
url: http://192.168.110.5:50071
qrCode:
url: http://xny.yj-3d.com:7788

View File

@ -125,6 +125,8 @@ security:
# todo 仅测试
- /facility/matrix/**
- /**/changxie/callback/**
- /gps/equipment/dataAcceptance
- /resource/oss/upload
# 多租户配置
tenant:
@ -194,6 +196,7 @@ api-decrypt:
- /actuator/** # 放行监控接口
- /other/ys7Device/webhook # 放行萤石云设备回调接口
- /auth/register # 放行注册接口
- /gps/equipment/dataAcceptance # GPS数据接收接口
springdoc:
api-docs:
@ -247,8 +250,8 @@ springdoc:
packages-to-scan: org.dromara.design
- group: 13.工作流模块
packages-to-scan: org.dromara.workflow
- group: 14.罗成模块
packages-to-scan: org.dromara.cory
# - group: 14.罗成模块
# packages-to-scan: org.dromara.cory
- group: 15.无人机模块
packages-to-scan: org.dromara.drone
- group: 20.代码生成模块
@ -269,11 +272,13 @@ springdoc:
packages-to-scan: org.dromara.bigscreen
- group: 22.投标管理模块
packages-to-scan: org.dromara.bidding
- group: 23.GPS定位模块
packages-to-scan: org.dromara.gps
# - group: 20.合同模块
# packages-to-scan: org.dromara.ctr
# - group: 21.招标模块
# packages-to-scan: org.dromara.tender
- group: 24.招标模块
packages-to-scan: org.dromara.tender
# knife4j的增强配置不需要增强可以不配
@ -324,11 +329,17 @@ management:
sse:
enabled: true
path: /resource/sse
wait: /task/taskWaiting
copy: /task/taskCopyList
project: /personnel-management/project
violationRecord: /safety-management/ai/violationRecord
drawing: /design-management/volumeCatalog
--- # websocket
websocket:
# 如果关闭 需要和前端开关一起关闭
enabled: false
enabled: true
# 路径
path: /resource/websocket
# 设置访问源地址

View File

@ -1,18 +1,28 @@
package org.dromara.test;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.io.FileUtil;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.dromara.contractor.domain.SubConstructionUser;
import org.dromara.contractor.service.ISubConstructionUserService;
import org.dromara.contractor.service.ISubUserSalaryDetailService;
import org.dromara.design.service.IDesTechnicalStandardService;
import org.dromara.facility.domain.FacMatrix;
import org.dromara.facility.service.IFacMatrixService;
import org.dromara.facility.service.IFacPhotovoltaicPanelPartsService;
import org.dromara.progress.service.IPgsProgressCategoryService;
import org.dromara.progress.service.IPgsProgressCategoryTemplateService;
import org.dromara.project.service.IBusProjectService;
import org.dromara.system.service.ISysDeptService;
import org.dromara.tender.service.impl.TenderSupplierInputServiceImpl;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.time.LocalDate;
import java.util.Date;
import java.util.List;
@ -30,6 +40,9 @@ public class DemoTest {
@Resource
private IPgsProgressCategoryService progressCategoryService;
@Resource
private IPgsProgressCategoryTemplateService progressCategoryTemplateService;
@Resource
private IFacMatrixService matrixService;
@ -41,6 +54,14 @@ public class DemoTest {
@Resource
private ISysDeptService deptService;
@Autowired
private TenderSupplierInputServiceImpl tenderSupplierInputService;
@Resource
private ISubConstructionUserService constructionUserService;
@Resource
private ISubUserSalaryDetailService userSalaryDetailService;
@Test
void test() {
@ -85,8 +106,38 @@ public class DemoTest {
}
@Test
void testProject() {
// 初始化施工类型模版
Boolean init = progressCategoryTemplateService.initTemplateByProject(1958935730389606402L);
}
/* @Test
void testDeptProject() {
deptService.selectProjectIdById(100L);
deptService.selectProjectIdById(1937478258803171329L);
}*/
@Test
void tenderExport() {
// 同步修改用户表的team_id字段并添加入场时间
LambdaUpdateWrapper<SubConstructionUser> constructionUserLuw = Wrappers.lambdaUpdate(SubConstructionUser.class)
.in(SubConstructionUser::getId, 1961446214960435201L, 1963077776210710529L, 1963080543771832321L, 1963151975159324673L)
.set(SubConstructionUser::getEntryDate, new Date());
constructionUserService.update(constructionUserLuw);
}
@Test
void testSalary() {
List<SubConstructionUser> list = constructionUserService.lambdaQuery()
.eq(SubConstructionUser::getProjectId, 1897160897167638529L)
.list();
if (CollUtil.isNotEmpty(list)) {
for (SubConstructionUser user : list) {
for (int i = 1; i < 7; i++) {
userSalaryDetailService.insertByAttendance(user.getSysUserId(), LocalDate.now().minusDays(i));
}
}
}
}
}

View File

@ -1,109 +0,0 @@
package org.dromara.test;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.dromara.formalities.domain.bo.AddBusFormalitiesAreConsolidatedBo;
import org.dromara.formalities.domain.bo.BusFormalitiesAreConsolidatedBo;
import org.dromara.formalities.domain.bo.BusListOfFormalitiesBo;
import org.dromara.formalities.domain.vo.BusListOfFormalitiesVo;
import org.dromara.formalities.service.IBusFormalitiesAreConsolidatedService;
import org.dromara.formalities.service.IBusListOfFormalitiesService;
import org.dromara.manager.ys7manager.Ys7Manager;
import org.dromara.manager.ys7manager.Ys7RequestUtils;
import org.dromara.manager.ys7manager.vo.Ys7QueryDeviceResponseVo;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.web.multipart.MultipartFile;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
/**
* @author lilemy
* @date 2025/6/12 17:06
*/
@Slf4j
@SpringBootTest
public class Ys7Test {
@Resource
private Ys7Manager ys7Manager;
@Test
void test() {
String token = ys7Manager.getToken();
List<Ys7QueryDeviceResponseVo> ys7QueryDeviceResponseVos = Ys7RequestUtils.queryDeviceVoList(token, 1, 20);
System.out.println(ys7QueryDeviceResponseVos);
}
@Test
void testCaptureDevicePic() {
String pic = ys7Manager.getCaptureDevicePic("AE9470016", 1, 1);
System.out.println(pic);
}
@Resource
private IBusListOfFormalitiesService busListOfFormalitiesService;
@Test
public void test111(){
BusListOfFormalitiesBo busListOfFormalitiesBo = new BusListOfFormalitiesBo();
// busListOfFormalitiesBo.setName("test1");
// busListOfFormalitiesBo.setPid(1955976169241026561L);
// busListOfFormalitiesService.insertByBo(busListOfFormalitiesBo);
// List<BusListOfFormalitiesVo> tree = busListOfFormalitiesService.getTree(busListOfFormalitiesBo);
// System.out.println(tree);
Boolean b = busListOfFormalitiesService.deleteWithValidByIds(1955976169241026561L, true);
System.out.println(b);
}
@Resource
private IBusFormalitiesAreConsolidatedService formalitiesAreConsolidatedService;
@Test
public void test222(){
BusFormalitiesAreConsolidatedBo busFormalitiesAreConsolidatedBo = new BusFormalitiesAreConsolidatedBo();
// List<AddBusFormalitiesAreConsolidatedBo> addBusFormalitiesAreConsolidatedBos = new ArrayList<>();
// AddBusFormalitiesAreConsolidatedBo bo1 = new AddBusFormalitiesAreConsolidatedBo();
// bo1.setFormalitiesId(1955977461032103939L);
// AddBusFormalitiesAreConsolidatedBo bo2 = new AddBusFormalitiesAreConsolidatedBo();
// bo2.setFormalitiesId(1955977461032103940L);
// bo2.setFormalitiesPid(1955977461032103939L);
// AddBusFormalitiesAreConsolidatedBo bo3 = new AddBusFormalitiesAreConsolidatedBo();
// bo3.setFormalitiesId(1955977461032103941L);
// bo3.setFormalitiesPid(1955977461032103939L);
// AddBusFormalitiesAreConsolidatedBo bo4 = new AddBusFormalitiesAreConsolidatedBo();
// bo4.setFormalitiesId(1955977461032103942L);
// bo4.setFormalitiesPid(1955977461032103939L);
// addBusFormalitiesAreConsolidatedBos.add(bo1);
// addBusFormalitiesAreConsolidatedBos.add(bo2);
// addBusFormalitiesAreConsolidatedBos.add(bo3);
// addBusFormalitiesAreConsolidatedBos.add(bo4);
// busFormalitiesAreConsolidatedBo.setAddBusFormalitiesAreConsolidatedBos(addBusFormalitiesAreConsolidatedBos);
// busFormalitiesAreConsolidatedBo.setProjectId(1555L);
// Boolean b = formalitiesAreConsolidatedService.insertByBo(busFormalitiesAreConsolidatedBo);
// System.out.println(b);
// busFormalitiesAreConsolidatedBo.setId(1956013379818409985L);
// busFormalitiesAreConsolidatedBo.setHead("舟山");
// busFormalitiesAreConsolidatedBo.setPlanTheStartTime(new Date());
// busFormalitiesAreConsolidatedBo.setRemark("asdasd");
// busFormalitiesAreConsolidatedBo.setStatus(0);
// MultipartFile[] files = {};
// Boolean b = formalitiesAreConsolidatedService.updateByBo(busFormalitiesAreConsolidatedBo, files);
// System.out.println(b);
}
@Resource
private IBusSegmentedIndicatorPlanningService busSegmentedIndicatorPlanningService;
@Test
public void test666(){
BusSegmentedIndicatorPlanningBo bo = new BusSegmentedIndicatorPlanningBo();
bo.setId(1958169755747459073L);
List<BusBillofquantitiesLimitListVo> more = busSegmentedIndicatorPlanningService.getMore(bo);
System.out.println(more);
}
}

View File

@ -80,4 +80,9 @@ public interface CacheNames {
*/
String ONLINE_TOKEN = "online_tokens";
/**
* 项目名称
*/
String PROJECT_NAME = "project_name#30d";
}

View File

@ -4,11 +4,12 @@ import lombok.Data;
import lombok.NoArgsConstructor;
import org.dromara.common.core.domain.dto.PostDTO;
import org.dromara.common.core.domain.dto.RoleDTO;
import org.dromara.common.core.domain.vo.SysProjectRoleMenuVo;
import org.dromara.common.core.domain.vo.SysProjectRolePermissionVo;
import java.io.Serial;
import java.io.Serializable;
import java.util.List;
import java.util.Set;
/**
* 登录用户身份权限
@ -37,6 +38,11 @@ public class LoginUser implements Serializable {
*/
private Long deptId;
/**
* 项目ID
*/
private Long projectId;
/**
* 部门类别编码
*/
@ -90,12 +96,12 @@ public class LoginUser implements Serializable {
/**
* 菜单权限
*/
private Set<String> menuPermission;
private List<SysProjectRoleMenuVo> menuPermission;
/**
* 角色权限
*/
private Set<String> rolePermission;
private List<SysProjectRolePermissionVo> rolePermission;
/**
* 用户名

View File

@ -30,4 +30,12 @@ public class RegisterBody extends LoginBody {
private String userType;
private Long projectId;
private String email;
private String phonenumber;
private Long deptId;
}

View File

@ -0,0 +1,28 @@
package org.dromara.common.core.domain.vo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Set;
/**
* @author lilemy
* @date 2025-08-27 18:14
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class SysProjectRoleMenuVo {
/**
* 项目id
*/
private Long projectId;
/**
* 项目菜单权限
*/
private Set<String> projectPermissions;
}

View File

@ -0,0 +1,28 @@
package org.dromara.common.core.domain.vo;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.util.Set;
/**
* @author lilemy
* @date 2025-08-27 17:53
*/
@Data
public class SysProjectRolePermissionVo implements Serializable {
@Serial
private static final long serialVersionUID = -6552769878716622338L;
/**
* 项目id
*/
private Long projectId;
/**
* 项目菜单权限
*/
private Set<String> projectRoles;
}

View File

@ -0,0 +1,17 @@
package org.dromara.common.core.service;
/**
* @author lilemy
* @date 2025-09-10 16:15
*/
public interface ProjectService {
/**
* 通过项目ID查询项目名称
*
* @param projectId 项目ID
* @return 项目名称
*/
String selectProjectNameById(Long projectId);
}

View File

@ -74,7 +74,7 @@ public interface UserService {
* @param roleIds 角色ids
* @return 用户
*/
List<UserDTO> selectUsersByRoleIds(List<Long> roleIds);
List<UserDTO> selectUsersByRoleIds(List<Long> roleIds,Long projectId);
/**
* 通过部门ID查询用户

View File

@ -376,4 +376,19 @@ public class DateUtils extends org.apache.commons.lang3.time.DateUtils {
}
}
/**
* 校验日期范围
*
* @param startDate 开始日期
* @param endDate 结束日期
* @return true 表示日期范围有效false 表示日期范围无效
*/
public static boolean isValidDateRange(LocalDate startDate, LocalDate endDate) {
try {
return !startDate.isAfter(endDate); // start <= end
} catch (DateTimeParseException e) {
return false; // 格式非法
}
}
}

View File

@ -71,7 +71,7 @@ public class MybatisPlusConfig {
public PaginationInnerInterceptor paginationInnerInterceptor() {
PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor();
// 分页合理化
paginationInnerInterceptor.setOverflow(true);
paginationInnerInterceptor.setOverflow(false);
return paginationInnerInterceptor;
}

View File

@ -1,13 +1,18 @@
package org.dromara.common.satoken.core.service;
import cn.dev33.satoken.stp.StpInterface;
import cn.hutool.core.collection.CollUtil;
import org.dromara.common.core.domain.model.LoginUser;
import org.dromara.common.core.domain.vo.SysProjectRoleMenuVo;
import org.dromara.common.core.domain.vo.SysProjectRolePermissionVo;
import org.dromara.common.core.enums.UserType;
import org.dromara.common.satoken.utils.LoginHelper;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
/**
* sa-token 权限管理实现类
@ -24,7 +29,28 @@ public class SaPermissionImpl implements StpInterface {
LoginUser loginUser = LoginHelper.getLoginUser();
UserType userType = UserType.getUserType(loginUser.getUserType());
if (userType == UserType.SYS_USER) {
return new ArrayList<>(loginUser.getMenuPermission());
Long projectId = loginUser.getProjectId();
List<SysProjectRoleMenuVo> menuPermission = loginUser.getMenuPermission();
if (CollUtil.isNotEmpty(menuPermission)) {
if (projectId != null) {
Map<Long, List<SysProjectRoleMenuVo>> map = menuPermission.stream()
.collect(Collectors.groupingBy(SysProjectRoleMenuVo::getProjectId));
if (map.containsKey(projectId)) {
return map.get(projectId).stream()
.map(SysProjectRoleMenuVo::getProjectPermissions)
.flatMap(Set::stream)
.filter(s -> !s.isEmpty())
.distinct()
.toList();
}
} else {
List<Set<String>> list = menuPermission.stream().map(SysProjectRoleMenuVo::getProjectPermissions).toList();
return list.stream().flatMap(Set::stream).filter(s -> !s.isEmpty()).distinct().toList();
}
} else {
return new ArrayList<>();
}
} else if (userType == UserType.APP_USER) {
// 其他端 自行根据业务编写
}
@ -40,7 +66,27 @@ public class SaPermissionImpl implements StpInterface {
LoginUser loginUser = LoginHelper.getLoginUser();
UserType userType = UserType.getUserType(loginUser.getUserType());
if (userType == UserType.SYS_USER) {
return new ArrayList<>(loginUser.getRolePermission());
Long projectId = loginUser.getProjectId();
List<SysProjectRolePermissionVo> rolePermission = loginUser.getRolePermission();
if (CollUtil.isNotEmpty(rolePermission)) {
if (projectId != null) {
Map<Long, List<SysProjectRolePermissionVo>> map = rolePermission.stream()
.collect(Collectors.groupingBy(SysProjectRolePermissionVo::getProjectId));
if (map.containsKey(projectId)) {
return map.get(projectId).stream()
.map(SysProjectRolePermissionVo::getProjectRoles)
.flatMap(Set::stream)
.filter(s -> !s.isEmpty())
.distinct()
.toList();
}
} else {
List<Set<String>> list = rolePermission.stream().map(SysProjectRolePermissionVo::getProjectRoles).toList();
return list.stream().flatMap(Set::stream).filter(s -> !s.isEmpty()).distinct().toList();
}
} else {
return new ArrayList<>();
}
} else if (userType == UserType.APP_USER) {
// 其他端 自行根据业务编写
}

View File

@ -39,6 +39,7 @@ public class LoginHelper {
public static final String DEPT_NAME_KEY = "deptName";
public static final String DEPT_CATEGORY_KEY = "deptCategory";
public static final String CLIENT_KEY = "clientid";
public static final String PROJECT_KEY = "projectId";
/**
* 登录系统 基于 设备类型
@ -131,6 +132,10 @@ public class LoginHelper {
return Convert.toStr(getExtra(DEPT_CATEGORY_KEY));
}
public static Long getProjectId() {
return Convert.toLong(getExtra(PROJECT_KEY));
}
/**
* 获取当前 Token 的扩展信息
*
@ -191,7 +196,7 @@ public class LoginHelper {
* @return 结果
*/
public static boolean isTenantAdmin() {
return Convert.toBool(isTenantAdmin(getLoginUser().getRolePermission()));
return true;
}
/**

View File

@ -18,4 +18,17 @@ public class SseProperties {
* 路径
*/
private String path;
private String wait;
private String copy;
private String project;
private String violationRecord;
private String drawing;
}

View File

@ -135,7 +135,7 @@ public class SseEmitterManager {
broadcastMessage.setMessage(sseMessageDto.getMessage());
broadcastMessage.setUserIds(sseMessageDto.getUserIds());
broadcastMessage.setRoute(sseMessageDto.getRoute());
broadcastMessage.setDetailId(sseMessageDto.getDetailId());
broadcastMessage.setProjectId(sseMessageDto.getProjectId());
RedisUtils.publish(SSE_TOPIC, broadcastMessage, consumer -> {
log.info("SSE发送主题订阅消息topic:{} session keys:{} message:{}",
SSE_TOPIC, sseMessageDto.getUserIds(), sseMessageDto.getMessage());

View File

@ -0,0 +1,25 @@
package org.dromara.common.sse.dto;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
@Data
public class SeeMessageContentDto implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 需要发送的消息
*/
private String type;
/**
* 路由
*/
private String content;
}

View File

@ -33,8 +33,13 @@ public class SseMessageDto implements Serializable {
private String route;
/**
* 详情
* 项目id
*/
private String detailId;
private Long projectId;
/**
* 是否记录
*/
private Boolean isRecord = true;
}

View File

@ -32,4 +32,9 @@ public interface TransConstant {
*/
String OSS_ID_TO_URL = "oss_id_to_url";
/**
* 项目id转名称
*/
String PROJECT_ID_TO_NAME = "project_id_to_name";
}

View File

@ -0,0 +1,35 @@
package org.dromara.common.translation.core.impl;
import jakarta.annotation.Resource;
import lombok.AllArgsConstructor;
import org.dromara.common.core.service.ProjectService;
import org.dromara.common.translation.annotation.TranslationType;
import org.dromara.common.translation.constant.TransConstant;
import org.dromara.common.translation.core.TranslationInterface;
/**
* @author lilemy
* @date 2025-09-10 16:13
*/
@AllArgsConstructor
@TranslationType(type = TransConstant.PROJECT_ID_TO_NAME)
public class ProjectNameTranslationImpl implements TranslationInterface<String> {
@Resource
private ProjectService projectService;
/**
* 翻译
*
* @param key 需要被翻译的键(不为空)
* @param other 其他参数
* @return 返回键对应的值
*/
@Override
public String translation(Object key, String other) {
if (key instanceof Long id) {
return projectService.selectProjectNameById(id);
}
return null;
}
}

View File

@ -4,3 +4,4 @@ org.dromara.common.translation.core.impl.DictTypeTranslationImpl
org.dromara.common.translation.core.impl.OssUrlTranslationImpl
org.dromara.common.translation.core.impl.UserNameTranslationImpl
org.dromara.common.translation.core.impl.NicknameTranslationImpl
org.dromara.common.translation.core.impl.ProjectNameTranslationImpl

View File

@ -12,6 +12,8 @@ public interface WebSocketConstants {
*/
String LOGIN_USER_KEY = "loginUser";
String PROJECT_ID = "projectId";
/**
* 订阅的频道
*/

View File

@ -13,6 +13,7 @@ import java.io.IOException;
import java.util.List;
import static org.dromara.common.websocket.constant.WebSocketConstants.LOGIN_USER_KEY;
import static org.dromara.common.websocket.constant.WebSocketConstants.PROJECT_ID;
/**
* WebSocketHandler 实现类
@ -27,14 +28,17 @@ public class PlusWebSocketHandler extends AbstractWebSocketHandler {
*/
@Override
public void afterConnectionEstablished(WebSocketSession session) throws IOException {
LoginUser loginUser = (LoginUser) session.getAttributes().get(LOGIN_USER_KEY);
if (ObjectUtil.isNull(loginUser)) {
// LoginUser loginUser = (LoginUser) session.getAttributes().get(LOGIN_USER_KEY);
Long loginUser = (Long) session.getAttributes().get(PROJECT_ID);
// if (ObjectUtil.isNull(loginUser) ) {
if (loginUser == null ) {
session.close(CloseStatus.BAD_DATA);
log.info("[connect] invalid token received. sessionId: {}", session.getId());
return;
}
WebSocketSessionHolder.addSession(loginUser.getUserId(), session);
log.info("[connect] sessionId: {},userId:{},userType:{}", session.getId(), loginUser.getUserId(), loginUser.getUserType());
WebSocketSessionHolder.addSession(loginUser, session);
// WebSocketSessionHolder.addSession(loginUser.getUserId(), session);
// log.info("[connect] sessionId: {},userId:{},userType:{}", session.getId(), loginUser.getUserId(), loginUser.getUserType());
}
/**
@ -47,11 +51,13 @@ public class PlusWebSocketHandler extends AbstractWebSocketHandler {
@Override
protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
// 从WebSocket会话中获取登录用户信息
LoginUser loginUser = (LoginUser) session.getAttributes().get(LOGIN_USER_KEY);
// LoginUser loginUser = (LoginUser) session.getAttributes().get(LOGIN_USER_KEY);
Long loginUser = (Long) session.getAttributes().get(PROJECT_ID);
// 创建WebSocket消息DTO对象
WebSocketMessageDto webSocketMessageDto = new WebSocketMessageDto();
webSocketMessageDto.setSessionKeys(List.of(loginUser.getUserId()));
// webSocketMessageDto.setSessionKeys(List.of(loginUser.getUserId()));
webSocketMessageDto.setSessionKeys(List.of(loginUser));
webSocketMessageDto.setMessage(message.getPayload());
WebSocketUtils.publishMessage(webSocketMessageDto);
}
@ -100,13 +106,16 @@ public class PlusWebSocketHandler extends AbstractWebSocketHandler {
*/
@Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) {
LoginUser loginUser = (LoginUser) session.getAttributes().get(LOGIN_USER_KEY);
if (ObjectUtil.isNull(loginUser)) {
// LoginUser loginUser = (LoginUser) session.getAttributes().get(LOGIN_USER_KEY);
Long loginUser = (Long) session.getAttributes().get(PROJECT_ID);
// if (ObjectUtil.isNull(loginUser)) {
if (loginUser != null ) {
log.info("[disconnect] invalid token received. sessionId: {}", session.getId());
return;
}
WebSocketSessionHolder.removeSession(loginUser.getUserId());
log.info("[disconnect] sessionId: {},userId:{},userType:{}", session.getId(), loginUser.getUserId(), loginUser.getUserType());
// WebSocketSessionHolder.removeSession(loginUser.getUserId());
WebSocketSessionHolder.removeSession(loginUser);
// log.info("[disconnect] sessionId: {},userId:{},userType:{}", session.getId(), loginUser.getUserId(), loginUser.getUserType());
}
/**

View File

@ -15,6 +15,7 @@ import org.springframework.web.socket.server.HandshakeInterceptor;
import java.util.Map;
import static org.dromara.common.websocket.constant.WebSocketConstants.LOGIN_USER_KEY;
import static org.dromara.common.websocket.constant.WebSocketConstants.PROJECT_ID;
/**
* WebSocket握手请求的拦截器
@ -44,6 +45,8 @@ public class PlusWebSocketInterceptor implements HandshakeInterceptor {
String headerCid = ServletUtils.getRequest().getHeader(LoginHelper.CLIENT_KEY);
String paramCid = ServletUtils.getParameter(LoginHelper.CLIENT_KEY);
String clientId = StpUtil.getExtra(LoginHelper.CLIENT_KEY).toString();
String projectIdStr = ServletUtils.getRequest().getParameter("projectId");
Long projectId = Long.parseLong(projectIdStr);
if (!StringUtils.equalsAny(clientId, headerCid, paramCid)) {
// token 无效
throw NotLoginException.newInstance(StpUtil.getLoginType(),
@ -52,6 +55,7 @@ public class PlusWebSocketInterceptor implements HandshakeInterceptor {
}
attributes.put(LOGIN_USER_KEY, loginUser);
attributes.put(PROJECT_ID,projectId);
return true;
} catch (NotLoginException e) {
log.error("WebSocket 认证失败'{}',无法访问系统资源", e.getMessage());

View File

@ -17,6 +17,16 @@
<dependencies>
<!-- <dependency>-->
<!-- <groupId>technology.tabula</groupId>-->
<!-- <artifactId>tabula</artifactId>-->
<!-- <version>1.0.4</version>-->
<!-- </dependency>-->
<!-- JSON解析FastJSON -->
<dependency>
<groupId>com.alibaba</groupId>
@ -73,18 +83,32 @@
</dependency>
<!-- 在pdf上生成二维码 -->
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>layout</artifactId>
<version>7.2.5</version>
</dependency>
<!-- iText 7 核心模块必须layout依赖此模块 -->
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>kernel</artifactId>
<version>7.2.5</version> <!-- 与layout版本严格一致 -->
</dependency>
<!-- 支持中文字体 -->
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itext-asian</artifactId>
<version>5.2.0</version>
</dependency>
<!-- iText -->
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itextpdf</artifactId>
<version>5.5.13.3</version>
</dependency>
<!-- <dependency>-->
<!-- <groupId>com.itextpdf</groupId>-->
<!-- <artifactId>itext-asian</artifactId>-->
<!-- <version>5.2.0</version>-->
<!-- </dependency>-->
<!-- &lt;!&ndash; iText &ndash;&gt;-->
<!-- <dependency>-->
<!-- <groupId>com.itextpdf</groupId>-->
<!-- <artifactId>itextpdf</artifactId>-->
<!-- <version>5.5.13.3</version>-->
<!-- </dependency>-->
<!-- ZXing -->
<dependency>
<groupId>com.google.zxing</groupId>
@ -108,6 +132,11 @@
<artifactId>ruoyi-common-doc</artifactId>
</dependency>
<dependency>
<groupId>org.dromara</groupId>
<artifactId>ruoyi-common-job</artifactId>
</dependency>
<dependency>
<groupId>org.dromara</groupId>
<artifactId>ruoyi-common-mybatis</artifactId>
@ -216,6 +245,17 @@
<version>5.3.0</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.25.0-GA</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
</dependency>
</dependencies>

View File

@ -3,7 +3,9 @@ package org.dromara.bidding.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.constraints.NotNull;
import org.apache.pdfbox.pdmodel.PDDocument;
import lombok.RequiredArgsConstructor;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.dromara.bidding.domain.bo.BusBiddingLimitListBo;
import org.dromara.bidding.domain.bo.BiddingAllVersionNumbersReq;
import org.dromara.bidding.domain.vo.BusBiddingLimitListVo;
@ -16,11 +18,14 @@ 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.web.core.BaseController;
import org.dromara.tender.domain.bo.BusBillofquantitiesLimitListBo;
import org.dromara.tender.domain.vo.BusBLimitListVersionsVo;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.IOException;
import java.util.List;
/**
@ -37,6 +42,8 @@ public class BusBiddingLimitListController extends BaseController {
private final IBusBiddingLimitListService busBiddingLimitListService;
/**
* 查询成本-投标列表
*/
@ -100,8 +107,8 @@ public class BusBiddingLimitListController extends BaseController {
@Log(title = "成本-投标", businessType = BusinessType.INSERT)
@RepeatSubmit()
@PostMapping("/importExcelFile")
public R<Void> importExcelFile(Long projectId, @RequestParam("file") MultipartFile file) {
return toAjax(busBiddingLimitListService.importExcelFile(projectId, file));
public R<Void> importExcelFile(BusBiddingLimitListBo bo, @RequestParam("file") MultipartFile file) {
return toAjax(busBiddingLimitListService.importExcelFile(bo, file));
}
/**
@ -134,8 +141,8 @@ public class BusBiddingLimitListController extends BaseController {
@Log(title = "成本-投标", businessType = BusinessType.UPDATE)
@RepeatSubmit()
@PutMapping()
public R<Void> edit(@Validated(EditGroup.class) @RequestBody BusBiddingLimitListBo bo) {
return toAjax(busBiddingLimitListService.updateByBo(bo));
public R<Void> edit(@RequestBody List<BusBiddingLimitListBo> bos) {
return toAjax(busBiddingLimitListService.updateByBo(bos));
}
/**

View File

@ -42,6 +42,14 @@ public class BusBiddingLimitVersions extends BaseEntity {
*/
private String versions;
/**
* 版本号名称
*/
private String versionsName;
/**
* excel文件
*/

View File

@ -1,5 +1,6 @@
package org.dromara.bidding.domain.bo;
import com.alibaba.excel.annotation.ExcelProperty;
import org.dromara.bidding.domain.BusBiddingLimitList;
import org.dromara.common.mybatis.core.domain.BaseEntity;
import org.dromara.common.core.validate.AddGroup;
@ -91,4 +92,6 @@ public class BusBiddingLimitListBo extends BaseEntity {
private String remark;
private Long type;
}

View File

@ -44,6 +44,14 @@ public class BusBiddingLimitVersionsBo extends BaseEntity {
@NotBlank(message = "版本号不能为空", groups = { AddGroup.class, EditGroup.class })
private String versions;
/**
* 版本号名称
*/
private String versionsName;
/**
* excel文件
*/

View File

@ -1,5 +1,6 @@
package org.dromara.bidding.domain.vo;
import com.alibaba.excel.annotation.write.style.ColumnWidth;
import org.dromara.bidding.domain.BusBiddingLimitList;
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import com.alibaba.excel.annotation.ExcelProperty;
@ -77,6 +78,7 @@ public class BusBiddingLimitListVo implements Serializable {
* 名称
*/
@ExcelProperty(value = "名称")
@ColumnWidth(50)
private String name;
/**

View File

@ -52,6 +52,14 @@ public class BusBiddingLimitVersionsVo implements Serializable {
@ExcelProperty(value = "版本号")
private String versions;
/**
* 版本号名称
*/
private String versionsName;
/**
* excel文件
*/

View File

@ -1,9 +1,12 @@
package org.dromara.bidding.mapper;
import org.apache.ibatis.annotations.Param;
import org.dromara.bidding.domain.BusBiddingLimitList;
import org.dromara.bidding.domain.vo.BusBiddingLimitListVo;
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
import java.util.List;
/**
* 成本-投标Mapper接口
*
@ -12,4 +15,6 @@ import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
*/
public interface BusBiddingLimitListMapper extends BaseMapperPlus<BusBiddingLimitList, BusBiddingLimitListVo> {
Boolean updateBatchByIdByNull(@Param("list") List<BusBiddingLimitList> list);
}

View File

@ -9,7 +9,6 @@ import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.mybatis.core.page.PageQuery;
import com.baomidou.mybatisplus.extension.service.IService;
import org.dromara.tender.domain.vo.BusBLimitListVersionsVo;
import org.springframework.web.multipart.MultipartFile;
import java.util.Collection;
@ -62,7 +61,7 @@ public interface IBusBiddingLimitListService extends IService<BusBiddingLimitLis
* @param bo 成本-投标
* @return 是否修改成功
*/
Boolean updateByBo(BusBiddingLimitListBo bo);
Boolean updateByBo(List<BusBiddingLimitListBo> bo);
/**
* 校验并批量删除成本-投标信息
@ -80,7 +79,7 @@ public interface IBusBiddingLimitListService extends IService<BusBiddingLimitLis
*/
List<BusBiddingLimitListVo> getTree(BusBiddingLimitListBo bo);
Boolean importExcelFile(Long projectId, MultipartFile file);
Boolean importExcelFile(BusBiddingLimitListBo projectId, MultipartFile file);
List<BusBiddingLimitVersionsVo> obtainAllVersionNumbers(BiddingAllVersionNumbersReq bo);

View File

@ -1,5 +1,7 @@
package org.dromara.bidding.service;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import org.dromara.bidding.domain.vo.BusBiddingLimitVersionsVo;
import org.dromara.bidding.domain.bo.BusBiddingLimitVersionsBo;
import org.dromara.bidding.domain.BusBiddingLimitVersions;
@ -67,4 +69,8 @@ public interface IBusBiddingLimitVersionsService extends IService<BusBiddingLimi
* @return 是否删除成功
*/
Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
BusBiddingLimitVersions queryByProjectId( String versions, Long projectId);
BusBiddingLimitVersions getByProjectIdVersions(Long projectId, String versions);
}

View File

@ -8,6 +8,7 @@ import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.bidding.domain.BusBiddingLimitList;
import org.dromara.bidding.domain.BusBiddingLimitVersions;
import org.dromara.bidding.domain.bo.BusBiddingLimitListBo;
import org.dromara.bidding.domain.bo.BusBiddingLimitVersionsBo;
import org.dromara.bidding.domain.bo.BiddingAllVersionNumbersReq;
@ -17,13 +18,13 @@ import org.dromara.bidding.mapper.BusBiddingLimitListMapper;
import org.dromara.bidding.service.IBusBiddingLimitListService;
import org.dromara.bidding.service.IBusBiddingLimitVersionsService;
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.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.common.utils.excel.ExcelDynamicReader;
import org.dromara.tender.domain.vo.BusBLimitListVersionsVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@ -82,13 +83,14 @@ public class BusBiddingLimitListServiceImpl extends ServiceImpl<BusBiddingLimitL
@Override
public List<BusBiddingLimitListVo> queryList(BusBiddingLimitListBo bo) {
LambdaQueryWrapper<BusBiddingLimitList> lqw = buildQueryWrapper(bo);
lqw.orderByAsc(BusBiddingLimitList::getNum);
return baseMapper.selectVoList(lqw);
}
private LambdaQueryWrapper<BusBiddingLimitList> buildQueryWrapper(BusBiddingLimitListBo bo) {
Map<String, Object> params = bo.getParams();
LambdaQueryWrapper<BusBiddingLimitList> lqw = Wrappers.lambdaQuery();
lqw.orderByDesc(BusBiddingLimitList::getId);
lqw.orderByAsc(BusBiddingLimitList::getSid);
lqw.eq(bo.getProjectId() != null, BusBiddingLimitList::getProjectId, bo.getProjectId());
lqw.eq(StringUtils.isNotBlank(bo.getVersions()), BusBiddingLimitList::getVersions, bo.getVersions());
lqw.eq(StringUtils.isNotBlank(bo.getSheet()), BusBiddingLimitList::getSheet, bo.getSheet());
@ -127,10 +129,30 @@ public class BusBiddingLimitListServiceImpl extends ServiceImpl<BusBiddingLimitL
* @return 是否修改成功
*/
@Override
public Boolean updateByBo(BusBiddingLimitListBo bo) {
BusBiddingLimitList update = MapstructUtils.convert(bo, BusBiddingLimitList.class);
validEntityBeforeSave(update);
return baseMapper.updateById(update) > 0;
public Boolean updateByBo(List<BusBiddingLimitListBo> bos) {
List<BusBiddingLimitList> list = new ArrayList<>();
for (BusBiddingLimitListBo bo : bos) {
if (bo.getId() == null) {
throw new ServiceException("id不能为空");
}
if (bo.getProjectId() == null) {
throw new ServiceException("项目id不能为空");
}
if (bo.getVersions() == null) {
throw new ServiceException("版本号不能为空");
}
BusBiddingLimitList update = MapstructUtils.convert(bo, BusBiddingLimitList.class);
if (bo.getUnitPrice() == null){
update.setUnitPrice(null);
}
validEntityBeforeSave(update);
list.add(update);
}
if (list.isEmpty()) {
return false;
}
return baseMapper.updateBatchByIdByNull(list);
}
/**
@ -157,13 +179,17 @@ public class BusBiddingLimitListServiceImpl extends ServiceImpl<BusBiddingLimitL
@Override
public List<BusBiddingLimitListVo> getTree(BusBiddingLimitListBo bo) {
BusBiddingLimitVersions biddingLimitVersions = busBiddingLimitVersionsService.queryByProjectId(bo.getVersions(),bo.getProjectId());
if (biddingLimitVersions == null || (bo.getType() == 1L && !BusinessStatusEnum.FINISH.getStatus().equals(biddingLimitVersions.getStatus()))) {
return null;
}
//获取所有数据
List<BusBiddingLimitListVo> listVoList = queryList(bo);
//过滤数量和单价为空的数据并计算总价
listVoList.stream().filter(vo -> vo.getUnitPrice() != null && vo.getUnitPrice().compareTo(BigDecimal.ZERO) != 0)
.filter(vo -> vo.getQuantity() != null && vo.getQuantity().compareTo(BigDecimal.ZERO) != 0)
.forEach(item -> {
item.setPrice(item.getUnitPrice().multiply(item.getQuantity()).setScale(2, RoundingMode.HALF_UP));
item.setPrice(item.getUnitPrice().multiply(item.getQuantity()).setScale(4, RoundingMode.HALF_UP));
});
//构建父子映射
@ -172,12 +198,54 @@ public class BusBiddingLimitListServiceImpl extends ServiceImpl<BusBiddingLimitL
//递归组装树形结构
List<BusBiddingLimitListVo> treeList = buildTree("0", parentMap);
for (BusBiddingLimitListVo item : treeList) {
calculateTreePrice(item);
}
return treeList;
}
/**
* 递归计算树形结构中每个节点的 price 字段:
* - 叶子节点price = quantity * unitPrice
* - 非叶子节点price = 所有子节点 price 的总和
*/
public void calculateTreePrice(BusBiddingLimitListVo node) {
if (node == null) return;
// 先处理所有子节点
for (BusBiddingLimitListVo child : node.getChildren()) {
calculateTreePrice(child);
}
// 如果是叶子节点,计算 price = quantity * unitPrice
if (node.getChildren().isEmpty()) {
if (node.getQuantity() != null && node.getUnitPrice() != null) {
node.setPrice(node.getQuantity().multiply(node.getUnitPrice()));
} else {
node.setPrice(BigDecimal.ZERO); // 默认值
}
} else {
// 非叶子节点:累加子节点的 price
BigDecimal totalPrice = node.getChildren().stream()
.map(BusBiddingLimitListVo::getPrice)
.reduce(BigDecimal.ZERO, BigDecimal::add)
.setScale(4, RoundingMode.HALF_UP);
node.setPrice(totalPrice);
}
}
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean importExcelFile(Long projectId, MultipartFile file) {
public Boolean importExcelFile(BusBiddingLimitListBo bo, MultipartFile file) {
BusBiddingLimitVersions biddingLimitVersions = busBiddingLimitVersionsService.getByProjectIdVersions(bo.getProjectId(),bo.getVersions());
if (biddingLimitVersions == null) {
throw new ServiceException("版本号不存在!!!");
}
if (BusinessStatusEnum.FINISH.getStatus().equals(biddingLimitVersions.getStatus())) {
throw new ServiceException("数据已审核完成,不允许修改!!!");
}
// 跳过1行表头读取0到6列共7列映射到ExcelData实体类
List<BusBiddingLimitListBo> dataList = null;
@ -186,7 +254,7 @@ public class BusBiddingLimitListServiceImpl extends ServiceImpl<BusBiddingLimitL
file, // 上传的文件
1, // 跳过1行表头
0, // 从第0列开始
12, // 到第12列结束
13, // 到第12列结束
BusBiddingLimitListBo.class // 目标实体类
);
} catch (Exception e) {
@ -209,7 +277,6 @@ public class BusBiddingLimitListServiceImpl extends ServiceImpl<BusBiddingLimitL
busBillofquantities.add(limitList);
});
log.info(busBillofquantities.toString());
return this.updateBatchById(busBillofquantities);
}

View File

@ -1,30 +1,29 @@
package org.dromara.bidding.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 lombok.extern.slf4j.Slf4j;
import org.dromara.bidding.domain.BusBiddingLimitVersions;
import org.dromara.bidding.domain.bo.BusBiddingLimitVersionsBo;
import org.dromara.bidding.domain.vo.BusBiddingLimitVersionsVo;
import org.dromara.bidding.mapper.BusBiddingLimitVersionsMapper;
import org.dromara.bidding.service.IBusBiddingLimitVersionsService;
import org.dromara.common.core.domain.event.ProcessDeleteEvent;
import org.dromara.common.core.domain.event.ProcessEvent;
import org.dromara.common.core.domain.event.ProcessTaskEvent;
import org.dromara.common.core.utils.MapstructUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.mybatis.core.page.PageQuery;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.RequiredArgsConstructor;
import org.dromara.design.domain.BusBillofquantitiesVersions;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Service;
import org.dromara.bidding.domain.bo.BusBiddingLimitVersionsBo;
import org.dromara.bidding.domain.vo.BusBiddingLimitVersionsVo;
import org.dromara.bidding.domain.BusBiddingLimitVersions;
import org.dromara.bidding.mapper.BusBiddingLimitVersionsMapper;
import org.dromara.bidding.service.IBusBiddingLimitVersionsService;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Collection;
/**
* 成本- 投标版本Service业务层处理
@ -46,7 +45,7 @@ public class BusBiddingLimitVersionsServiceImpl extends ServiceImpl<BusBiddingLi
* @return 成本- 投标版本
*/
@Override
public BusBiddingLimitVersionsVo queryById(Long id){
public BusBiddingLimitVersionsVo queryById(Long id) {
return baseMapper.selectVoById(id);
}
@ -120,7 +119,7 @@ public class BusBiddingLimitVersionsServiceImpl extends ServiceImpl<BusBiddingLi
/**
* 保存前的数据校验
*/
private void validEntityBeforeSave(BusBiddingLimitVersions entity){
private void validEntityBeforeSave(BusBiddingLimitVersions entity) {
//TODO 做一些数据校验,如唯一约束
}
@ -133,12 +132,26 @@ public class BusBiddingLimitVersionsServiceImpl extends ServiceImpl<BusBiddingLi
*/
@Override
public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
if(isValid){
if (isValid) {
//TODO 做一些业务上的校验,判断是否需要校验
}
return baseMapper.deleteByIds(ids) > 0;
}
@Override
public BusBiddingLimitVersions queryByProjectId(String versions, Long projectId) {
return baseMapper.selectOne(new LambdaQueryWrapper<BusBiddingLimitVersions>()
.eq(BusBiddingLimitVersions::getProjectId, projectId)
.eq(BusBiddingLimitVersions::getVersions, versions));
}
@Override
public BusBiddingLimitVersions getByProjectIdVersions(Long projectId, String versions) {
return baseMapper.selectOne(new LambdaQueryWrapper<BusBiddingLimitVersions>()
.eq(BusBiddingLimitVersions::getProjectId, projectId)
.eq(BusBiddingLimitVersions::getVersions, versions));
}
/**
* 总体流程监听(例如: 草稿,撤销,退回,作废,终止,已完成,单任务完成等)
@ -147,13 +160,12 @@ public class BusBiddingLimitVersionsServiceImpl extends ServiceImpl<BusBiddingLi
*
* @param processEvent 参数
*/
@org.springframework.context.event.EventListener(condition = "#processEvent.flowCode.endsWith('biddingLimitList')")
@EventListener(condition = "#processEvent.flowCode.endsWith('biddingLimitList')")
public void processPlansHandlErequipmentList(ProcessEvent processEvent) {
log.info("物资设备清单审核任务执行了{}", processEvent.toString());
String id = processEvent.getBusinessId();
String split = id.split("_")[0];
LambdaQueryWrapper<BusBiddingLimitVersions> eq = new LambdaQueryWrapper<BusBiddingLimitVersions>()
.eq(BusBiddingLimitVersions::getVersions, split);
.eq(BusBiddingLimitVersions::getId, id);
BusBiddingLimitVersions busBiddingLimitVersions = new BusBiddingLimitVersions();
busBiddingLimitVersions.setStatus(processEvent.getStatus());
boolean update = this.update(busBiddingLimitVersions, eq);
@ -178,7 +190,7 @@ public class BusBiddingLimitVersionsServiceImpl extends ServiceImpl<BusBiddingLi
*
* @param processTaskEvent 参数
*/
@org.springframework.context.event.EventListener(condition = "#processTaskEvent.flowCode.endsWith('biddingLimitList')")
@EventListener(condition = "#processTaskEvent.flowCode.endsWith('biddingLimitList')")
public void processTaskPlansHandlerEquipmentList(ProcessTaskEvent processTaskEvent) {
log.info("物资设备清单审核任务创建了{}", processTaskEvent.toString());
}

View File

@ -42,9 +42,6 @@ public class BusListOfWinningBidsServiceImpl extends ServiceImpl<BusListOfWinnin
@Autowired
private ISysOssService ossService;
// @Autowired
// private ICtrIncomeContractService ctrIncomeContractService;
/**
* 查询中标项目一览
*
@ -99,17 +96,6 @@ public class BusListOfWinningBidsServiceImpl extends ServiceImpl<BusListOfWinnin
*/
@Override
public Boolean insertByBo(BusListOfWinningBidsBo bo) {
// if ("0".equals(bo.getWhetherBid()) && bo.getBidFileId() != null ) {
// SysOssVo ossVo = ossService.getById(bo.getBidFileId());
// bo.setBidFileName(ossVo.getOriginalName());
// bo.setBidFile(ossVo.getUrl());
//// CtrIncomeContract ctrIncomeContract = new CtrIncomeContract();
//// ctrIncomeContract.setContractName(bo.getProjectName());
//// ctrIncomeContract.setContractOwner(bo.getTenderer());
//// ctrIncomeContract.setProjectId(bo.getProjectId());
//// ctrIncomeContractService.save(ctrIncomeContract);
// }
if (bo.getId() == null) {
BusListOfWinningBids add = MapstructUtils.convert(bo, BusListOfWinningBids.class);
validEntityBeforeSave(add);

View File

@ -0,0 +1,220 @@
package org.dromara.bigscreen.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import jakarta.annotation.Resource;
import org.dromara.bigscreen.domain.dto.WeatherQueryReq;
import org.dromara.bigscreen.domain.vo.*;
import org.dromara.bigscreen.service.EnterpriseBigScreenService;
import org.dromara.common.core.domain.R;
import org.dromara.manager.weathermanager.vo.WeatherVo;
import org.dromara.project.domain.BusAttendance;
import org.dromara.project.domain.BusProject;
import org.dromara.project.domain.BusUserProjectRelevancy;
import org.dromara.project.service.IBusAttendanceService;
import org.dromara.project.service.IBusProjectService;
import org.dromara.project.service.IBusUserProjectRelevancyService;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.stream.Collectors;
/**
* 企业级大屏
*
* @author lilemy
* @date 2025-09-09 14:55
*/
@Validated
@RestController
@RequestMapping("/enterprise/big/screen")
public class EnterpriseBigScreenController {
@Resource
private EnterpriseBigScreenService enterpriseBigScreenService;
@Resource
private IBusUserProjectRelevancyService userProjectRelevancyService;
@Resource
private IBusAttendanceService attendanceService;
@Resource
private IBusProjectService projectService;
/**
* 获取关键指标
*/
@SaCheckPermission("enterprise:bigScreen:keyIndex")
@GetMapping("/keyIndex")
public R<EnterpriseKeyIndexVo> getEnterpriseKeyIndex() {
return R.ok(enterpriseBigScreenService.getEnterpriseKeyIndex());
}
/**
* 项目进度分析
*/
@SaCheckPermission("enterprise:bigScreen:projectProgress")
@GetMapping("/projectProgress")
public R<ProjectProgressAnalysisVo> getProjectProgress() {
return R.ok(enterpriseBigScreenService.getProjectProgressAnalysis());
}
/**
* 项目产值对比
*/
@SaCheckPermission("enterprise:bigScreen:projectOutputValueComparison")
@GetMapping("/projectOutputValueComparison")
public R<List<OutputValueComparisonVo>> getProjectOutputValueComparison() {
return R.ok(enterpriseBigScreenService.getProjectOutputValueComparison());
}
/**
* 风险预警
*/
@SaCheckPermission("enterprise:bigScreen:riskEarlyWarning")
@GetMapping("/riskEarlyWarning")
public R<List<RiskEarlyWarningVo>> getRiskEarlyWarning() {
return R.ok(enterpriseBigScreenService.getRiskEarlyWarning());
}
/**
* 查询天气
*/
@SaCheckPermission("enterprise:bigScreen:weather")
@GetMapping("/weather")
public R<List<WeatherVo>> getProjectWeather(WeatherQueryReq req) {
return R.ok(enterpriseBigScreenService.getWeather3DaysList(req));
}
/**
* 查询安全天数
*/
@SaCheckPermission("enterprise:bigScreen:safetyDay")
@GetMapping("/safetyDay")
public R<Long> getProjectSafetyDay() {
LocalDate date = LocalDate.of(2023, 1, 1);
LocalDate now = LocalDate.now();
long days = Math.abs(ChronoUnit.DAYS.between(date, now));
return R.ok(days);
}
/**
* 人数统计
*/
@SaCheckPermission("enterprise:bigScreen:peopleCount")
@GetMapping("/peopleCount")
public R<PeopleCountVo> getProjectPeopleCount() {
PeopleCountVo peopleCountVo = new PeopleCountVo();
List<BusUserProjectRelevancy> list = userProjectRelevancyService.list();
//0系统管理员 1普通人员 2项目管理员 3分包人员
peopleCountVo.setConstructionPersonnelCount(list.stream().filter(item -> "1".equals(item.getUserType()))
.map(BusUserProjectRelevancy::getUserId)
.distinct().count());
peopleCountVo.setManagersCount(list.stream().filter(item -> "2".equals(item.getUserType()))
.map(BusUserProjectRelevancy::getUserId)
.distinct().count());
peopleCountVo.setSubcontractorsCount(list.stream().filter(item -> "3".equals(item.getUserType()))
.map(BusUserProjectRelevancy::getUserId)
.distinct().count());
return R.ok(peopleCountVo);
}
/**
* 出勤人数统计
*/
@SaCheckPermission("enterprise:bigScreen:allAttendanceCount")
@GetMapping("/allAttendanceCount")
public R<TodayAttendanceCountVo> getAllAttendanceCount() {
TodayAttendanceCountVo todayAttendanceCountVo = new TodayAttendanceCountVo();
List<BusAttendance> list = attendanceService.list(Wrappers.<BusAttendance>lambdaQuery()
.eq(BusAttendance::getClockDate, LocalDate.now())
.in(BusAttendance::getClockStatus, Arrays.asList("1", "2", "3"))
);
long attendanceCount = list.stream().map(BusAttendance::getUserId).distinct().count();
todayAttendanceCountVo.setAttendanceCount(attendanceCount);
// 查询总人数
List<BusUserProjectRelevancy> relevancyList = userProjectRelevancyService.list();
long totalUserCount = relevancyList.stream().map(BusUserProjectRelevancy::getUserId).distinct().count();
// 计算考勤率(保留一位小数)
if (totalUserCount > 0) {
BigDecimal rate = new BigDecimal(attendanceCount * 100)
.divide(new BigDecimal(totalUserCount), 1, RoundingMode.HALF_UP);
todayAttendanceCountVo.setAttendanceRate(rate.doubleValue());
} else {
todayAttendanceCountVo.setAttendanceRate(0.0);
}
return R.ok(todayAttendanceCountVo);
}
/**
* 每个项目的出勤人数
*/
@SaCheckPermission("enterprise:bigScreen:projectAttendanceCount")
@GetMapping("/projectAttendanceCount")
public R<List<ProjectAttendanceCountVo>> getProjectAttendanceCount() {
ArrayList<ProjectAttendanceCountVo> projectAttendanceCountVos = new ArrayList<>();
List<BusAttendance> list = attendanceService.list(Wrappers.<BusAttendance>lambdaQuery()
.eq(BusAttendance::getClockDate, LocalDate.now())
.in(BusAttendance::getClockStatus, Arrays.asList("1", "2", "3"))
);
List<BusUserProjectRelevancy> relevancyList = userProjectRelevancyService.list();
// 转换为 Map<projectId, 去重后的 userId 数量>
Map<Long, Integer> projectUserCountMap = relevancyList.stream()
.collect(Collectors.groupingBy(
BusUserProjectRelevancy::getProjectId,
Collectors.mapping(
BusUserProjectRelevancy::getUserId,
Collectors.collectingAndThen(
Collectors.toSet(),
Set::size
)
)
));
for (Long projectId : projectUserCountMap.keySet()) {
ProjectAttendanceCountVo projectAttendanceCountVo = new ProjectAttendanceCountVo();
BusProject byId = projectService.getById(projectId);
if (byId == null) {
continue;
}
projectAttendanceCountVo.setProjectName(byId.getProjectName());
long count = list.stream().filter(item -> item.getProjectId().equals(projectId))
.map(BusAttendance::getUserId)
.distinct()
.count();
Integer i = projectUserCountMap.get(projectId);
BigDecimal rate = new BigDecimal("0.0");
if (i > 0) {
rate = new BigDecimal(count * 100)
.divide(new BigDecimal(projectUserCountMap.get(projectId)), 1, RoundingMode.HALF_UP);
}
projectAttendanceCountVo.setAttendanceRate(rate.doubleValue());
projectAttendanceCountVos.add(projectAttendanceCountVo);
}
return R.ok(projectAttendanceCountVos);
}
}

View File

@ -11,13 +11,13 @@ import org.dromara.ctr.domain.CtrExpensesContract;
import org.dromara.ctr.domain.CtrIncomeContract;
import org.dromara.ctr.service.ICtrExpensesContractService;
import org.dromara.ctr.service.ICtrIncomeContractService;
import org.dromara.manager.weathermanager.vo.WeatherVo;
import org.dromara.out.domain.OutSettlementValueOwner;
import org.dromara.out.domain.OutSettlementValueSubcontract;
import org.dromara.out.service.IOutSettlementValueOwnerService;
import org.dromara.out.service.IOutSettlementValueSubcontractService;
import org.dromara.project.domain.vo.project.BusProjectGisVo;
import org.dromara.project.domain.vo.project.BusProjectSafetyDayVo;
import org.dromara.project.domain.vo.project.BusProjectWeatherVo;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
@ -114,7 +114,7 @@ public class MoneyBigScreenController {
.eq(CtrIncomeContract::getProjectId, projectId)
.last("limit 1")
);
if (contract != null && contract.getPayRatio()!= null) {
if (contract != null && contract.getPayRatio() != null) {
actualAmount = actualAmount.add(projectMonthlyAmountMap.get(projectId).multiply(contract.getPayRatio()).divide(HUNDRED));
}
@ -169,7 +169,7 @@ public class MoneyBigScreenController {
.last("limit 1")
);
if (contract != null && contract.getPayRatio()!= null) {
if (contract != null && contract.getPayRatio() != null) {
actualAmount = actualAmount.add(projectAmountMap.get(contractCode).multiply(contract.getPayRatio()).divide(HUNDRED));
}
@ -243,7 +243,7 @@ public class MoneyBigScreenController {
.filter(contract -> contract.getAmount() != null && contract.getAmount().compareTo(THIRD_PHASE) >= 0)
.count();
return R.ok(new MoneyContractCountVo(4, 4, 6, 6));
return R.ok(new MoneyContractCountVo(4, 4, 6, 6));
// return R.ok(new MoneyContractCountVo(lessThan1M, between1MAnd5M, between5MAnd10M, greaterThanOrEqualTo10M));
}
@ -350,7 +350,7 @@ public class MoneyBigScreenController {
.eq(CtrIncomeContract::getProjectId, projectId)
.last("limit 1")
);
if (contract != null && contract.getPayRatio()!= null) {
if (contract != null && contract.getPayRatio() != null) {
incomeAmount = incomeAmount.add(incomeGroupedByProject.get(projectId).multiply(contract.getPayRatio()).divide(HUNDRED));
}
}
@ -380,7 +380,7 @@ public class MoneyBigScreenController {
.last("limit 1")
);
if (contract != null && contract.getPayRatio()!= null) {
if (contract != null && contract.getPayRatio() != null) {
expensesAmount = expensesAmount.add(expenseGroupedByContract.get(contractCode).multiply(contract.getPayRatio()).divide(HUNDRED));
}
}
@ -448,7 +448,7 @@ public class MoneyBigScreenController {
.eq(CtrIncomeContract::getProjectId, projectId)
.last("limit 1")
);
if (contract != null && contract.getPayRatio()!= null) {
if (contract != null && contract.getPayRatio() != null) {
incomeAmount = incomeAmount.add(incomeGroupedByProject.get(projectId).multiply(contract.getPayRatio()).divide(HUNDRED));
}
}
@ -478,7 +478,7 @@ public class MoneyBigScreenController {
.last("limit 1")
);
if (contract != null && contract.getPayRatio()!= null) {
if (contract != null && contract.getPayRatio() != null) {
expensesAmount = expensesAmount.add(expenseGroupedByContract.get(contractCode).multiply(contract.getPayRatio()).divide(HUNDRED));
}
}
@ -511,7 +511,7 @@ public class MoneyBigScreenController {
.filter(java.util.Objects::nonNull) // 过滤掉 null 值
.reduce(BigDecimal.ZERO, BigDecimal::add);
List<OutSettlementValueSubcontract> subcontractList = settlementValueSubcontractService.list();
List<OutSettlementValueSubcontract> subcontractList = settlementValueSubcontractService.list();
BigDecimal expensesCash = subcontractList.stream()
.map(OutSettlementValueSubcontract::getSettlementValue)
@ -526,8 +526,6 @@ public class MoneyBigScreenController {
}
/**
* 获取当前月份的开始时间和结束时间
*
@ -548,8 +546,8 @@ public class MoneyBigScreenController {
*/
@SaCheckPermission("project:bigScreen:weather")
@GetMapping("/weather/{projectId}")
public R<List<BusProjectWeatherVo>> getProjectWeather(@NotNull(message = "主键不能为空")
@PathVariable Long projectId) {
public R<List<WeatherVo>> getProjectWeather(@NotNull(message = "主键不能为空")
@PathVariable Long projectId) {
return R.ok(moneyBigScreenService.getProjectWeather(projectId));
}

View File

@ -1,31 +1,41 @@
package org.dromara.bigscreen.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import jakarta.annotation.Resource;
import jakarta.validation.constraints.NotNull;
import lombok.RequiredArgsConstructor;
import org.dromara.bigscreen.domain.vo.ProjectImageProgressVo;
import org.dromara.bigscreen.domain.vo.ProjectLandVo;
import org.dromara.bigscreen.domain.vo.ProjectPeopleVo;
import org.dromara.bigscreen.domain.vo.ProjectSafetyInspectionVo;
import org.dromara.bigscreen.domain.*;
import org.dromara.bigscreen.domain.vo.*;
import org.dromara.bigscreen.mapper.ProjectBigScreenMapper;
import org.dromara.bigscreen.service.ProjectBigScreenService;
import org.dromara.common.core.domain.R;
import org.dromara.common.core.utils.DateUtils;
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.utils.BigDecimalUtil;
import org.dromara.gps.domain.bo.GpsEquipmentBo;
import org.dromara.gps.domain.vo.GpsEquipmentSonVo;
import org.dromara.gps.service.IGpsEquipmentService;
import org.dromara.land.domain.BusLandBlock;
import org.dromara.land.domain.BusLandTransferLedger;
import org.dromara.land.service.IBusLandBlockService;
import org.dromara.land.service.IBusLandTransferLedgerService;
import org.dromara.manager.weathermanager.vo.WeatherVo;
import org.dromara.other.service.IOthYs7DeviceService;
import org.dromara.project.domain.BusProject;
import org.dromara.project.domain.vo.project.BusProjectSafetyDayVo;
import org.dromara.project.domain.vo.project.BusProjectWeatherVo;
import org.dromara.project.domain.vo.projectnews.BusProjectNewsVo;
import org.dromara.project.service.IBusProjectService;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.*;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@ -49,6 +59,14 @@ public class ProjectBigScreenController {
private final IBusLandBlockService busLandBlockService;
private final IGpsEquipmentService gpsEquipmentService;
private final IOthYs7DeviceService othYs7DeviceService;
private final ProjectBigScreenMapper projectBigScreenMapper;
private final IBusProjectService projectService;
/**
* 查询项目土地统计
*/
@ -103,8 +121,8 @@ public class ProjectBigScreenController {
*/
@SaCheckPermission("project:bigScreen:weather")
@GetMapping("/weather/{projectId}")
public R<List<BusProjectWeatherVo>> getProjectWeather(@NotNull(message = "主键不能为空")
@PathVariable Long projectId) {
public R<List<WeatherVo>> getProjectWeather(@NotNull(message = "主键不能为空")
@PathVariable Long projectId) {
return R.ok(projectBigScreenService.getProjectWeather(projectId));
}
@ -125,7 +143,14 @@ public class ProjectBigScreenController {
@GetMapping("/news/{projectId}")
public R<List<BusProjectNewsVo>> getProjectNews(@NotNull(message = "主键不能为空")
@PathVariable Long projectId) {
return R.ok(projectBigScreenService.getProjectNews(projectId));
List<BusCorporateEvents> busCorporateEvents = projectBigScreenMapper.getBusCorporateEvents();
return R.ok(busCorporateEvents.stream().map(event -> {
BusProjectNewsVo vo = new BusProjectNewsVo();
vo.setId(event.getId());
vo.setTitle(event.getHeadline());
vo.setContent(event.getContent());
return vo;
}).toList());
}
/**
@ -135,7 +160,23 @@ public class ProjectBigScreenController {
@GetMapping("/safetyInspection/{projectId}")
public R<List<ProjectSafetyInspectionVo>> getProjectSafetyInspection(@NotNull(message = "主键不能为空")
@PathVariable Long projectId) {
return R.ok(projectBigScreenService.getProjectSafetyInspection(projectId));
BusProject project = projectService.getById(projectId);
projectId = project.getGoId();
String pic;
if (projectId == 60) {
pic = "http://xny.yj-3d.com:7363/";
} else {
pic = "http://xny.yj-3d.com:7464/";
}
List<BusTour> busTours = projectBigScreenMapper.selectTourByProjectId(projectId);
return R.ok(busTours.stream().map(tour -> {
ProjectSafetyInspectionVo vo = new ProjectSafetyInspectionVo();
vo.setId(tour.getId());
vo.setViolationType(tour.getTourType());
vo.setPicture(pic + tour.getPicture());
vo.setCreateTime(tour.getCreatedAt());
return vo;
}).toList());
}
/**
@ -145,7 +186,52 @@ public class ProjectBigScreenController {
@GetMapping("/people/{projectId}")
public R<ProjectPeopleVo> getProjectPeople(@NotNull(message = "主键不能为空")
@PathVariable Long projectId) {
return R.ok(projectBigScreenService.getProjectPeople(projectId));
BusProject project = projectService.getById(projectId);
projectId = project.getGoId();
Integer projectUserCount = projectBigScreenMapper.getProjectUserCount(projectId);
ProjectPeopleVo vo = new ProjectPeopleVo();
vo.setPeopleCount(BigDecimal.valueOf(projectUserCount));
Integer attendanceCount = projectBigScreenMapper.getAttendanceCount(projectId, DateUtils.getDate());
vo.setAttendanceCount(BigDecimal.valueOf(attendanceCount));
vo.setAttendanceRate(BigDecimalUtil.toPercentage(BigDecimal.valueOf(attendanceCount), BigDecimal.valueOf(projectUserCount)));
List<BusConstructionUser> projectUserList = projectBigScreenMapper.getProjectUserList(projectId);
List<BusProjectTeamByGo> teamList = projectBigScreenMapper.getTeamList(projectId);
List<ProjectTeamAttendanceVo> teamAttendanceList = new ArrayList<>();
String punchRange = project.getPunchRange();
String punchTime = "";
if (punchRange != null) {
String start = punchRange.split(",")[0];
punchTime = LocalDate.now() + " " + start;
}
if (projectUserList != null && teamList != null) {
projectUserList = projectUserList.stream().filter(user -> user.getTeamId() != null).toList();
Map<Long, List<BusConstructionUser>> userMap = projectUserList.stream()
.collect(Collectors.groupingBy(BusConstructionUser::getTeamId));
for (BusProjectTeamByGo team : teamList) {
ProjectTeamAttendanceVo vo1 = new ProjectTeamAttendanceVo();
vo1.setId(team.getId());
vo1.setTeamName(team.getName());
vo1.setAttendanceTime(punchTime);
vo1.setAttendanceNumber(BigDecimal.ZERO);
List<BusConstructionUser> userList = userMap.get(team.getId());
if (CollUtil.isNotEmpty(userList)) {
List<String> list = userList.stream().map(BusConstructionUser::getOpenid).distinct().toList();
Integer aCount = projectBigScreenMapper.getAttendanceCountByOpenIds(list, LocalDate.now());
vo1.setAttendanceNumber(BigDecimal.valueOf(aCount));
}
vo1.setAllNumber(BigDecimal.valueOf(userMap.getOrDefault(team.getId(), List.of()).size()));
if (vo1.getAttendanceNumber() != null && vo1.getAllNumber() != null && vo1.getAllNumber().compareTo(BigDecimal.ZERO) != 0) {
vo1.setAttendanceRate(BigDecimalUtil.toPercentage(vo1.getAttendanceNumber(), vo1.getAllNumber()));
} else {
vo1.setAttendanceRate(BigDecimal.ZERO);
}
teamAttendanceList.add(vo1);
}
}
vo.setTeamAttendanceList(teamAttendanceList);
return R.ok(vo);
// return R.ok(projectBigScreenService.getProjectPeople(projectId));
}
/**
@ -165,6 +251,189 @@ public class ProjectBigScreenController {
@GetMapping("/generalize/{projectId}")
public R<String> getProjectGeneralize(@NotNull(message = "主键不能为空")
@PathVariable Long projectId) {
return R.ok(projectBigScreenService.getProjectGeneralize(projectId));
BusProject project = projectService.getById(projectId);
if (project != null) {
Long goId = project.getGoId();
if (goId != null) {
List<SysProjectIntroduce> sysProjectIntroduces = projectBigScreenMapper.selectByProjectId(goId);
return R.ok(sysProjectIntroduces.getFirst().getRichText());
}
}
// return R.ok(projectBigScreenService.getProjectGeneralize(projectId));
return R.ok();
}
/**
* 查询设备列表
*/
@SaCheckPermission("project:bigScreen:getClientList")
@GetMapping("/getClientList/{projectId}")
public R<List<Map<String, Object>>> getClientList(@NotNull(message = "主键不能为空")
@PathVariable Long projectId) {
List<GpsEquipmentSonVo> voList = gpsEquipmentService.getClientList(projectId);
// List<OthYs7Device> othYs7DeviceList = othYs7DeviceService.lambdaQuery()
// .eq(OthYs7Device::getProjectId, projectId)
// .list();
List<Map<String, Object>> maps = new ArrayList<>();
Map<String, Object> gpsMap = new HashMap<>();
Map<String, Object> wrjMap = new HashMap<>();
Map<String, Object> sxtMap = new HashMap<>();
List<Map<String, Object>> gpsChildrenMap = new ArrayList<>();
// List<Map<String, Object>> wrjChildrenMap = new ArrayList<>();
// List<Map<String, Object>> sxtChildrenMap = new ArrayList<>();
if (voList != null && !voList.isEmpty()) {
for (GpsEquipmentSonVo item : voList) {
Map<String, Object> gps = new HashMap<>();
gps.put("id", item.getClientId());
gps.put("label", item.getClientId());
gps.put("name", item.getDeviceName());
gps.put("type", "positioningDevice");
gps.put("lat", item.getLocLatitude());
gps.put("lng", item.getLocLongitude());
gps.put("alt", item.getLocAltitude());
gpsChildrenMap.add(gps);
}
}
// if (othYs7DeviceList != null && !othYs7DeviceList.isEmpty()) {
// for (OthYs7Device item : othYs7DeviceList) {
// Map<String, Object> sxt = new HashMap<>();
// sxt.put("id", item.getDeviceSerial());
// sxt.put("label", item.getDeviceSerial());
// sxt.put("name", item.getDeviceName());
// sxt.put("type", "shexiangtou");
//// sxt.put("lat", item.getLocLatitude());
//// sxt.put("lng", item.getLocLongitude());
//// sxt.put("alt", item.getLocAltitude());
// sxtChildrenMap.add(sxt);
// }
// }
List<Map<String, Object>> maps1 = setSxt();
List<Map<String, Object>> maps2 = setWrj();
gpsMap.put("id", 1);
gpsMap.put("label", "定位设备");
gpsMap.put("children", gpsChildrenMap);
sxtMap.put("id", 2);
sxtMap.put("label", "摄像头");
// sxtMap.put("children",sxtChildrenMap);
sxtMap.put("children", maps1);
wrjMap.put("id", 3);
wrjMap.put("label", "无人机");
// wrjMap.put("children",wrjChildrenMap);
wrjMap.put("children", maps2);
maps.add(gpsMap);
maps.add(wrjMap);
maps.add(sxtMap);
return R.ok(maps);
}
/**
* 查询GPS设备用户列表
*/
@SaCheckPermission("project:bigScreen:getList")
@GetMapping("/getList")
public R<List<String>> getList(Long projectId) {
return R.ok(projectBigScreenService.getList(projectId));
}
/**
* 新增GPS设备详细
*/
@SaCheckPermission("project:bigScreen:setList")
@Log(title = "GPS设备详细", businessType = BusinessType.INSERT)
@RepeatSubmit()
@PostMapping("/setList")
public void setList(@RequestBody GpsEquipmentBo bo) {
projectBigScreenService.setList(bo);
}
public List<Map<String, Object>> setSxt() {
List<Map<String, Object>> sxtChildrenMap = new ArrayList<>();
HashMap<String, Object> map1 = new HashMap<>();
map1.put("id", "55");
map1.put("label", "1222222");
map1.put("name", "22");
map1.put("type", "camera");
map1.put("lng", 106.48349615411811);
map1.put("lat", 29.54856374364732);
map1.put("alt", 0);
HashMap<String, Object> map2 = new HashMap<>();
map2.put("id", "56");
map2.put("label", "1222223");
map2.put("name", "23");
map2.put("type", "camera");
map2.put("lng", 106.48442273257676);
map2.put("lat", 29.53841670498476);
map2.put("alt", 0);
HashMap<String, Object> map3 = new HashMap<>();
map3.put("id", "57");
map3.put("label", "1222224");
map3.put("name", "24");
map3.put("type", "camera");
map3.put("lng", 106.49197896482423);
map3.put("lat", 29.52931974282576);
map3.put("alt", 0);
HashMap<String, Object> map4 = new HashMap<>();
map4.put("id", "58");
map4.put("label", "1222225");
map4.put("name", "25");
map4.put("type", "camera");
map4.put("lng", 106.50293584930655);
map4.put("lat", 29.533025743929034);
map4.put("alt", 0);
sxtChildrenMap.add(map1);
sxtChildrenMap.add(map2);
sxtChildrenMap.add(map3);
sxtChildrenMap.add(map4);
return sxtChildrenMap;
}
public List<Map<String, Object>> setWrj() {
List<Map<String, Object>> sxtChildrenMap = new ArrayList<>();
HashMap<String, Object> map1 = new HashMap<>();
map1.put("id", "65");
map1.put("label", "6222222");
map1.put("name", "32");
map1.put("type", "drone");
map1.put("lng", 106.49556855602525);
map1.put("lat", 29.534393226355515);
map1.put("alt", 0);
HashMap<String, Object> map2 = new HashMap<>();
map2.put("id", "66");
map2.put("label", "2222223");
map2.put("name", "33");
map2.put("type", "drone");
map2.put("lng", 106.49142431645038);
map2.put("lat", 29.534472802500083);
map2.put("alt", 0);
HashMap<String, Object> map3 = new HashMap<>();
map3.put("id", "67");
map3.put("label", "2222224");
map3.put("name", "34");
map3.put("type", "drone");
map3.put("lng", 106.49142125177437);
map3.put("lat", 29.541881138875755);
map3.put("alt", 0);
HashMap<String, Object> map4 = new HashMap<>();
map4.put("id", "68");
map4.put("label", "2222225");
map4.put("name", "35");
map4.put("type", "drone");
map4.put("lng", 106.50256649933792);
map4.put("lat", 29.54260793685717);
map4.put("alt", 0);
sxtChildrenMap.add(map1);
sxtChildrenMap.add(map2);
sxtChildrenMap.add(map3);
sxtChildrenMap.add(map4);
return sxtChildrenMap;
}
}

View File

@ -0,0 +1,58 @@
package org.dromara.bigscreen.domain;
import lombok.Data;
import java.math.BigDecimal;
import java.time.LocalDateTime;
@Data
public class BusAttendanceByGo {
private Long id; // 主键ID
private String userName; // 人员姓名
private String pacePhoto; // 人脸照
private Long projectId; // 项目id
private String createBy; // 创建者
private String updateBy; // 更新者
private LocalDateTime createdAt; // 创建时间
private LocalDateTime updatedAt; // 更新时间
private LocalDateTime deletedAt; // 删除时间
private String clockOn; // 上午打卡
private String clockOff; // 下午打卡
private String printingDate; // 年月日打卡时间
private String isPinch; // 1正常,2迟到,3早退,4缺勤,5补卡
private String openid; // 微信id
private String pinchOpenId; // 代打id
private String clockRecord; // 多次打卡时间记录
private String pinchUserName; // 代打人姓名
private String commuter; // 上下班1上班 2下班
private String punchRange; // 打卡范围
private BigDecimal dailyWage; // 日薪
private String lng; // 经度
private String lat; // 纬度
private String location; // 逆编码地址信息
private LocalDateTime missing; // 缺卡统一处理时间
}

View File

@ -0,0 +1,91 @@
package org.dromara.bigscreen.domain;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.math.BigDecimal;
import java.time.LocalDateTime;
@Data
@TableName("bus_construction_user")
public class BusConstructionUser {
@TableId(type = IdType.AUTO)
private Long id; // 主键ID
private String openid; // 微信id
private String nickName; // 微信名称
private Long teamId; // 班组id
private String headIcon; // 登陆照片
private String pacePhoto; // 人脸照
private String userName; // 人员姓名
private Long projectId; // 项目id
private String status; // 状态0在职 1离职-字典position_status
private String isPinch; // 是否代打
private String ifManagement; // 是否班组管理
private String createBy; // 创建者
private String updateBy; // 更新者
private LocalDateTime createdAt; // 创建时间
private LocalDateTime updatedAt; // 更新时间
private LocalDateTime deletedAt; // 删除时间
private String phone; // 电话
private String sex; // 1:男,2女,3保密
private String sfzNation; // 身份证民族
private String sfzNumber; // 身份证号码
private String sfzStart; // 身份证有效开始期
private String sfzEnd; // 身份证有效结束期
private String sfzSite; // 身份证地址
private String sfzBirth; // 身份证出生日期
private String nativePlace; // 籍贯
private String yhkNumber; // 银行卡号
private String yhkOpeningBank; // 开户行
private String yhkCardholder; // 持卡人
private String typeOfWork; // 工种(字典)
private String clock; // 打卡(1启用打卡 2禁止打卡)
private Long labourserviceId; // 劳务公司id
private String entryDate; // 入场时间
private String leaveDate; // 离场时间
private BigDecimal salary; // 薪水为0表示无效
private String projectRecord; // 是否开启项目备案1开启 2不开启
private String wxOrPc; // 哪添加的1表示pc 2表示小程序
private String subscription; // 消息订阅状态1订阅 2无订阅
private String devNum; // 安全帽设备标识
}

View File

@ -0,0 +1,52 @@
package org.dromara.bigscreen.domain;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import java.time.LocalDateTime;
/**
* 企业大事记
*/
@Data
@TableName("bus_corporate_events")
public class BusCorporateEvents {
@TableId(type = IdType.AUTO)
private Long id;
/**
* 标题
*/
private String headline;
/**
* 内容
*/
private String content;
/**
* 创建人
*/
private Long createdBy;
/**
* 更新人
*/
private Long updatedBy;
/**
* 创建时间
*/
private LocalDateTime createdAt;
/**
* 更新时间
*/
private LocalDateTime updatedAt;
/**
* 删除时间(软删除标记)
*/
private LocalDateTime deletedAt;
}

View File

@ -0,0 +1,31 @@
package org.dromara.bigscreen.domain;
import lombok.Data;
import java.time.LocalDateTime;
@Data
public class BusProjectTeamByGo {
private Long id; // 主键id
private Long projectId; // 项目id
private String name; // 班组名称
private String isClockIn; // 范围内打卡0范围内打卡 1任何地点打卡
private String punchRange; // 打卡范围(id串)
private String remark; // 备注
private String createBy; // 创建者
private String updateBy; // 更新者
private Long createDept; // 创建部门
private LocalDateTime createTime; // 创建时间
private LocalDateTime updateTime; // 更新时间
}

View File

@ -0,0 +1,25 @@
package org.dromara.bigscreen.domain;
import lombok.Data;
import java.time.LocalDateTime;
@Data
public class BusProjectTeamMemberByGo {
private Long id; // 主键id
private Long teamId; // 班组id
private Long projectId; // 项目id
private Long memberId; // 施工人员id
private String postId; // 岗位0普通员工1组长
private String remark; // 备注
private LocalDateTime createTime; // 创建时间
private LocalDateTime updateTime; // 更新时间
}

View File

@ -0,0 +1,35 @@
package org.dromara.bigscreen.domain;
import lombok.Data;
import java.util.Date;
@Data
public class BusTour {
private Long id; // 主键ID
private Long projectId; // 项目id
private String tourCategory; // 类别字典(如:无人机识别、监控拍摄)
private String tourType; // 类型字典(如:安全帽、安全带)
private String picture; // 图片路径
private Integer num; // 违规数量
private String describe; // 故障描述
private Date createdAt; // 创建时间
private Date updatedAt; // 更新时间
private Date deletedAt; // 删除时间
private String tableName; // 表名
private Long tableId; // 表id
private String sxtName; // 摄像头名称
}

View File

@ -0,0 +1,34 @@
package org.dromara.bigscreen.domain;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.time.LocalDateTime;
@Data
@TableName("sys_project_introduce")
public class SysProjectIntroduce {
@TableId(type = IdType.AUTO)
private Long id; // 主键id
private Long projectId; // 项目id
private String headline; // 标题
private String richText; // 富文本
private String createdBy; // 创建人
private String updatedBy; // 更新人
private LocalDateTime createdAt; // 创建时间
private LocalDateTime updatedAt; // 更新时间
private LocalDateTime deletedAt; // 删除时间
private String files; // 附件
}

View File

@ -0,0 +1,31 @@
package org.dromara.bigscreen.domain.dto;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
/**
* @author lilemy
* @date 2025-09-09 15:16
*/
@Data
public class WeatherQueryReq implements Serializable {
@Serial
private static final long serialVersionUID = -2550570761981859666L;
/**
* 经度
*/
@NotBlank(message = "经度不能为空")
private String lng;
/**
* 纬度
*/
@NotBlank(message = "纬度不能为空")
private String lat;
}

View File

@ -0,0 +1,38 @@
package org.dromara.bigscreen.domain.vo;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.math.BigDecimal;
/**
* @author lilemy
* @date 2025-09-09 15:59
*/
@Data
public class EnterpriseKeyIndexVo implements Serializable {
@Serial
private static final long serialVersionUID = -3987781906203623727L;
/**
* 在建项目数量
*/
private Long ongoingProject;
/**
* 合同总额(单位:亿元)
*/
private Long totalContractAmount;
/**
* 总容量
*/
private BigDecimal totalCapacity;
/**
* 今日施工项目数量
*/
private Long todayProject;
}

View File

@ -0,0 +1,38 @@
package org.dromara.bigscreen.domain.vo;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.math.BigDecimal;
/**
* @author lilemy
* @date 2025-09-09 19:12
*/
@Data
public class OutputValueComparisonVo implements Serializable {
@Serial
private static final long serialVersionUID = -6902563869975528076L;
/**
* 项目id
*/
private Long projectId;
/**
* 项目名称
*/
private String projectName;
/**
* 计划产值
*/
private BigDecimal planValue;
/**
* 实际产值
*/
private BigDecimal actualValue;
}

View File

@ -0,0 +1,22 @@
package org.dromara.bigscreen.domain.vo;
import lombok.Data;
@Data
public class PeopleCountVo {
/**
* 施工人员
*/
private Long constructionPersonnelCount;
/**
* 分包人员
*/
private Long SubcontractorsCount;
/**
* 管理人员
*/
private Long managersCount;
}

View File

@ -0,0 +1,33 @@
package org.dromara.bigscreen.domain.vo;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.math.BigDecimal;
/**
* @author lilemy
* @date 2025-09-09 16:04
*/
@Data
public class PeopleOverviewVo implements Serializable {
@Serial
private static final long serialVersionUID = -4353811031023888101L;
/**
* 出勤人数
*/
private Long attendanceNumber;
/**
* 出勤率
*/
private BigDecimal attendanceRate;
/**
* 施工人员数量
*/
private Long constructorNumber;
}

View File

@ -0,0 +1,29 @@
package org.dromara.bigscreen.domain.vo;
import lombok.Data;
@Data
public class ProjectAttendanceCountVo {
/**
* 项目id
*/
private Long projectId;
/**
* 项目名称
*/
private String projectName;
/**
* 考勤人数
*/
private Long attendanceCount;
/**
* 考勤率
*/
private Double attendanceRate;
}

View File

@ -0,0 +1,39 @@
package org.dromara.bigscreen.domain.vo;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.List;
/**
* @author lilemy
* @date 2025-09-09 16:51
*/
@Data
public class ProjectProgressAnalysisVo implements Serializable {
@Serial
private static final long serialVersionUID = -2170524608375159201L;
/**
* 并网总容量 (MW)
*/
private BigDecimal gridConnectedCapacity;
/**
* 计划总容量 (MW)
*/
private BigDecimal plannedCapacity;
/**
* 延期项目数量
*/
private Integer delayedProjectCount;
/**
* 项目进度详情
*/
List<ProjectProgressDetailVo> projectProgressDetailList;
}

View File

@ -0,0 +1,33 @@
package org.dromara.bigscreen.domain.vo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.math.BigDecimal;
/**
* @author lilemy
* @date 2025-09-09 16:55
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class ProjectProgressDetailVo {
/**
* 项目名称
*/
private String projectName;
/**
* 项目容量
*/
private BigDecimal projectCapacity;
/**
* 施工进度百分比 (0~100)
*/
private BigDecimal completionRate;
}

View File

@ -0,0 +1,48 @@
package org.dromara.bigscreen.domain.vo;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.time.LocalDate;
/**
* @author lilemy
* @date 2025-09-09 20:04
*/
@Data
public class RiskEarlyWarningVo implements Serializable {
@Serial
private static final long serialVersionUID = 5250172770638676715L;
/**
* 时间
*/
private LocalDate date;
/**
* 风险类型
*/
private String riskType;
/**
* 报警内容
*/
private String alarmContent;
/**
* 威胁等级
*/
private String dangerLevel;
/**
* 来源
*/
private String source;
/**
* 告警等级
*/
private String alarmLevel;
}

View File

@ -0,0 +1,19 @@
package org.dromara.bigscreen.domain.vo;
import lombok.Data;
@Data
public class TodayAttendanceCountVo {
/**
* 考勤人员数量
*/
private Long attendanceCount;
/**
* 出勤率
*/
private Double attendanceRate;
}

View File

@ -0,0 +1,56 @@
package org.dromara.bigscreen.mapper;
import com.baomidou.dynamic.datasource.annotation.DS;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import org.dromara.bigscreen.domain.*;
import java.time.LocalDate;
import java.util.List;
/**
* @author lilemy
* @date 2025-09-10 19:32
*/
@DS("slave")
@Mapper
public interface ProjectBigScreenMapper {
@Select("select * from bus_corporate_events limit 10")
List<BusCorporateEvents> getBusCorporateEvents();
@Select("select * from sys_project_introduce where project_id = #{projectId}")
List<SysProjectIntroduce> selectByProjectId(@Param("projectId") Long projectId);
@Select("select count(*) from bus_construction_user where project_id = #{projectId} && deleted_at is NULL")
Integer getProjectUserCount(@Param("projectId") Long projectId);
@Select("select count(*) from bus_attendance where project_id = #{projectId} && printing_date = #{printingDate} && commuter = '1'")
Integer getAttendanceCount(@Param("projectId") Long projectId,
@Param("printingDate") String printingDate);
@Select("SELECT * FROM sys_project_team WHERE project_id = #{projectId} && deleted_at is NULL")
List<BusProjectTeamByGo> getTeamList(@Param("projectId") Long projectId);
@Select("SELECT * FROM bus_construction_user WHERE project_id = #{projectId} && deleted_at is NULL")
List<BusConstructionUser> getProjectUserList(@Param("projectId") Long projectId);
@Select({
"<script>",
"SELECT count(*) ",
"FROM bus_attendance ",
"WHERE openid IN ",
"<foreach collection='openIds' item='id' open='(' separator=',' close=')'>",
" #{id}",
"</foreach>",
"AND printing_date = #{printingDate} ",
"AND commuter = '1'",
"</script>"
})
Integer getAttendanceCountByOpenIds(@Param("openIds") List<String> openIds,
@Param("printingDate") LocalDate printingDate);
@Select("SELECT * FROM bus_tour WHERE project_id = #{projectId} AND deleted_at IS NULL LIMIT 10")
List<BusTour> selectTourByProjectId(@Param("projectId") Long projectId);
}

View File

@ -0,0 +1,53 @@
package org.dromara.bigscreen.service;
import org.dromara.bigscreen.domain.dto.WeatherQueryReq;
import org.dromara.bigscreen.domain.vo.EnterpriseKeyIndexVo;
import org.dromara.bigscreen.domain.vo.OutputValueComparisonVo;
import org.dromara.bigscreen.domain.vo.ProjectProgressAnalysisVo;
import org.dromara.bigscreen.domain.vo.RiskEarlyWarningVo;
import org.dromara.manager.weathermanager.vo.WeatherVo;
import java.util.List;
/**
* @author lilemy
* @date 2025-09-09 15:31
*/
public interface EnterpriseBigScreenService {
/**
* 获取关键指标
*
* @return 关键指标
*/
EnterpriseKeyIndexVo getEnterpriseKeyIndex();
/**
* 获取项目进度分析
*
* @return 项目进度分析
*/
ProjectProgressAnalysisVo getProjectProgressAnalysis();
/**
* 获取项目产值对比
*
* @return 项目产值对比
*/
List<OutputValueComparisonVo> getProjectOutputValueComparison();
/**
* 获取风险预警
*
* @return 风险预警
*/
List<RiskEarlyWarningVo> getRiskEarlyWarning();
/**
* 获取3天的天气列表
*
* @param req 查询参数
* @return 天气列表
*/
List<WeatherVo> getWeather3DaysList(WeatherQueryReq req);
}

View File

@ -1,8 +1,8 @@
package org.dromara.bigscreen.service;
import org.dromara.manager.weathermanager.vo.WeatherVo;
import org.dromara.project.domain.vo.project.BusProjectGisVo;
import org.dromara.project.domain.vo.project.BusProjectSafetyDayVo;
import org.dromara.project.domain.vo.project.BusProjectWeatherVo;
import java.util.List;
@ -25,7 +25,7 @@ public interface MoneyBigScreenService {
* @param projectId 项目id
* @return 项目天气
*/
List<BusProjectWeatherVo> getProjectWeather(Long projectId);
List<WeatherVo> getProjectWeather(Long projectId);
/**
* 获取项目安全天数

View File

@ -3,8 +3,9 @@ package org.dromara.bigscreen.service;
import org.dromara.bigscreen.domain.vo.ProjectImageProgressVo;
import org.dromara.bigscreen.domain.vo.ProjectPeopleVo;
import org.dromara.bigscreen.domain.vo.ProjectSafetyInspectionVo;
import org.dromara.manager.weathermanager.vo.WeatherVo;
import org.dromara.gps.domain.bo.GpsEquipmentBo;
import org.dromara.project.domain.vo.project.BusProjectSafetyDayVo;
import org.dromara.project.domain.vo.project.BusProjectWeatherVo;
import org.dromara.project.domain.vo.projectnews.BusProjectNewsVo;
import java.util.List;
@ -21,7 +22,7 @@ public interface ProjectBigScreenService {
* @param projectId 项目id
* @return 项目天气
*/
List<BusProjectWeatherVo> getProjectWeather(Long projectId);
List<WeatherVo> getProjectWeather(Long projectId);
/**
* 获取项目安全天数
@ -70,4 +71,8 @@ public interface ProjectBigScreenService {
* @return 项目概括
*/
String getProjectGeneralize(Long projectId);
List<String> getList(Long projectId);
void setList(GpsEquipmentBo bo);
}

View File

@ -0,0 +1,383 @@
package org.dromara.bigscreen.service.impl;
import cn.hutool.core.collection.CollUtil;
import jakarta.annotation.Resource;
import org.dromara.bigscreen.domain.dto.WeatherQueryReq;
import org.dromara.bigscreen.domain.vo.*;
import org.dromara.bigscreen.service.EnterpriseBigScreenService;
import org.dromara.common.core.enums.BusinessStatusEnum;
import org.dromara.common.core.utils.DateUtils;
import org.dromara.common.utils.BigDecimalUtil;
import org.dromara.ctr.domain.CtrExpensesContract;
import org.dromara.ctr.domain.CtrIncomeContract;
import org.dromara.ctr.service.ICtrExpensesContractService;
import org.dromara.ctr.service.ICtrIncomeContractService;
import org.dromara.manager.weathermanager.WeatherConstant;
import org.dromara.manager.weathermanager.WeatherManager;
import org.dromara.manager.weathermanager.vo.WeatherVo;
import org.dromara.out.domain.BusProcurement;
import org.dromara.out.domain.OutConstructionValue;
import org.dromara.out.domain.OutMonthPlanAudit;
import org.dromara.out.domain.OutValueAllocation;
import org.dromara.out.service.IBusProcurementService;
import org.dromara.out.service.IOutConstructionValueService;
import org.dromara.out.service.IOutMonthPlanAuditService;
import org.dromara.out.service.IOutValueAllocationService;
import org.dromara.progress.domain.PgsProgressCategory;
import org.dromara.progress.domain.PgsProgressPlanDetail;
import org.dromara.progress.service.IPgsProgressCategoryService;
import org.dromara.progress.service.IPgsProgressPlanDetailService;
import org.dromara.project.domain.BusProject;
import org.dromara.project.service.IBusProjectService;
import org.dromara.safety.domain.HseRecognizeRecord;
import org.dromara.safety.domain.HseViolationLevel;
import org.dromara.safety.domain.HseViolationRecord;
import org.dromara.safety.service.IHseRecognizeRecordService;
import org.dromara.safety.service.IHseViolationLevelService;
import org.dromara.safety.service.IHseViolationRecordService;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.util.*;
import java.util.stream.Collectors;
/**
* @author lilemy
* @date 2025-09-09 15:32
*/
@Service
public class EnterpriseBigScreenServiceImpl implements EnterpriseBigScreenService {
@Resource
private WeatherManager weatherManager;
@Resource
private IBusProjectService projectService;
@Resource
private IPgsProgressCategoryService progressCategoryService;
@Resource
private IPgsProgressPlanDetailService progressPlanDetailService;
@Resource
private ICtrIncomeContractService incomeContractService;
@Resource
private ICtrExpensesContractService expensesContractService;
@Resource
private IOutValueAllocationService outValueAllocationService;
@Resource
private IOutMonthPlanAuditService outMonthPlanAuditService;
@Resource
private IOutConstructionValueService outConstructionValueService;
@Resource
private IHseViolationRecordService hseViolationRecordService;
@Resource
private IHseViolationLevelService hseViolationLevelService;
@Resource
private IBusProcurementService busProcurementService;
@Resource
private IHseRecognizeRecordService hseRecognizeRecordService;
/**
* 获取关键指标
*
* @return 关键指标
*/
@Override
public EnterpriseKeyIndexVo getEnterpriseKeyIndex() {
EnterpriseKeyIndexVo vo = new EnterpriseKeyIndexVo();
// 在建项目
List<BusProject> projectList = projectService.lambdaQuery()
.eq(BusProject::getStatus, 0)
.eq(BusProject::getPId, 0)
.list();
// 总容量
BigDecimal totalCapacity = projectList.stream().map(BusProject::getActual)
.filter(s -> s != null && !s.isBlank()) // 过滤掉空值
.map(BigDecimal::new) // 转成 BigDecimal
.reduce(BigDecimal.ZERO, BigDecimal::add);
// 总合同金额
List<CtrIncomeContract> incomeContractList = incomeContractService.lambdaQuery()
.select(CtrIncomeContract::getAmount)
.list();
BigDecimal totalIncomeAmount = incomeContractList.stream()
.map(CtrIncomeContract::getAmount)
.filter(Objects::nonNull)
.reduce(BigDecimal.ZERO, BigDecimal::add);
List<CtrExpensesContract> expensesContractList = expensesContractService.lambdaQuery()
.select(CtrExpensesContract::getAmount)
.list();
BigDecimal totalExpensesAmount = expensesContractList.stream()
.map(CtrExpensesContract::getAmount)
.filter(Objects::nonNull)
.reduce(BigDecimal.ZERO, BigDecimal::add);
// 今日施工项目数量
Long todayProject = progressPlanDetailService.lambdaQuery()
.select(PgsProgressPlanDetail::getProjectId)
.eq(PgsProgressPlanDetail::getDate, LocalDate.now())
.list()
.stream().map(PgsProgressPlanDetail::getProjectId).distinct().count();
vo.setOngoingProject((long) projectList.size());
vo.setTotalCapacity(totalCapacity);
vo.setTotalContractAmount(totalIncomeAmount.add(totalExpensesAmount).longValue());
vo.setTodayProject(todayProject);
return vo;
}
/**
* 获取项目进度分析
*
* @return 项目进度分析
*/
@Override
public ProjectProgressAnalysisVo getProjectProgressAnalysis() {
ProjectProgressAnalysisVo vo = new ProjectProgressAnalysisVo();
// 1. 查询顶级项目
List<BusProject> projectList = projectService.lambdaQuery()
.select(BusProject::getId, BusProject::getProjectName, BusProject::getActual, BusProject::getPlan)
.eq(BusProject::getStatus, 0)
.eq(BusProject::getPId, 0)
.list();
if (CollUtil.isEmpty(projectList)) {
return vo;
}
// 2. 拿到顶级项目 id
List<Long> projectIds = projectList.stream()
.map(BusProject::getId)
.distinct()
.toList();
// 3. 查询子项目pId 在顶级项目id里
List<BusProject> subProject = projectService.lambdaQuery()
.select(BusProject::getId, BusProject::getPId)
.in(BusProject::getPId, projectIds)
.list();
// 4. 按父项目id分组子项目
Map<Long, List<BusProject>> subProjectMap = subProject.stream()
.collect(Collectors.groupingBy(BusProject::getPId));
// 5. 查询所有子项目的进度分类
List<PgsProgressCategory> progressCategoryList = progressCategoryService.lambdaQuery()
.select(PgsProgressCategory::getId,
PgsProgressCategory::getProjectId,
PgsProgressCategory::getCompleted,
PgsProgressCategory::getTotal)
.in(CollUtil.isNotEmpty(subProject),
PgsProgressCategory::getProjectId,
subProject.stream().map(BusProject::getId).toList())
.list();
// 6. 按子项目id分组进度分类
Map<Long, List<PgsProgressCategory>> progressCategoryMap = progressCategoryList.stream()
.collect(Collectors.groupingBy(PgsProgressCategory::getProjectId));
// 并网容量 =(完工产值/计划总产值)* 计划容量
List<OutputValueComparisonVo> list = getProjectOutputValueComparison();
BigDecimal actualValue = list.stream()
.map(OutputValueComparisonVo::getActualValue)
.filter(Objects::nonNull)
.reduce(BigDecimal.ZERO, BigDecimal::add);
BigDecimal planValue = list.stream()
.map(OutputValueComparisonVo::getPlanValue)
.filter(Objects::nonNull)
.reduce(BigDecimal.ZERO, BigDecimal::add);
BigDecimal plannedCapacity = projectList.stream().map(BusProject::getPlan)
.filter(s -> s != null && !s.isBlank()) // 过滤掉空值
.map(BigDecimal::new) // 转成 BigDecimal
.reduce(BigDecimal.ZERO, BigDecimal::add);
BigDecimal gridConnectedCapacity = BigDecimal.ZERO;
if (planValue.compareTo(BigDecimal.ZERO) != 0) {
gridConnectedCapacity = plannedCapacity.multiply(actualValue.divide(planValue, 2, RoundingMode.HALF_UP));
}
vo.setGridConnectedCapacity(gridConnectedCapacity);
List<ProjectProgressDetailVo> detailVoList = projectList.stream().map(project -> {
ProjectProgressDetailVo detailVo = new ProjectProgressDetailVo();
detailVo.setProjectName(project.getProjectName());
detailVo.setProjectCapacity(new BigDecimal(project.getActual()));
List<BusProject> children = subProjectMap.getOrDefault(project.getId(), List.of());
List<PgsProgressCategory> categoryList = new ArrayList<>();
for (BusProject child : children) {
categoryList.addAll(progressCategoryMap.getOrDefault(child.getId(), List.of()));
}
if (CollUtil.isNotEmpty(categoryList)) {
BigDecimal completed = categoryList.stream().map(PgsProgressCategory::getCompleted)
.filter(Objects::nonNull)
.reduce(BigDecimal.ZERO, BigDecimal::add);
BigDecimal total = categoryList.stream().map(PgsProgressCategory::getTotal)
.filter(Objects::nonNull)
.reduce(BigDecimal.ZERO, BigDecimal::add);
detailVo.setCompletionRate(BigDecimalUtil.toPercentage(completed, total));
}
return detailVo;
}).toList();
vo.setGridConnectedCapacity(gridConnectedCapacity);
vo.setPlannedCapacity(plannedCapacity);
// todo 获取延迟项目数
vo.setDelayedProjectCount(0);
vo.setProjectProgressDetailList(detailVoList);
return vo;
}
/**
* 获取项目产值对比
*
* @return 项目产值对比
*/
@Override
public List<OutputValueComparisonVo> getProjectOutputValueComparison() {
// 查询顶级项目
List<BusProject> projectList = projectService.lambdaQuery()
.select(BusProject::getId, BusProject::getProjectName, BusProject::getActual, BusProject::getPlan)
.eq(BusProject::getStatus, 0)
.eq(BusProject::getPId, 0)
.list();
// 计划产值
List<OutValueAllocation> planList = outValueAllocationService.lambdaQuery()
.select(OutValueAllocation::getProjectId, OutValueAllocation::getOwnerTotalValue)
.list();
Map<Long, BigDecimal> planMap = planList.stream()
.collect(Collectors.toMap(OutValueAllocation::getProjectId, OutValueAllocation::getOwnerTotalValue));
// 实际产值
// 施工产值
List<OutConstructionValue> constructionValueList = outConstructionValueService.lambdaQuery()
.select(OutConstructionValue::getProjectId, OutConstructionValue::getOwnerValue)
.eq(OutConstructionValue::getAuditStatus, BusinessStatusEnum.FINISH.getStatus())
.list();
// 采购产值
List<BusProcurement> purchaseValueList = busProcurementService.lambdaQuery()
.select(BusProcurement::getProjectId, BusProcurement::getAcceptedQuantity, BusProcurement::getUnitPrice)
.list();
// 设计产值
List<OutMonthPlanAudit> designValueList = outMonthPlanAuditService.lambdaQuery()
.select(OutMonthPlanAudit::getProjectId, OutMonthPlanAudit::getDesignValue)
.eq(OutMonthPlanAudit::getType, "1")
.list();
// 封装数据
return projectList.stream().map(project -> {
OutputValueComparisonVo vo = new OutputValueComparisonVo();
vo.setProjectId(project.getId());
vo.setProjectName(project.getProjectName());
vo.setPlanValue(planMap.getOrDefault(project.getId(), BigDecimal.ZERO));
BigDecimal actualValue = BigDecimal.ZERO;
// 设计产值
List<OutMonthPlanAudit> designValue = designValueList.stream()
.filter(design -> design.getProjectId().equals(project.getId()))
.toList();
if (CollUtil.isNotEmpty(designValue)) {
BigDecimal dValue = designValue.stream()
.filter(Objects::nonNull)
.map(OutMonthPlanAudit::getDesignValue)
.reduce(BigDecimal.ZERO, BigDecimal::add);
actualValue = actualValue.add(dValue);
}
// 施工产值
List<OutConstructionValue> constructionValue = constructionValueList.stream()
.filter(construction -> construction.getProjectId().equals(project.getId()))
.toList();
if (CollUtil.isNotEmpty(constructionValue)) {
BigDecimal cValue = constructionValue.stream()
.filter(Objects::nonNull)
.map(OutConstructionValue::getOwnerValue)
.reduce(BigDecimal.ZERO, BigDecimal::add);
actualValue = actualValue.add(cValue);
}
// 采购产值
List<BusProcurement> purchaseValue = purchaseValueList.stream()
.filter(purchase -> purchase.getProjectId().equals(project.getId()))
.toList();
if (CollUtil.isNotEmpty(purchaseValue)) {
BigDecimal pValue = purchaseValue.stream()
.filter(Objects::nonNull)
.filter(purchase -> purchase.getAcceptedQuantity() != null && purchase.getUnitPrice() != null)
.map(purchase -> purchase.getAcceptedQuantity().multiply(purchase.getUnitPrice()))
.reduce(BigDecimal.ZERO, BigDecimal::add);
actualValue = actualValue.add(pValue);
}
vo.setActualValue(actualValue);
return vo;
}).filter(vo -> vo.getActualValue().compareTo(BigDecimal.ZERO) > 0)
.toList();
}
/**
* 获取风险预警
*
* @return 风险预警
*/
@Override
public List<RiskEarlyWarningVo> getRiskEarlyWarning() {
List<HseViolationRecord> recordList = hseViolationRecordService.lambdaQuery()
.last("limit 10")
.list();
recordList = recordList.stream()
.filter(record -> record.getReviewType() == null || record.getReviewType().equals("2"))
.toList();
if (CollUtil.isEmpty(recordList)) {
return Collections.emptyList();
}
// 项目映射
Set<Long> projectIds = recordList.stream()
.map(HseViolationRecord::getProjectId)
.collect(Collectors.toSet());
List<BusProject> projectList = projectService.lambdaQuery()
.select(BusProject::getId, BusProject::getProjectName)
.in(BusProject::getId, projectIds)
.list();
Map<Long, String> projectMap = projectList.stream()
.collect(Collectors.toMap(BusProject::getId, BusProject::getProjectName));
// 风险映射
Set<Long> levelIds = recordList.stream()
.map(HseViolationRecord::getLevelId)
.collect(Collectors.toSet());
List<HseViolationLevel> levelList = hseViolationLevelService.lambdaQuery()
.select(HseViolationLevel::getId, HseViolationLevel::getRiskType, HseViolationLevel::getViolationLevel)
.in(HseViolationLevel::getId, levelIds)
.list();
Map<Long, HseViolationLevel> levelMap = levelList.stream()
.collect(Collectors.toMap(HseViolationLevel::getId, level -> level));
// 识别记录映射
Set<Long> recognizeIds = recordList.stream()
.map(HseViolationRecord::getRecognizeId)
.collect(Collectors.toSet());
List<HseRecognizeRecord> recognizeRecordList = hseRecognizeRecordService.lambdaQuery()
.in(HseRecognizeRecord::getId, recognizeIds)
.list();
Map<Long, HseRecognizeRecord> recognizeRecordMap = recognizeRecordList.stream()
.collect(Collectors.toMap(HseRecognizeRecord::getId, recognize -> recognize));
return recordList.stream().map(record -> {
RiskEarlyWarningVo vo = new RiskEarlyWarningVo();
Date violationTime = record.getViolationTime() != null ? record.getViolationTime() : record.getCreateTime();
vo.setDate(DateUtils.toLocalDate(violationTime));
vo.setSource(projectMap.getOrDefault(record.getProjectId(), "未知项目"));
vo.setRiskType(record.getViolationType());
if (levelMap.containsKey(record.getLevelId())) {
HseViolationLevel level = levelMap.get(record.getLevelId());
vo.setAlarmLevel(level.getRiskType());
vo.setDangerLevel(level.getViolationLevel());
}
if (recognizeRecordMap.containsKey(record.getRecognizeId())) {
HseRecognizeRecord hseRecognizeRecord = recognizeRecordMap.get(record.getRecognizeId());
vo.setAlarmContent(hseRecognizeRecord.getDescription());
}
return vo;
}).toList();
}
/**
* 获取3天的天气列表
*
* @param req 查询参数
* @return 天气列表
*/
@Override
public List<WeatherVo> getWeather3DaysList(WeatherQueryReq req) {
return weatherManager.getWeatherListVo(req.getLng(), req.getLat(), WeatherConstant.THREE_DAYS_WEATHER_PATH);
}
}

View File

@ -4,10 +4,10 @@ import jakarta.annotation.Resource;
import org.dromara.bigscreen.service.MoneyBigScreenService;
import org.dromara.common.core.constant.HttpStatus;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.manager.weathermanager.vo.WeatherVo;
import org.dromara.project.domain.BusProject;
import org.dromara.project.domain.vo.project.BusProjectGisVo;
import org.dromara.project.domain.vo.project.BusProjectSafetyDayVo;
import org.dromara.project.domain.vo.project.BusProjectWeatherVo;
import org.dromara.project.service.IBusProjectService;
import org.springframework.stereotype.Service;
@ -40,7 +40,7 @@ public class MoneyBigScreenServiceImpl implements MoneyBigScreenService {
* @return 项目天气
*/
@Override
public List<BusProjectWeatherVo> getProjectWeather(Long projectId) {
public List<WeatherVo> getProjectWeather(Long projectId) {
checkProject(projectId);
return projectService.getWeather(projectId);
}

View File

@ -1,8 +1,8 @@
package org.dromara.bigscreen.service.impl;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONUtil;
import jakarta.annotation.Resource;
import org.dromara.bigscreen.domain.vo.ProjectImageProgressVo;
import org.dromara.bigscreen.domain.vo.ProjectPeopleVo;
@ -11,14 +11,19 @@ import org.dromara.bigscreen.domain.vo.ProjectTeamAttendanceVo;
import org.dromara.bigscreen.service.ProjectBigScreenService;
import org.dromara.common.core.constant.HttpStatus;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.redis.utils.RedisUtils;
import org.dromara.common.utils.BigDecimalUtil;
import org.dromara.contractor.domain.SubConstructionUser;
import org.dromara.contractor.service.ISubConstructionUserService;
import org.dromara.gps.domain.bo.GpsEquipmentBo;
import org.dromara.manager.weathermanager.vo.WeatherVo;
import org.dromara.progress.domain.PgsProgressCategory;
import org.dromara.progress.domain.enums.PgsProgressUnitTypeEnum;
import org.dromara.progress.service.IPgsProgressCategoryService;
import org.dromara.project.domain.BusProject;
import org.dromara.project.domain.BusProjectTeam;
import org.dromara.project.domain.BusProjectTeamMember;
import org.dromara.project.domain.vo.project.BusProjectSafetyDayVo;
import org.dromara.project.domain.vo.project.BusProjectWeatherVo;
import org.dromara.project.domain.vo.projectnews.BusProjectNewsVo;
import org.dromara.project.service.*;
import org.dromara.safety.domain.HseRecognizeRecord;
@ -27,11 +32,9 @@ import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.*;
import java.util.stream.Collectors;
/**
@ -62,6 +65,9 @@ public class ProjectBigScreenServiceImpl implements ProjectBigScreenService {
@Resource
private IBusProjectTeamMemberService projectTeamMemberService;
@Resource
private IPgsProgressCategoryService progressCategoryService;
/**
* 获取项目天气
*
@ -69,7 +75,7 @@ public class ProjectBigScreenServiceImpl implements ProjectBigScreenService {
* @return 项目天气
*/
@Override
public List<BusProjectWeatherVo> getProjectWeather(Long projectId) {
public List<WeatherVo> getProjectWeather(Long projectId) {
checkProject(projectId);
return projectService.getWeather(projectId);
}
@ -203,50 +209,130 @@ public class ProjectBigScreenServiceImpl implements ProjectBigScreenService {
@Override
public ProjectImageProgressVo getProjectImageProgress(Long projectId) {
checkProject(projectId);
// 生成 0 ~ 100 的随机 double
double random = RandomUtil.randomDouble(0, 100);
// 保留两位小数(四舍五入)
double result = NumberUtil.round(random, 2).doubleValue();
ProjectImageProgressVo vo = new ProjectImageProgressVo();
vo.setAreaPercentage(BigDecimal.valueOf(result));
// 生成 0 ~ 100 的随机 double
double random1 = RandomUtil.randomDouble(0, 100);
// 保留两位小数(四舍五入)
double result1 = NumberUtil.round(random1, 2).doubleValue();
vo.setRoadPercentage(BigDecimal.valueOf(result1));
// 生成 0 ~ 100 的随机 double
double random2 = RandomUtil.randomDouble(0, 100);
// 保留两位小数(四舍五入)
double result2 = NumberUtil.round(random2, 2).doubleValue();
vo.setCollectorLinePercentage(BigDecimal.valueOf(result2));
// 生成 0 ~ 100 的随机 double
double random3 = RandomUtil.randomDouble(0, 100);
// 保留两位小数(四舍五入)
double result3 = NumberUtil.round(random3, 2).doubleValue();
vo.setExportLinePercentage(BigDecimal.valueOf(result3));
// 生成 0 ~ 100 的随机 double
double random4 = RandomUtil.randomDouble(0, 100);
// 保留两位小数(四舍五入)
double result4 = NumberUtil.round(random4, 2).doubleValue();
vo.setSubstationPercentage(BigDecimal.valueOf(result4));
// 生成 0 ~ 100 的随机 double
double random5 = RandomUtil.randomDouble(0, 100);
// 保留两位小数(四舍五入)
double result5 = NumberUtil.round(random5, 2).doubleValue();
vo.setBoxTransformerPercentage(BigDecimal.valueOf(result5));
// 生成 0 ~ 100 的随机 double
double random6 = RandomUtil.randomDouble(0, 100);
// 保留两位小数(四舍五入)
double result6 = NumberUtil.round(random6, 2).doubleValue();
vo.setTotalPercentage(BigDecimal.valueOf(result6));
// 获取所有子项目列表
List<BusProject> subProjectList = projectService.lambdaQuery()
.eq(BusProject::getPId, projectId)
.list();
if (CollUtil.isEmpty(subProjectList)) {
return vo;
}
// 子项目id列表
List<Long> subProjectIds = subProjectList.stream().map(BusProject::getId).toList();
// 计算集电线路
vo.setCollectorLinePercentage(BigDecimal.valueOf(0.00));
// 计算送出线路
vo.setExportLinePercentage(BigDecimal.valueOf(0.00));
// 计算升压站
vo.setSubstationPercentage(BigDecimal.valueOf(0.00));
// 计算光伏场区
vo.setAreaPercentage(BigDecimal.valueOf(0.00));
// 计算道路
vo.setRoadPercentage(BigDecimal.valueOf(0.00));
// 计算箱变
vo.setBoxTransformerPercentage(BigDecimal.ZERO);
// 获取集电线路、送出线路、升压站数据
List<PgsProgressCategory> progressCategoryList = progressCategoryService.lambdaQuery()
.in(PgsProgressCategory::getProjectId, subProjectIds)
.in(PgsProgressCategory::getName, "集电线路", "送出线路", "升压站", "光伏场区")
.eq(PgsProgressCategory::getParentId, 0L)
.list();
if (CollUtil.isNotEmpty(progressCategoryList)) {
List<Long> categoryIds = progressCategoryList.stream().map(PgsProgressCategory::getId).distinct().toList();
List<PgsProgressCategory> leafNodesByTopIds = progressCategoryService.getLeafNodesByTopIds(categoryIds);
Map<String, List<PgsProgressCategory>> categoryMap = progressCategoryList.stream()
.collect(Collectors.groupingBy(PgsProgressCategory::getName));
// 计算集电线路
if (categoryMap.containsKey("集电线路")) {
List<PgsProgressCategory> categoryList = categoryMap.get("集电线路");
List<Long> ids = categoryList.stream().map(PgsProgressCategory::getId).distinct().toList();
List<PgsProgressCategory> list = leafNodesByTopIds.stream()
.filter(node -> {
Set<Long> ancestorSet = Arrays.stream(node.getAncestors().split(",")).map(Long::parseLong).collect(Collectors.toSet());
return ids.stream().anyMatch(ancestorSet::contains);
}).toList();
BigDecimal percentage = progressCategoryService.getCompletedPercentage(list);
if (percentage.compareTo(BigDecimal.ZERO) > 0) {
vo.setCollectorLinePercentage(percentage);
}
}
// 计算送出线路
if (categoryMap.containsKey("送出线路")) {
List<PgsProgressCategory> categoryList = categoryMap.get("送出线路");
List<Long> ids = categoryList.stream().map(PgsProgressCategory::getId).distinct().toList();
List<PgsProgressCategory> list = leafNodesByTopIds.stream()
.filter(node -> {
Set<Long> ancestorSet = Arrays.stream(node.getAncestors().split(",")).map(Long::parseLong).collect(Collectors.toSet());
return ids.stream().anyMatch(ancestorSet::contains);
}).toList();
BigDecimal percentage = progressCategoryService.getCompletedPercentage(list);
if (percentage.compareTo(BigDecimal.ZERO) > 0) {
vo.setExportLinePercentage(percentage);
}
}
// 计算升压站
if (categoryMap.containsKey("升压站")) {
List<PgsProgressCategory> categoryList = categoryMap.get("升压站");
List<Long> ids = categoryList.stream().map(PgsProgressCategory::getId).distinct().toList();
List<PgsProgressCategory> list = leafNodesByTopIds.stream()
.filter(node -> {
Set<Long> ancestorSet = Arrays.stream(node.getAncestors().split(",")).map(Long::parseLong).collect(Collectors.toSet());
return ids.stream().anyMatch(ancestorSet::contains);
}).toList();
BigDecimal percentage = progressCategoryService.getCompletedPercentage(list);
if (percentage.compareTo(BigDecimal.ZERO) > 0) {
vo.setSubstationPercentage(percentage);
}
}
// 计算光伏场区
if (categoryMap.containsKey("光伏场区")) {
List<PgsProgressCategory> categoryList = categoryMap.get("光伏场区");
List<Long> ids = categoryList.stream().map(PgsProgressCategory::getId).distinct().toList();
List<PgsProgressCategory> list = leafNodesByTopIds.stream()
.filter(node -> {
Set<Long> ancestorSet = Arrays.stream(node.getAncestors().split(",")).map(Long::parseLong).collect(Collectors.toSet());
return ids.stream().anyMatch(ancestorSet::contains);
}).toList();
BigDecimal percentage = progressCategoryService.getCompletedPercentage(list);
if (percentage.compareTo(BigDecimal.ZERO) > 0) {
vo.setAreaPercentage(percentage);
}
}
}
// 计算道路
List<PgsProgressCategory> roadCategoryList = progressCategoryService.lambdaQuery()
.in(PgsProgressCategory::getProjectId, subProjectIds)
.like(PgsProgressCategory::getName, "道路")
.ne(PgsProgressCategory::getUnitType, PgsProgressUnitTypeEnum.NULL.getValue())
.list();
if (CollUtil.isNotEmpty(roadCategoryList)) {
BigDecimal percentage = progressCategoryService.getCompletedPercentage(roadCategoryList);
if (percentage.compareTo(BigDecimal.ZERO) > 0) {
vo.setRoadPercentage(percentage);
}
}
// 计算箱变
List<PgsProgressCategory> boxTransformerCategoryList = progressCategoryService.lambdaQuery()
.in(PgsProgressCategory::getProjectId, subProjectIds)
.like(PgsProgressCategory::getName, "箱变")
.ne(PgsProgressCategory::getUnitType, PgsProgressUnitTypeEnum.NULL.getValue())
.list();
if (CollUtil.isNotEmpty(boxTransformerCategoryList)) {
BigDecimal percentage = progressCategoryService.getCompletedPercentage(boxTransformerCategoryList);
if (percentage.compareTo(BigDecimal.ZERO) > 0) {
vo.setBoxTransformerPercentage(percentage);
}
}
// 计算总进度
vo.setTotalPercentage(
vo.getCollectorLinePercentage()
.add(vo.getExportLinePercentage())
.add(vo.getSubstationPercentage())
.add(vo.getAreaPercentage())
.add(vo.getRoadPercentage())
.add(vo.getBoxTransformerPercentage())
.divide(BigDecimal.valueOf(6), 2, RoundingMode.HALF_UP) // 保留两位小数
);
return vo;
}
@ -276,4 +362,26 @@ public class ProjectBigScreenServiceImpl implements ProjectBigScreenService {
throw new ServiceException("项目不存在", HttpStatus.NOT_FOUND);
}
}
@Override
public List<String> getList(Long projectId) {
if (projectId == null){
throw new ServiceException("项目id不能为空");
}
Object object = RedisUtils.getCacheObject("xmjdp:" + projectId);
if (object == null) {
return null;
}
JSONArray objects = JSONUtil.parseArray(object);
return objects.toList(String.class);
}
@Override
public void setList(GpsEquipmentBo bo) {
if (bo.getProjectId() == null){
throw new ServiceException("项目id不能为空");
}
RedisUtils.setCacheObject("xmjdp:"+bo.getProjectId(), bo.getIdList());
}
}

View File

@ -9,7 +9,7 @@ import org.dromara.cailiaoshebei.domain.bo.*;
import org.dromara.cailiaoshebei.domain.vo.*;
import org.dromara.cailiaoshebei.service.IBusCailiaoshebeiPiciService;
import org.dromara.common.utils.BatchNumberGenerator;
import org.dromara.design.domain.bo.ObtainTheListReq;
import org.dromara.design.domain.bo.CoryObtainTheListReq;
import org.dromara.design.domain.vo.ObtainTheListRes;
import org.dromara.design.service.IBusBillofquantitiesVersionsService;
import org.springframework.web.bind.annotation.*;
@ -25,7 +25,6 @@ import org.dromara.common.log.enums.BusinessType;
import org.dromara.cailiaoshebei.service.IBusCailiaoshebeiService;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import java.util.Date;
import java.util.List;
/**
@ -245,7 +244,7 @@ public class BusCailiaoshebeiController extends BaseController {
*/
@SaCheckPermission("design:cailiaoshebei:obtainTheList")
@GetMapping("/obtainTheList")
public R<List<ObtainTheListRes>> obtainTheList(ObtainTheListReq bo, PageQuery pageQuery) {
public R<List<ObtainTheListRes>> obtainTheList(CoryObtainTheListReq bo, PageQuery pageQuery) {
return R.ok(busBillofquantitiesVersionsService.obtainTheList(bo));
}

View File

@ -1,5 +1,6 @@
package org.dromara.cailiaoshebei.controller;
import java.math.BigDecimal;
import java.util.List;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
@ -14,10 +15,10 @@ import org.dromara.common.core.enums.BusinessStatusEnum;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.design.domain.BusBillofquantities;
import org.dromara.design.domain.BusBillofquantitiesVersions;
import org.dromara.design.domain.dto.desCollect.DesCollectBatchDto;
import org.dromara.design.domain.vo.DesCollectVo;
import org.dromara.design.domain.bo.CoryObtainTheListReq;
import org.dromara.design.service.IBusBillofquantitiesService;
import org.dromara.design.service.IBusBillofquantitiesVersionsService;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.validation.annotation.Validated;
import org.dromara.common.idempotent.annotation.RepeatSubmit;
@ -122,17 +123,26 @@ public class BusMrpBaseController extends BaseController {
/**
* 批量新增或修改
*/
@SaCheckPermission("cailiaoshebei:mrpBase:addbatch")
@RepeatSubmit()
@PostMapping("/batch")
public R<Void> batchAddOrUpdate(@RequestBody BusMrpDto dto) {
return toAjax(busMrpBaseService.batchAddOrUpdate(dto));
}
/**
* 获取剩余量
*/
@GetMapping("/remaining")
public R<BigDecimal> remaining(Long suppliespriceId,Long mrpBaseId) {
return R.ok(busMrpBaseService.remaining(suppliespriceId,mrpBaseId));
}
/**
* 导入物资需求批次计划
*/
@SaCheckPermission("cailiaoshebei:mrpBase:add")
@SaCheckPermission("cailiaoshebei:mrpBase:import")
@Log(title = "物资-批次需求计划基础信息", businessType = BusinessType.INSERT)
@RepeatSubmit()
@PostMapping("/import")
@ -149,9 +159,10 @@ public class BusMrpBaseController extends BaseController {
public R<List<BusBillofquantitiesVersions>> obtainTheVersion(Long projectId) {
List<BusBillofquantitiesVersions> list = busBillofquantitiesVersionsService.list(Wrappers.<BusBillofquantitiesVersions>lambdaQuery()
.eq(BusBillofquantitiesVersions::getProjectId, projectId)
.eq(BusBillofquantitiesVersions::getWorkOrderType, "3")
.eq(BusBillofquantitiesVersions::getStatus, BusinessStatusEnum.FINISH.getStatus())
);
if (!list.isEmpty()){
if (CollectionUtils.isEmpty(list)){
throw new ServiceException("请先完成物资工程量清单");
}
return R.ok(list);
@ -160,12 +171,13 @@ public class BusMrpBaseController extends BaseController {
/**
* 获取工程量清单列表
*/
@SaCheckPermission("cailiaoshebei:purchaseDoc:add")
@GetMapping("/engineeringList")
public R<List<BusBillofquantities>> obtainTheList(Long projectId) {
@SaCheckPermission("cailiaoshebei:purchaseDoc:coryEngineeringList")
@GetMapping("/coryEngineeringList")
public R<List<BusBillofquantities>> obtainTheList(CoryObtainTheListReq req) {
BusBillofquantitiesVersions one = busBillofquantitiesVersionsService.getOne(Wrappers.<BusBillofquantitiesVersions>lambdaQuery()
.eq(BusBillofquantitiesVersions::getWorkOrderType, "3") //物资工程量清单
.eq(BusBillofquantitiesVersions::getProjectId, projectId)
.eq(BusBillofquantitiesVersions::getProjectId, req.getProjectId())
.eq(BusBillofquantitiesVersions::getVersions, req.getVersions())
.eq(BusBillofquantitiesVersions::getStatus, BusinessStatusEnum.FINISH.getStatus())
.last("limit 1")
);

View File

@ -1,6 +1,7 @@
package org.dromara.cailiaoshebei.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.constraints.NotEmpty;
@ -33,8 +34,12 @@ import org.dromara.design.service.IBusBillofquantitiesVersionsService;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
/**
* 物资-采购联系单
@ -67,6 +72,8 @@ public class BusPurchaseDocController extends BaseController {
return busPurchaseDocService.queryPageList(bo, pageQuery);
}
/**
* 导出物资-采购联系单列表
*/
@ -81,7 +88,7 @@ public class BusPurchaseDocController extends BaseController {
/**
* 根据主键导出物资-采购联系单
*/
@SaCheckPermission("cailiaoshebei:purchaseDoc:downloadWord")
@SaCheckPermission("cailiaoshebei:purchaseDoc:exportWord")
@Log(title = "物资-采购联系单", businessType = BusinessType.EXPORT)
@PostMapping("/export/word")
public void exportWordById(@NotNull(message = "主键不能为空") Long id,
@ -106,7 +113,7 @@ public class BusPurchaseDocController extends BaseController {
*
* @param id 主键
*/
@SaCheckPermission("cailiaoshebei:purchaseDoc:queryPdf")
@SaCheckPermission("cailiaoshebei:purchaseDoc:pdf")
@GetMapping("/pdf/{id}")
public R<String> getPic(@NotNull(message = "主键不能为空")
@PathVariable Long id) {
@ -157,36 +164,45 @@ public class BusPurchaseDocController extends BaseController {
@PathVariable("id") Long id) {
List<BusPlanDocAssociation> list = planDocAssociationService.list(Wrappers.lambdaQuery(BusPlanDocAssociation.class)
.eq(BusPlanDocAssociation::getDocId, id));
List<Long> list1 = list.stream().map(BusPlanDocAssociation::getPlanId).toList();
if (list1.isEmpty()) {
if (CollectionUtil.isEmpty(list)) {
return R.ok(new ArrayList<>());
}
Map<Long, BigDecimal> collect = list.stream()
.filter(Objects::nonNull) // 过滤空对象
.collect(Collectors.toMap(
BusPlanDocAssociation::getPlanId,
BusPlanDocAssociation::getDemandQuantity,
(existing, replacement) -> existing // 保留第一个遇到的重复键
));
BusMaterialbatchdemandplanBo bo = new BusMaterialbatchdemandplanBo();
bo.setIds(list1);
return R.ok(materialbatchdemandplanService.queryList(bo));
}
/**
* 获取工程量清单列表
*/
@SaCheckPermission("cailiaoshebei:purchaseDoc:add")
@GetMapping("/engineeringList")
public R<List<BusBillofquantities>> obtainTheList(Long projectId) {
BusBillofquantitiesVersions one = busBillofquantitiesVersionsService.getOne(Wrappers.<BusBillofquantitiesVersions>lambdaQuery()
.eq(BusBillofquantitiesVersions::getWorkOrderType, "3") //物资工程量清单
.eq(BusBillofquantitiesVersions::getProjectId, projectId)
.eq(BusBillofquantitiesVersions::getStatus, BusinessStatusEnum.FINISH.getStatus())
.last("limit 1")
);
if (one == null) {
throw new ServiceException("请先完成物资工程量清单");
bo.setIds(new ArrayList<>(collect.keySet()));
List<BusMaterialbatchdemandplanVo> busMaterialbatchdemandplanVos = materialbatchdemandplanService.queryList(bo);
for (BusMaterialbatchdemandplanVo busMaterialbatchdemandplanVo : busMaterialbatchdemandplanVos) {
busMaterialbatchdemandplanVo.setDemandQuantity(collect.get(busMaterialbatchdemandplanVo.getId()).longValue());
}
List<BusBillofquantities> list = busBillofquantitiesService.list(Wrappers.<BusBillofquantities>lambdaQuery()
.eq(BusBillofquantities::getVersions, one.getVersions())
);
return R.ok(list);
return R.ok(busMaterialbatchdemandplanVos);
}
//
// /**
// * 获取工程量清单列表
// */
// @SaCheckPermission("cailiaoshebei:purchaseDoc:add")
// @GetMapping("/engineeringList")
// public R<List<BusBillofquantities>> obtainTheList(Long projectId) {
// BusBillofquantitiesVersions one = busBillofquantitiesVersionsService.getOne(Wrappers.<BusBillofquantitiesVersions>lambdaQuery()
// .eq(BusBillofquantitiesVersions::getWorkOrderType, "3") //物资工程量清单
// .eq(BusBillofquantitiesVersions::getProjectId, projectId)
// .eq(BusBillofquantitiesVersions::getStatus, BusinessStatusEnum.FINISH.getStatus())
// .last("limit 1")
// );
// if (one == null) {
// throw new ServiceException("请先完成物资工程量清单");
// }
// List<BusBillofquantities> list = busBillofquantitiesService.list(Wrappers.<BusBillofquantities>lambdaQuery()
// .eq(BusBillofquantities::getVersions, one.getVersions())
// );
//
// return R.ok(list);
// }
}

View File

@ -107,7 +107,7 @@ public class BusPurchaseUserController extends BaseController {
/**
* 新增或修改物资采购人员
*/
@SaCheckPermission("cailiaoshebei:purchaseUser:add")
@SaCheckPermission("cailiaoshebei:purchaseUser:addOrUpdate")
@Log(title = "物资采购人员", businessType = BusinessType.INSERT)
@RepeatSubmit()
@PostMapping("/addOrUpdate")
@ -121,7 +121,7 @@ public class BusPurchaseUserController extends BaseController {
*
* @param projectId 项目id
*/
@SaCheckPermission("cailiaoshebei:purchaseUser:query")
@SaCheckPermission("cailiaoshebei:purchaseUser:byProject")
@GetMapping("/byProject/{projectId}")
public R<BusPurchaseUserVo> getInfoByProject(@NotNull(message = "主键不能为空")
@PathVariable Long projectId) {

View File

@ -1,32 +1,33 @@
package org.dromara.cailiaoshebei.controller;
import java.util.List;
import cn.hutool.core.bean.BeanUtil;
import lombok.RequiredArgsConstructor;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.constraints.*;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.hutool.core.bean.BeanUtil;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.constraints.NotNull;
import lombok.RequiredArgsConstructor;
import org.dromara.cailiaoshebei.domain.bo.BusTotalsupplyplanAuditBo;
import org.dromara.cailiaoshebei.domain.bo.BusTotalsupplyplanBo;
import org.dromara.cailiaoshebei.domain.bo.MasterDataReq;
import org.dromara.cailiaoshebei.domain.bo.TotalsupplyplanQueryListReq;
import org.dromara.cailiaoshebei.domain.dto.MasterDataReqDto;
import org.dromara.cailiaoshebei.domain.vo.BusTotalsupplyplanAuditVo;
import org.dromara.cailiaoshebei.domain.vo.BusTotalsupplyplanVo;
import org.dromara.cailiaoshebei.service.IBusTotalsupplyplanAuditService;
import org.springframework.web.bind.annotation.*;
import org.springframework.validation.annotation.Validated;
import org.dromara.common.idempotent.annotation.RepeatSubmit;
import org.dromara.common.log.annotation.Log;
import org.dromara.common.web.core.BaseController;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.cailiaoshebei.service.IBusTotalsupplyplanService;
import org.dromara.common.core.domain.R;
import org.dromara.common.core.validate.EditGroup;
import org.dromara.common.log.enums.BusinessType;
import org.dromara.common.excel.utils.ExcelUtil;
import org.dromara.cailiaoshebei.domain.vo.BusTotalsupplyplanVo;
import org.dromara.cailiaoshebei.domain.bo.BusTotalsupplyplanBo;
import org.dromara.cailiaoshebei.service.IBusTotalsupplyplanService;
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.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.util.List;
/**
* 物资-总供应计划
@ -82,6 +83,16 @@ public class BusTotalsupplyplanController extends BaseController {
ExcelUtil.exportExcel(list, "物资-总供应计划", BusTotalsupplyplanVo.class, response);
}
/**
* 导入物资-总供应计划数据
*/
@SaCheckPermission("design:totalsupplyplan:import")
@Log(title = "物资-总供应计划", businessType = BusinessType.IMPORT)
@PostMapping("/import")
public R<Void> importData(@RequestPart("file") MultipartFile file) {
return toAjax(busTotalsupplyplanService.importData(file));
}
/**
* 获取物资-总供应计划详细信息
*
@ -90,7 +101,7 @@ public class BusTotalsupplyplanController extends BaseController {
@SaCheckPermission("design:totalsupplyplan:query")
@GetMapping("/{id}")
public R<BusTotalsupplyplanVo> getInfo(@NotNull(message = "主键不能为空")
@PathVariable Long id) {
@PathVariable Long id) {
return R.ok(busTotalsupplyplanService.queryById(id));
}
@ -116,6 +127,17 @@ public class BusTotalsupplyplanController extends BaseController {
return toAjax(busTotalsupplyplanService.updateByBo(bo));
}
/**
* 批量修改物资-总供应计划
*/
@SaCheckPermission("design:totalsupplyplan:batchEdit")
@Log(title = "物资-总供应计划", businessType = BusinessType.UPDATE)
@RepeatSubmit()
@PutMapping("/batchEdit")
public R<Void> batchEdit(@Validated(EditGroup.class) @RequestBody List<BusTotalsupplyplanBo> boList) {
return toAjax(busTotalsupplyplanService.updateBatch(boList));
}
// /**
// * 删除物资-总供应计划
// *

View File

@ -6,6 +6,7 @@ import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serial;
import java.math.BigDecimal;
/**
* 物资-批次需求计划与采购单关联对象 bus_plan_doc_association
@ -37,6 +38,11 @@ public class BusPlanDocAssociation extends BaseEntity {
*/
private Long planId;
/**
* 需求数量
*/
private BigDecimal demandQuantity;
/**
* 采购联系单id
*/

View File

@ -1,14 +1,14 @@
package org.dromara.cailiaoshebei.domain;
import org.dromara.common.mybatis.core.domain.BaseEntity;
import com.baomidou.mybatisplus.annotation.*;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.time.LocalDate;
import java.util.Date;
import org.dromara.common.mybatis.core.domain.BaseEntity;
import java.io.Serial;
import java.time.LocalDate;
import java.util.Date;
/**
* 物资-总供应计划对象 bus_totalsupplyplan
@ -35,6 +35,16 @@ public class BusTotalsupplyplan extends BaseEntity {
*/
private Long projectId;
/**
* 批次ID
*/
private String sid;
/**
* 批次父ID
*/
private String pid;
/**
* 批次号
*/

View File

@ -114,4 +114,10 @@ public class BusMaterialbatchdemandplanBo extends BaseEntity {
* 主键集合
*/
private List<Long> ids;
/**
* 供应商id
*/
private Long supplierId;
}

View File

@ -9,6 +9,8 @@ import lombok.Data;
import lombok.EqualsAndHashCode;
import jakarta.validation.constraints.*;
import java.math.BigDecimal;
/**
* 物资-批次需求计划与采购单关联业务对象 bus_plan_doc_association
*
@ -41,5 +43,8 @@ public class BusPlanDocAssociationBo extends BaseEntity {
*/
private Long docId;
/**
* 需求数量
*/
private BigDecimal demandQuantity;
}

View File

@ -28,6 +28,11 @@ public class BusTotalsupplyplanBo extends BaseEntity {
@NotNull(message = "主键ID不能为空", groups = { EditGroup.class })
private Long id;
/**
* 项目Id
*/
private Long projectId;
/**
* 批次号
*/

View File

@ -0,0 +1,21 @@
package org.dromara.cailiaoshebei.domain.bo;
import java.io.Serializable;
/**
* @Author 铁憨憨
* @Date 2025/8/23 4:34
* @Version 1.0
*/
public class ObtainTheListReq implements Serializable {
/**
* 项目id
*/
private Long projectId;
/**
* 版本
*/
private String versions;
}

View File

@ -1,5 +1,6 @@
package org.dromara.cailiaoshebei.domain.vo;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.Date;
import com.fasterxml.jackson.annotation.JsonFormat;
@ -43,6 +44,11 @@ public class BusMaterialbatchdemandplanVo implements Serializable {
@ExcelProperty(value = "批次ID")
private String batchId;
/**
* 批次号
*/
private String batchNumber;
/**
* 基础信息ID
*/
@ -125,4 +131,9 @@ public class BusMaterialbatchdemandplanVo implements Serializable {
*/
private String qs;
/**
* 创建时间
*/
private BigDecimal remaining;
}

View File

@ -10,6 +10,7 @@ import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Date;
@ -52,5 +53,8 @@ public class BusPlanDocAssociationVo implements Serializable {
@ExcelProperty(value = "采购联系单id")
private Long docId;
/**
* 需求数量
*/
private BigDecimal demandQuantity;
}

View File

@ -1,15 +1,14 @@
package org.dromara.cailiaoshebei.domain.vo;
import java.util.Date;
import org.dromara.cailiaoshebei.domain.BusTotalsupplyplan;
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import com.alibaba.excel.annotation.ExcelProperty;
import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data;
import org.dromara.cailiaoshebei.domain.BusTotalsupplyplan;
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
/**
@ -43,48 +42,49 @@ public class BusTotalsupplyplanVo implements Serializable {
@ExcelProperty(value = "项目Id")
private Long projectId;
/**
* 批次ID
*/
private String sid;
/**
* 批次父ID
*/
private String pid;
/**
* 编制日期
*/
@ExcelProperty(value = "编制日期")
private Date compileDate;
/**
* 计划编号
*/
@ExcelProperty(value = "计划编号")
private String planNumber;
/**
* 编号
*/
@ExcelProperty(value = "编号")
private String num;
/**
* 名称
*/
@ExcelProperty(value = "名称")
@ExcelProperty(value = "工程或费用名称")
private String name;
/**
* 规格
*/
@ExcelProperty(value = "规格")
private String specification;
/**
* 材质
*/
@ExcelProperty(value = "材质")
private String texture;
/**
* 单位
*/
@ExcelProperty(value = "单位")
private String unit;
/**
* 规格
*/
@ExcelProperty(value = "规格型号")
private String specification;
/**
* 数量
*/
@ -97,34 +97,39 @@ public class BusTotalsupplyplanVo implements Serializable {
@ExcelProperty(value = "品牌")
private String brand;
/**
* 材质
*/
@ExcelProperty(value = "材质")
private String texture;
/**
* 质量标准
*/
@ExcelProperty(value = "质量标准")
private String qualityStandard;
/**
* 预计使用日期
*/
@ExcelProperty(value = "预计使用日期")
private Date dateService;
/**
* 交货地点
*/
@ExcelProperty(value = "交货地点")
private String deliveryPoints;
/**
* 使用部位
*/
@ExcelProperty(value = "使用部位")
private String partUsed;
/**
* 交货地点
*/
@ExcelProperty(value = "交货地点")
private String deliveryPoints;
/**
* 预计使用日期
*/
@ExcelProperty(value = "预计使用日期")
private Date dateService;
/**
* 审核状态
*/
@ExcelProperty(value = "审核状态")
private String status;
/**

View File

@ -12,6 +12,7 @@ import org.dromara.common.mybatis.core.page.PageQuery;
import com.baomidou.mybatisplus.extension.service.IService;
import org.springframework.web.bind.annotation.RequestBody;
import java.math.BigDecimal;
import java.util.Collection;
import java.util.List;
@ -82,4 +83,9 @@ public interface IBusMrpBaseService extends IService<BusMrpBase>{
* 导入物资需求批次计划
*/
Boolean importData(BusMrpExportDto dto);
/**
* 获取物资已有数量
*/
BigDecimal remaining(Long suppliespriceId,Long mrpBaseId);
}

View File

@ -1,14 +1,15 @@
package org.dromara.cailiaoshebei.service;
import com.baomidou.mybatisplus.extension.service.IService;
import org.dromara.cailiaoshebei.domain.BusTotalsupplyplan;
import org.dromara.cailiaoshebei.domain.bo.BusTotalsupplyplanBo;
import org.dromara.cailiaoshebei.domain.bo.MasterDataReq;
import org.dromara.cailiaoshebei.domain.dto.MasterDataReqDto;
import org.dromara.cailiaoshebei.domain.vo.BusTotalsupplyplanVo;
import org.dromara.cailiaoshebei.domain.bo.BusTotalsupplyplanBo;
import org.dromara.cailiaoshebei.domain.BusTotalsupplyplan;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.springframework.web.multipart.MultipartFile;
import com.baomidou.mybatisplus.extension.service.IService;
import java.util.Collection;
import java.util.List;
@ -18,7 +19,7 @@ import java.util.List;
* @author Lion Li
* @date 2025-08-13
*/
public interface IBusTotalsupplyplanService extends IService<BusTotalsupplyplan>{
public interface IBusTotalsupplyplanService extends IService<BusTotalsupplyplan> {
/**
* 查询物资-总供应计划
@ -78,4 +79,20 @@ public interface IBusTotalsupplyplanService extends IService<BusTotalsupplyplan>
* @return 主数据列表
*/
MasterDataReqDto masterData(MasterDataReq bo);
/**
* 批量更新
*
* @param boList 批量更新
* @return 是否更新成功
*/
Boolean updateBatch(List<BusTotalsupplyplanBo> boList);
/**
* 导入数据
*
* @param file 导入数据
* @return 是否导入成功
*/
Boolean importData(MultipartFile file);
}

View File

@ -22,6 +22,11 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.RequiredArgsConstructor;
import org.dromara.tender.domain.bo.BusBiddingPlanBo;
import org.dromara.tender.domain.vo.BusBiddingPlanVo;
import org.dromara.tender.domain.vo.BusBillofquantitiesLimitListVo;
import org.dromara.tender.service.IBusBiddingPlanService;
import org.dromara.tender.service.ITenderSupplierInputService;
import org.springframework.context.annotation.Lazy;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
@ -34,9 +39,7 @@ import org.dromara.cailiaoshebei.mapper.BusMaterialbatchdemandplanMapper;
import org.dromara.cailiaoshebei.service.IBusMaterialbatchdemandplanService;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
import java.util.Map;
import java.util.Collection;
import java.util.*;
/**
* 物资-批次需求计划Service业务层处理
@ -60,6 +63,10 @@ public class BusMaterialbatchdemandplanServiceImpl extends ServiceImpl<BusMateri
@Autowired
private IBusMaterialsorderService busMaterialsorderService;
@Lazy
@Autowired
private IBusBiddingPlanService busBiddingPlanService;
/**
* 查询物资-批次需求计划
*
@ -82,6 +89,25 @@ public class BusMaterialbatchdemandplanServiceImpl extends ServiceImpl<BusMateri
public TableDataInfo<BusMaterialbatchdemandplanVo> queryPageList(BusMaterialbatchdemandplanBo bo, PageQuery pageQuery) {
LambdaQueryWrapper<BusMaterialbatchdemandplan> lqw = buildQueryWrapper(bo);
Page<BusMaterialbatchdemandplanVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
if (bo.getSupplierId()!=null){
BusBiddingPlanBo bo1 = new BusBiddingPlanBo();
bo1.setProjectId(bo.getProjectId());
bo1.setType("2");
bo1.setWinningBidderId(bo.getSupplierId());
List<BusBillofquantitiesLimitListVo> busBiddingPlanVos = busBiddingPlanService.getBillofquantitiesLimitListVo(bo1);
if (busBiddingPlanVos == null || busBiddingPlanVos.isEmpty()) {
throw new ServiceException("该供应商暂无材料");
}
Set<String> hashSet = new HashSet<>();
busBiddingPlanVos.stream().forEach(vo -> {
hashSet.add(vo.getName()+"+"+vo.getSpecification());
});
List<BusMaterialbatchdemandplanVo> list = result.getRecords().stream().filter(vo -> {
String key = vo.getName() + "+" + vo.getSpecification(); // 拼接字符串(需与 Set 中格式一致)
return hashSet.contains(key); // 仅保留 Set 中存在的数据
}).toList();
result.setRecords(list);
}
return TableDataInfo.build(result);
}

View File

@ -63,6 +63,7 @@ public class BusMrpBaseServiceImpl extends ServiceImpl<BusMrpBaseMapper, BusMrpB
private final IBusBillofquantitiesService busBillofquantitiesService;
/**
* 查询物资-批次需求计划基础信息
*
@ -77,6 +78,10 @@ public class BusMrpBaseServiceImpl extends ServiceImpl<BusMrpBaseMapper, BusMrpB
BusMaterialbatchdemandplanBo planBo = new BusMaterialbatchdemandplanBo();
planBo.setMrpBaseId(id);
List<BusMaterialbatchdemandplanVo> voList = planservice.queryList(planBo);
for (BusMaterialbatchdemandplanVo vo : voList) {
BigDecimal remaining = remaining(vo.getSuppliespriceId(), id);
vo.setRemaining(remaining);
}
busMrpVo.setMrpBaseBo(busMrpBaseVo);
busMrpVo.setPlanList(voList);
@ -264,6 +269,26 @@ public class BusMrpBaseServiceImpl extends ServiceImpl<BusMrpBaseMapper, BusMrpB
return true;
}
@Override
public BigDecimal remaining(Long suppliespriceId,Long mrpBaseId) {
BusBillofquantities byId = busBillofquantitiesService.getById(suppliespriceId);
// 获取数据库中已有的数量
List<BusMaterialbatchdemandplan> existingList = planservice.list(
Wrappers.lambdaQuery(BusMaterialbatchdemandplan.class)
.eq(BusMaterialbatchdemandplan::getSuppliespriceId, suppliespriceId)
.ne(mrpBaseId!=null,BusMaterialbatchdemandplan::getMrpBaseId, mrpBaseId)// 排除当前批次
);
BigDecimal reduce = BigDecimal.ZERO;
if(CollectionUtil.isNotEmpty(existingList)){
reduce = existingList.stream()
.map(BusMaterialbatchdemandplan::getDemandQuantity)
.reduce(BigDecimal.ZERO, BigDecimal::add);
}
return byId.getQuantity().subtract(reduce);
}
/**
* 总体流程监听(例如: 草稿,撤销,退回,作废,终止,已完成,单任务完成等)
* 正常使用只需#processEvent.flowCode=='leave1'

View File

@ -18,6 +18,7 @@ import org.docx4j.openpackaging.exceptions.Docx4JException;
import org.docx4j.openpackaging.packages.WordprocessingMLPackage;
import org.dromara.cailiaoshebei.controller.constant;
import org.dromara.cailiaoshebei.domain.BusMaterialbatchdemandplan;
import org.dromara.cailiaoshebei.domain.BusMrpBase;
import org.dromara.cailiaoshebei.domain.BusPlanDocAssociation;
import org.dromara.cailiaoshebei.domain.BusPurchaseDoc;
import org.dromara.cailiaoshebei.domain.bo.BusPlanDocAssociationBo;
@ -56,8 +57,11 @@ import org.springframework.transaction.annotation.Transactional;
import java.io.*;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.net.FileNameMap;
import java.net.URLConnection;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
@ -66,6 +70,7 @@ import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;
/**
* 物资-采购联系单Service业务层处理
@ -120,7 +125,10 @@ public class BusPurchaseDocServiceImpl extends ServiceImpl<BusPurchaseDocMapper,
LambdaQueryWrapper<BusPurchaseDoc> lqw = buildQueryWrapper(bo);
Page<BusPurchaseDocVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
result.getRecords().forEach(v -> {
v.setPlanCode(mrpBaseService.getById(v.getMrpBaseId()).getPlanCode());
BusMrpBase byId = mrpBaseService.getById(v.getMrpBaseId());
if (byId != null) {
v.setPlanCode(byId.getPlanCode());
}
});
return TableDataInfo.build(result);
}
@ -178,6 +186,7 @@ public class BusPurchaseDocServiceImpl extends ServiceImpl<BusPurchaseDocMapper,
BusPurchaseDoc add = MapstructUtils.convert(bo, BusPurchaseDoc.class);
validEntityBeforeSave(add);
validNum(bo.getAssociationList());
boolean flag = baseMapper.insert(add) > 0;
if (flag) {
bo.setId(add.getId());
@ -193,6 +202,30 @@ public class BusPurchaseDocServiceImpl extends ServiceImpl<BusPurchaseDocMapper,
return flag;
}
public void validNum(List<BusPlanDocAssociationBo> associationList){
for (BusPlanDocAssociationBo association : associationList) {
BusMaterialbatchdemandplan byId = materialbatchdemandplanService.getById(association.getPlanId());
List<BusPlanDocAssociation> list = planDocAssociationService.list(Wrappers.lambdaQuery(BusPlanDocAssociation.class)
.eq(BusPlanDocAssociation::getPlanId, association.getPlanId()));
BigDecimal total = list.stream()
.map(BusPlanDocAssociation::getDemandQuantity)
.filter(Objects::nonNull)
.reduce(BigDecimal.ZERO, BigDecimal::add);
if(total.add(association.getDemandQuantity()).compareTo(byId.getDemandQuantity()) > 0){
throw new ServiceException("材料:" + byId.getName() + "已超出计划单的物料批次需求计划数量");
}
}
}
/**
* 修改物资-采购联系单
*
@ -204,11 +237,10 @@ public class BusPurchaseDocServiceImpl extends ServiceImpl<BusPurchaseDocMapper,
BusPurchaseDoc update = MapstructUtils.convert(bo, BusPurchaseDoc.class);
validEntityBeforeSave(update);
planDocAssociationService.remove(Wrappers.<BusPlanDocAssociation>lambdaQuery()
.eq(BusPlanDocAssociation::getProjectId, update.getProjectId())
.eq(BusPlanDocAssociation::getDocId, update.getId()));
if (CollectionUtil.isNotEmpty(bo.getAssociationList())) {
planDocAssociationService.remove(Wrappers.<BusPlanDocAssociation>lambdaQuery()
.eq(BusPlanDocAssociation::getProjectId, update.getProjectId())
.eq(BusPlanDocAssociation::getDocId, update.getId()));
List<BusPlanDocAssociation> convert = MapstructUtils.convert(bo.getAssociationList(), BusPlanDocAssociation.class);
convert.forEach(item -> {
item.setProjectId(update.getProjectId());
@ -315,8 +347,9 @@ public class BusPurchaseDocServiceImpl extends ServiceImpl<BusPurchaseDocMapper,
.eq(BusPlanDocAssociation::getDocId, purchaseDoc.getId())
.list();
if (CollUtil.isNotEmpty(planDocAssociationList)) {
List<Long> planIds = planDocAssociationList.stream().map(BusPlanDocAssociation::getPlanId).toList();
items = materialbatchdemandplanService.listByIds(planIds);
Map<Long, BigDecimal> map = planDocAssociationList.stream().collect(Collectors.toMap(BusPlanDocAssociation::getPlanId, BusPlanDocAssociation::getDemandQuantity));
items = materialbatchdemandplanService.listByIds(map.keySet());
items.forEach(item -> item.setDemandQuantity(map.get(item.getId())));
}
BusPurchaseDocWordDto data = this.getReplacementDto(purchaseDoc, items);
Path newTargetDir = Paths.get(System.getProperty("user.dir"), constant.getBusPurchaseDocFileUrl(purchaseDoc, now));
@ -346,10 +379,12 @@ public class BusPurchaseDocServiceImpl extends ServiceImpl<BusPurchaseDocMapper,
String filePath = constant.getBusPurchaseDocFileUrl(purchaseDoc, now) + "/";
String fileName = constant.getBusPurchaseDocFileName(purchaseDoc);
File file = new File(filePath + fileName);
log.info("{}", file.exists());
try {
// Word → PDF
ByteArrayOutputStream pdfOut = new ByteArrayOutputStream();
WordprocessingMLPackage wordML = WordprocessingMLPackage.load(file);
log.info(String.valueOf(wordML));
Docx4J.toPDF(wordML, pdfOut);
FileNameMap fileNameMap = URLConnection.getFileNameMap();
String createDate = DateUtils.formatDate(purchaseDoc.getCreateTime());
@ -374,6 +409,9 @@ public class BusPurchaseDocServiceImpl extends ServiceImpl<BusPurchaseDocMapper,
throw new ServiceException("获取物资采购联系单详情失败,错误信息: " + e.getMessage());
} catch (IOException e) {
throw new ServiceException("保存PDF文件失败错误信息: " + e.getMessage());
} catch (Exception e) {
log.error("保存PDF文件失败错误信息: {}", e.getMessage());
throw new ServiceException("保存PDF文件失败系统异常");
}
}
}
@ -390,10 +428,61 @@ public class BusPurchaseDocServiceImpl extends ServiceImpl<BusPurchaseDocMapper,
if (purchaseDoc == null) {
throw new ServiceException("物料领料单不存在");
}
// 创建Word
this.createWord(purchaseDoc);
// 清理OSS上的旧文件
// Long pdfFileId = purchaseDoc.getPdfFileId();
// if (pdfFileId != null) {
// ossService.deleteWithValidByIds(List.of(pdfFileId), true);
// }
// 准备数据
List<BusMaterialbatchdemandplan> items = new ArrayList<>();
List<BusPlanDocAssociation> planDocAssociationList = planDocAssociationService.lambdaQuery()
.eq(BusPlanDocAssociation::getDocId, purchaseDoc.getId())
.list();
if (CollUtil.isNotEmpty(planDocAssociationList)) {
List<Long> planIds = planDocAssociationList.stream()
.map(BusPlanDocAssociation::getPlanId)
.toList();
items = materialbatchdemandplanService.listByIds(planIds);
}
BusPurchaseDocWordDto data = this.getReplacementDto(purchaseDoc, items);
// 生成文件并直接写入响应流
try (InputStream is = getClass().getClassLoader().getResourceAsStream(constant.PURCHASE_DOC_TEMPLATE_PATH)) {
if (is == null) {
throw new ServiceException("模板文件不存在");
}
// 配置模板渲染策略
LoopRowTableRenderPolicy hackLoopTableRenderPolicy = new LoopRowTableRenderPolicy();
Configure config = Configure.builder()
.bind("items", hackLoopTableRenderPolicy)
.build();
// 编译模板并渲染数据
XWPFTemplate template = XWPFTemplate.compile(is, config);
template.render(data);
// 设置响应头信息
String fileName = constant.getBusPurchaseDocFileName(purchaseDoc);
response.setContentType("application/vnd.openxmlformats-officedocument.wordprocessingml.document");
response.setHeader("Content-Disposition", "attachment;filename=" +
URLEncoder.encode(fileName, StandardCharsets.UTF_8.name()));
response.setCharacterEncoding("UTF-8");
// 直接通过模板的write方法写入响应流
try (OutputStream os = response.getOutputStream()) {
template.write(os);
os.flush();
}
template.close();
} catch (IOException e) {
log.error("生成Word文件失败", e);
throw new OssException("生成Word文件失败错误信息: " + e.getMessage());
}
}
/**
* 根据主键查询详情PDF地址
*
@ -443,6 +532,7 @@ public class BusPurchaseDocServiceImpl extends ServiceImpl<BusPurchaseDocMapper,
BeanUtils.copyProperties(item, itemDto);
itemDto.setNum(i);
itemDto.setChildName(item.getName());
itemDto.setDemandQuantity(item.getDemandQuantity() != null ? item.getDemandQuantity().longValue() : null);
dtoItems.add(itemDto);
}
dto.setItems(dtoItems);

View File

@ -1,34 +1,40 @@
package org.dromara.cailiaoshebei.service.impl;
import cn.hutool.core.bean.BeanUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelReader;
import com.alibaba.excel.read.metadata.ReadSheet;
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.cailiaoshebei.domain.BusTotalsupplyplan;
import org.dromara.cailiaoshebei.domain.BusTotalsupplyplanAudit;
import org.dromara.cailiaoshebei.domain.bo.BusTotalsupplyplanBo;
import org.dromara.cailiaoshebei.domain.bo.MasterDataReq;
import org.dromara.cailiaoshebei.domain.dto.MasterDataReqDto;
import org.dromara.cailiaoshebei.domain.vo.BusTotalsupplyplanVo;
import org.dromara.cailiaoshebei.mapper.BusTotalsupplyplanMapper;
import org.dromara.cailiaoshebei.service.IBusTotalsupplyplanAuditService;
import org.dromara.cailiaoshebei.service.IBusTotalsupplyplanService;
import org.dromara.common.core.constant.HttpStatus;
import org.dromara.common.core.domain.event.ProcessDeleteEvent;
import org.dromara.common.core.domain.event.ProcessEvent;
import org.dromara.common.core.domain.event.ProcessTaskEvent;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.utils.MapstructUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.excel.core.DefaultExcelListener;
import org.dromara.common.mybatis.core.page.PageQuery;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.RequiredArgsConstructor;
import org.dromara.cailiaoshebei.domain.BusTotalsupplyplanAudit;
import org.dromara.cailiaoshebei.service.IBusTotalsupplyplanAuditService;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Service;
import org.dromara.cailiaoshebei.domain.bo.BusTotalsupplyplanBo;
import org.dromara.cailiaoshebei.domain.vo.BusTotalsupplyplanVo;
import org.dromara.cailiaoshebei.domain.BusTotalsupplyplan;
import org.dromara.cailiaoshebei.mapper.BusTotalsupplyplanMapper;
import org.dromara.cailiaoshebei.service.IBusTotalsupplyplanService;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import java.util.List;
import java.util.Map;
import java.util.Collection;
import java.util.*;
/**
* 物资-总供应计划Service业务层处理
@ -39,14 +45,11 @@ import java.util.Collection;
@RequiredArgsConstructor
@Service
@Slf4j
public class BusTotalsupplyplanServiceImpl extends ServiceImpl<BusTotalsupplyplanMapper, BusTotalsupplyplan> implements IBusTotalsupplyplanService {
private final BusTotalsupplyplanMapper baseMapper;
public class BusTotalsupplyplanServiceImpl extends ServiceImpl<BusTotalsupplyplanMapper, BusTotalsupplyplan>
implements IBusTotalsupplyplanService {
private final IBusTotalsupplyplanAuditService busTotalsupplyplanAuditService;
/**
* 查询物资-总供应计划
*
@ -54,7 +57,7 @@ public class BusTotalsupplyplanServiceImpl extends ServiceImpl<BusTotalsupplypla
* @return 物资-总供应计划
*/
@Override
public BusTotalsupplyplanVo queryById(Long id){
public BusTotalsupplyplanVo queryById(Long id) {
return baseMapper.selectVoById(id);
}
@ -80,20 +83,35 @@ public class BusTotalsupplyplanServiceImpl extends ServiceImpl<BusTotalsupplypla
*/
@Override
public List<BusTotalsupplyplanVo> queryList(BusTotalsupplyplanBo bo) {
// 1. 先构建查询条件MyBatis Plus 的 LambdaQueryWrapper
LambdaQueryWrapper<BusTotalsupplyplan> lqw = buildQueryWrapper(bo);
// 2. 查出所有 pid有子节点的 sid
List<String> parentIds = baseMapper.selectList(
Wrappers.<BusTotalsupplyplan>lambdaQuery()
.select(BusTotalsupplyplan::getPid)
.isNotNull(BusTotalsupplyplan::getPid)
).stream()
.map(BusTotalsupplyplan::getPid)
.filter(Objects::nonNull)
.distinct()
.toList();
// 3. 在查询条件的基础上,加上 notIn 过滤(排除父节点,只取叶子节点)
lqw.notIn(!parentIds.isEmpty(), BusTotalsupplyplan::getSid, parentIds);
// 4. 查询叶子节点
return baseMapper.selectVoList(lqw);
}
private LambdaQueryWrapper<BusTotalsupplyplan> buildQueryWrapper(BusTotalsupplyplanBo bo) {
Map<String, Object> params = bo.getParams();
LambdaQueryWrapper<BusTotalsupplyplan> lqw = Wrappers.lambdaQuery();
lqw.orderByDesc(BusTotalsupplyplan::getId);
lqw.eq(bo.getProjectId() != null ,BusTotalsupplyplan::getProjectId,bo.getProjectId());
lqw.eq(StringUtils.isNotBlank(bo.getTexture()), BusTotalsupplyplan::getTexture, bo.getTexture());
lqw.eq(StringUtils.isNotBlank(bo.getBrand()), BusTotalsupplyplan::getBrand, bo.getBrand());
lqw.eq(StringUtils.isNotBlank(bo.getQualityStandard()), BusTotalsupplyplan::getQualityStandard, bo.getQualityStandard());
lqw.eq(bo.getDateService() != null, BusTotalsupplyplan::getDateService, bo.getDateService());
lqw.eq(StringUtils.isNotBlank(bo.getDeliveryPoints()), BusTotalsupplyplan::getDeliveryPoints, bo.getDeliveryPoints());
lqw.eq(StringUtils.isNotBlank(bo.getPartUsed()), BusTotalsupplyplan::getPartUsed, bo.getPartUsed());
lqw.orderByAsc(BusTotalsupplyplan::getId);
return lqw;
}
@ -130,7 +148,7 @@ public class BusTotalsupplyplanServiceImpl extends ServiceImpl<BusTotalsupplypla
/**
* 保存前的数据校验
*/
private void validEntityBeforeSave(BusTotalsupplyplan entity){
private void validEntityBeforeSave(BusTotalsupplyplan entity) {
//TODO 做一些数据校验,如唯一约束
}
@ -143,7 +161,7 @@ public class BusTotalsupplyplanServiceImpl extends ServiceImpl<BusTotalsupplypla
*/
@Override
public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
if(isValid){
if (isValid) {
//TODO 做一些业务上的校验,判断是否需要校验
}
return baseMapper.deleteByIds(ids) > 0;
@ -162,6 +180,78 @@ public class BusTotalsupplyplanServiceImpl extends ServiceImpl<BusTotalsupplypla
return masterDataReqDto;
}
/**
* 批量更新
*
* @param boList 批量更新
* @return 是否更新成功
*/
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean updateBatch(List<BusTotalsupplyplanBo> boList) {
List<BusTotalsupplyplan> list = BeanUtil.copyToList(boList, BusTotalsupplyplan.class);
for (BusTotalsupplyplan busTotalsupplyplan : list) {
validEntityBeforeSave(busTotalsupplyplan);
}
return this.updateBatchById(list);
}
/**
* 导入数据
*
* @param file 导入数据
* @return 是否导入成功
*/
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean importData(MultipartFile file) {
// 检查文件是否为空
if (file == null || file.isEmpty()) {
throw new ServiceException("上传文件不能为空", HttpStatus.BAD_REQUEST);
}
// 检查文件大小
if (file.getSize() == 0) {
throw new ServiceException("上传文件不能为空文件", HttpStatus.BAD_REQUEST);
}
// 检查文件名
if (file.getOriginalFilename() == null || file.getOriginalFilename().isEmpty()) {
throw new ServiceException("文件名不能为空", HttpStatus.BAD_REQUEST);
}
try {
// 使用EasyExcel读取所有sheet
List<BusTotalsupplyplanVo> allData = new ArrayList<>();
// 创建Excel读取监听器
DefaultExcelListener<BusTotalsupplyplanVo> listener = new DefaultExcelListener<>(false);
// 读取Excel文件
ExcelReader excelReader = EasyExcel.read(file.getInputStream(), BusTotalsupplyplanVo.class, listener).build();
// 获取所有sheet
List<ReadSheet> sheetList = excelReader.excelExecutor().sheetList();
// 遍历所有sheet
for (ReadSheet readSheet : sheetList) {
// 为每个sheet创建新的监听器实例
DefaultExcelListener<BusTotalsupplyplanVo> sheetListener = new DefaultExcelListener<>(false);
// 读取当前sheet数据
EasyExcel.read(file.getInputStream(), BusTotalsupplyplanVo.class, sheetListener)
.sheet(readSheet.getSheetNo())
.doRead();
// 将当前sheet的数据添加到总数据中
allData.addAll(sheetListener.getExcelResult().getList());
}
// 关闭读取器
excelReader.finish();
if (allData.isEmpty()) {
throw new ServiceException("未读取到有效数据", HttpStatus.BAD_REQUEST);
}
// 处理导入的数据
List<BusTotalsupplyplan> list = BeanUtil.copyToList(allData, BusTotalsupplyplan.class);
// 批量更新
return this.updateBatchById(list);
} catch (Exception e) {
log.error("物资供货总计划导入Excel文件失败", e);
throw new ServiceException("导入失败: " + e.getMessage(), HttpStatus.ERROR);
}
}
/**
* 总体流程监听(例如: 草稿,撤销,退回,作废,终止,已完成,单任务完成等)
* 正常使用只需#processEvent.flowCode=='leave1'
@ -169,7 +259,7 @@ public class BusTotalsupplyplanServiceImpl extends ServiceImpl<BusTotalsupplypla
*
* @param processEvent 参数
*/
@org.springframework.context.event.EventListener(condition = "#processEvent.flowCode.endsWith('totalsupplyplan')")
@EventListener(condition = "#processEvent.flowCode.endsWith('totalsupplyplan')")
public void processPlansHandler(ProcessEvent processEvent) {
log.info("物资总供应计划审核任务执行了{}", processEvent.toString());
String id = processEvent.getBusinessId();
@ -190,7 +280,7 @@ public class BusTotalsupplyplanServiceImpl extends ServiceImpl<BusTotalsupplypla
*
* @param processTaskEvent 参数
*/
@org.springframework.context.event.EventListener(condition = "#processTaskEvent.flowCode.endsWith('totalsupplyplan')")
@EventListener(condition = "#processTaskEvent.flowCode.endsWith('totalsupplyplan')")
public void processTaskPlansHandler(ProcessTaskEvent processTaskEvent) {
log.info("物资总供应计划审核任务创建了{}", processTaskEvent.toString());
}

View File

@ -18,9 +18,11 @@ public class BigDecimalUtil {
*/
public static BigDecimal toPercentage(BigDecimal dividend, BigDecimal divisor) {
if (dividend == null || divisor == null || divisor.compareTo(BigDecimal.ZERO) == 0) {
return BigDecimal.ZERO;
return BigDecimal.valueOf(0.00);
}
return dividend.divide(divisor, 2, RoundingMode.HALF_UP).multiply(new BigDecimal("100"));
return dividend
.multiply(new BigDecimal("100"))
.divide(divisor, 2, RoundingMode.HALF_UP);
}
}

View File

@ -46,6 +46,9 @@ public class IdCardEncryptorUtil {
* @return 解密后的身份证号码
*/
public String decrypt(String encrypted) {
if (encrypted == null) {
return null;
}
return aes.decryptStr(encrypted);
}
}

View File

@ -6,33 +6,27 @@ import com.google.zxing.BarcodeFormat;
import com.google.zxing.MultiFormatWriter;
import com.google.zxing.client.j2se.MatrixToImageWriter;
import com.google.zxing.common.BitMatrix;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.Image;
import com.itextpdf.text.pdf.PdfContentByte;
import com.itextpdf.text.pdf.PdfReader;
import com.itextpdf.text.pdf.PdfStamper;
import org.dromara.system.domain.vo.SysOssVo;
import com.itextpdf.io.image.ImageData;
import com.itextpdf.io.image.ImageDataFactory;
import com.itextpdf.kernel.geom.Rectangle;
import com.itextpdf.kernel.pdf.*;
import com.itextpdf.kernel.pdf.canvas.PdfCanvas;
import com.itextpdf.layout.Canvas;
import com.itextpdf.layout.element.Image;
import java.awt.image.BufferedImage;
import java.io.*;
import java.net.FileNameMap;
import java.net.URLConnection;
import java.util.concurrent.CompletableFuture;
import java.net.HttpURLConnection;
import java.net.URL;
/**
* @author lilemy
* @date 2025/7/7 9:56
* @date 2025/7/7
*/
public class PdfBoxQrCodeGenerator {
/**
* 生成二维码图片并返回路径
*
* @param text 二维码文本
* @param width 二维码宽度
* @param height 二维码高度
* @param outputPath 二维码图片保存路径
* @return 二维码图片保存路径
*/
public static String generateQRCodeImage(String text, int width, int height, String outputPath) throws Exception {
BitMatrix bitMatrix = new MultiFormatWriter().encode(text, BarcodeFormat.QR_CODE, width, height);
@ -42,11 +36,6 @@ public class PdfBoxQrCodeGenerator {
/**
* 获取二维码图片字节数组
*
* @param text 二维码文本
* @param width 二维码宽度
* @param height 二维码高度
* @return 二维码图片字节数组
*/
public static byte[] generateQRCodeBytes(String text, int width, int height) {
BufferedImage image = QrCodeUtil.generate(text, width, height);
@ -56,79 +45,502 @@ public class PdfBoxQrCodeGenerator {
}
/**
* 获取二维码图片字节数组
*
* @param text 二维码文本
* @return 二维码图片字节数组
* 获取二维码图片字节数组默认200x200
*/
public static byte[] generateQRCodeBytes(String text) {
return generateQRCodeBytes(text, 200, 200);
}
/**
* 在PDF指定位置添加二维码
*
* @param srcPdf 原PDF文件路径
* @param destPdf 新PDF文件路径
* @param qrImagePath 二维码图片路径
* @param pageNum 页码
* @param x 坐标
* @param y 坐标
* 在PDF指定位置添加二维码(基于图片路径)
*/
public static void addQRCodeToPDF(String srcPdf, String destPdf, String qrImagePath, int pageNum, float x, float y) throws IOException, DocumentException {
PdfReader reader = new PdfReader(srcPdf);
PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(destPdf));
PdfContentByte content = stamper.getOverContent(pageNum);
Image image = Image.getInstance(qrImagePath);
image.setAbsolutePosition(x, y); // 坐标:左下角为原点
image.scaleAbsolute(100, 100); // 设置二维码大小
content.addImage(image);
stamper.close();
reader.close();
public static void addQRCodeToPDF(String srcPdf, String destPdf, String qrImagePath, int pageNum, float x, float y)
throws IOException {
byte[] qrBytes = java.nio.file.Files.readAllBytes(new File(qrImagePath).toPath());
addQRCodeToPDF(srcPdf, destPdf, qrBytes, pageNum, x, y);
}
/**
* 在PDF指定位置添加二维码
*
* @param srcPdf 原PDF文件路径
* @param destPdf 新PDF文件路径
* @param qrCodeBytes 二维码图片字节数组
* @param pageNum 页码
* @param x 坐标
* @param y 坐标
* 在PDF指定位置添加二维码(基于字节数组)
*/
public static void addQRCodeToPDF(String srcPdf, String destPdf, byte[] qrCodeBytes, int pageNum, float x, float y) throws IOException, DocumentException {
public static void addQRCodeToPDF(String srcPdf, String destPdf, byte[] qrCodeBytes, int pageNum, float x, float y)
throws IOException {
PdfReader reader = new PdfReader(srcPdf);
PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(destPdf));
PdfContentByte content = stamper.getOverContent(pageNum);
PdfWriter writer = new PdfWriter(destPdf);
PdfDocument pdfDoc = new PdfDocument(reader, writer);
Image image = Image.getInstance(qrCodeBytes);
image.setAbsolutePosition(x, y); // 坐标:左下角为原点
image.scaleAbsolute(100, 100); // 设置二维码大小
PdfPage page = pdfDoc.getPage(pageNum);
content.addImage(image);
stamper.close();
reader.close();
PdfCanvas pdfCanvas = new PdfCanvas(page.newContentStreamAfter(), page.getResources(), pdfDoc);
try (Canvas canvas = new Canvas(pdfCanvas, page.getPageSize())) {
ImageData imageData = ImageDataFactory.create(qrCodeBytes);
Image image = new Image(imageData)
.scaleAbsolute(100, 100)
.setFixedPosition(x, y);
canvas.add(image);
}
pdfDoc.close();
}
/**
* 在PDF指定位置添加二维码并返回数据流
*
* @param srcPdf 原PDF文件路径
* @param qrCodeBytes 二维码图片字节数组
* @param pageNum 页码
* @param x 坐标
* @param y 坐标
* @return 插入二维码后的PDF文件流
*/
// public static ByteArrayOutputStream addQRCodeToPDF(String srcPdf, byte[] qrCodeBytes, int pageNum, float x, float y) throws IOException, DocumentException {
public static ByteArrayOutputStream addQRCodeToPDF(String srcPdf, byte[] qrCodeBytes, int pageNum, float x, float y)
throws IOException {
PdfReader reader = new PdfReader(srcPdf);
ByteArrayOutputStream pdfOut = new ByteArrayOutputStream();
PdfWriter writer = new PdfWriter(pdfOut);
PdfDocument pdfDoc = new PdfDocument(reader, writer);
PdfPage page = pdfDoc.getPage(pageNum);
PdfCanvas pdfCanvas = new PdfCanvas(page.newContentStreamAfter(), page.getResources(), pdfDoc);
try (Canvas canvas = new Canvas(pdfCanvas, page.getPageSize())) {
ImageData imageData = ImageDataFactory.create(qrCodeBytes);
Image image = new Image(imageData)
.scaleAbsolute(100, 100)
.setFixedPosition(x, y);
canvas.add(image);
}
pdfDoc.close();
return pdfOut;
}
/**
* 在PDF每一页的指定位置添加二维码并返回数据流根据页面方向自动调整位置
*/
// public static ByteArrayOutputStream addQRCodeToPDFOnAllPages(String srcPdf, byte[] qrCodeBytes, boolean isChangeFile)
// throws IOException {
//
// PdfReader reader;
// if (srcPdf.startsWith("http://") || srcPdf.startsWith("https://")) {
// // 网络地址 PDF
// URL url = new URL(srcPdf);
// HttpURLConnection connection = (HttpURLConnection) url.openConnection();
// connection.setConnectTimeout(10000);
// connection.setReadTimeout(30000);
// connection.setRequestMethod("GET");
//
// int responseCode = connection.getResponseCode();
// if (responseCode != HttpURLConnection.HTTP_OK) {
// throw new IOException("无法获取 PDF 文件HTTP响应码: " + responseCode);
// }
// InputStream inputStream = connection.getInputStream();
// reader = new PdfReader(inputStream);
// } else {
// // 本地文件 PDF
// File srcFile = new File(srcPdf);
// if (!srcFile.exists()) {
// throw new IOException("源PDF文件不存在: " + srcPdf);
// }
// reader = new PdfReader(srcPdf);
// }
//
// PdfReader reader = new PdfReader(srcPdf);
// ByteArrayOutputStream pdfOut = new ByteArrayOutputStream();
// PdfStamper stamper = new PdfStamper(reader, pdfOut);
// PdfWriter writer = new PdfWriter(pdfOut);
// PdfDocument pdfDoc = new PdfDocument(reader, writer);
//
// int numberOfPages = pdfDoc.getNumberOfPages();
//
// for (int pageNum = 1; pageNum <= numberOfPages; pageNum++) {
// PdfPage page = pdfDoc.getPage(pageNum);
// Rectangle pageSize = page.getPageSize();
//
// float pageWidth = pageSize.getWidth();
// float pageHeight = pageSize.getHeight();
//
// float qrX, qrY;
// float newWidth, newHeight;
//
// if (isChangeFile && pageNum == 1) {
// qrX = pageWidth - 143;
// qrY = pageHeight - 213;
// newWidth = 67;
// newHeight = 80;
// } else {
// if (pageWidth > pageHeight) {
// // 横版
// qrX = pageWidth - 90;
// qrY = 24;
// newWidth = 67;
// newHeight = 79;
// } else {
// // 竖版
// qrX = 226;
// qrY = pageHeight - 185;
// newWidth = 69;
// newHeight = 80;
// }
// }
//
// PdfCanvas pdfCanvas = new PdfCanvas(page.newContentStreamAfter(), page.getResources(), pdfDoc);
// try (Canvas canvas = new Canvas(pdfCanvas, page.getPageSize())) {
// ImageData imageData = ImageDataFactory.create(qrCodeBytes);
// Image image = new Image(imageData)
// .scaleAbsolute(newWidth, newHeight)
// .setFixedPosition(qrX, qrY);
// canvas.add(image);
// }
// }
//
// pdfDoc.close();
// return pdfOut;
// }
// public static void main(String[] args) {
// String path = "C:\\Users\\YuanJie\\Desktop\\test.pdf";
// String outputPath = "C:\\Users\\YuanJie\\Desktop\\test1.pdf";
//
// String params = "http://192.168.110.151:7788/codeDetail?id=" + "1957649652924448769";
// byte[] bytes = PdfBoxQrCodeGenerator.generateQRCodeBytes(params);
//
// try {
// System.out.println("二维码字节大小: " + bytes.length + " 字节");
//
// // 在每一页添加二维码
// ByteArrayOutputStream byteArrayOutputStream =
// PdfBoxQrCodeGenerator.addQRCodeToPDFOnAllPages(path, bytes, false);
//
// try (FileOutputStream fileOut = new FileOutputStream(outputPath)) {
// byteArrayOutputStream.writeTo(fileOut);
// }
//
// System.out.println("PDF文件已成功生成到: " + outputPath);
// System.out.println("生成的PDF大小: " + new File(outputPath).length() + " 字节");
//
// } catch (Exception e) {
// e.printStackTrace();
// System.out.println("图纸管理 => 审核结束,向文件添加二维码失败");
// }
// }
// 固定的边距(点)
private static final float MARGIN = 10.0f;
// 二维码大小比例页面宽高中较小值的20%
private static final float QR_SIZE_RATIO = 0.2f;
// 二维码大小比例页面宽高中较小值的20%
private static final float QR_SIZE = 60.0f;
// 竖坐标
private static final float V_QR_X = 232.0f;
// 竖坐标
private static final float V_QR_Y = 679.5f;
// 横坐标
private static final float H_QR_X = 1116.5f;
// 横坐标
private static final float H_QR_Y = 34.0f;
public static ByteArrayOutputStream addQRCodeToPDFOnAllPages(String srcPdf, byte[] qrCodeBytes, boolean isChangeFile)
throws IOException {
PdfReader reader;
if (srcPdf.startsWith("http://") || srcPdf.startsWith("https://")) {
URL url = new URL(srcPdf);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setConnectTimeout(10000);
connection.setReadTimeout(30000);
connection.setRequestMethod("GET");
int responseCode = connection.getResponseCode();
if (responseCode != HttpURLConnection.HTTP_OK) {
throw new IOException("无法获取 PDF 文件HTTP响应码: " + responseCode);
}
InputStream inputStream = connection.getInputStream();
reader = new PdfReader(inputStream);
} else {
File srcFile = new File(srcPdf);
if (!srcFile.exists()) {
throw new IOException("源PDF文件不存在: " + srcPdf);
}
reader = new PdfReader(srcPdf);
}
ByteArrayOutputStream pdfOut = new ByteArrayOutputStream();
PdfWriter writer = new PdfWriter(pdfOut);
PdfDocument pdfDoc = new PdfDocument(reader, writer);
int numberOfPages = pdfDoc.getNumberOfPages();
for (int pageNum = 1; pageNum <= numberOfPages; pageNum++) {
if (pageNum == 1 && isChangeFile) {
continue;
}
PdfPage page = pdfDoc.getPage(pageNum);
// 获取页面的所有边界框信息
Rectangle mediaBox = page.getMediaBox();
Rectangle cropBox = page.getCropBox();
// 使用CropBox作为主要参考因为它定义了页面的可见区域
Rectangle visibleArea = (cropBox != null) ? cropBox : mediaBox;
// 计算页面宽高中较小的值
// float minDimension = Math.min(visibleArea.getWidth(), visibleArea.getHeight());
// 动态计算二维码大小页面宽高中较小值的20%
// float qrSize = minDimension * QR_SIZE_RATIO;
// 输出页面尺寸信息
// System.out.println("页面 " + pageNum + " 尺寸: " + visibleArea.getWidth() + "x" + visibleArea.getHeight());
// System.out.println("页面 " + pageNum + " 二维码大小 (点): " + qrSize);
// 计算左上角的位置PDF坐标系原点在左下角
// float qrX = visibleArea.getLeft() + MARGIN;
// float qrY = visibleArea.getTop() - qrSize - MARGIN;
// 打印二维码位置信息
// System.out.println("页面 " + pageNum + " 左上角二维码位置: x=" + qrX + ", y=" + qrY);
float qrX;
float qrY;
if (visibleArea.getWidth() > visibleArea.getHeight()) {
qrX = H_QR_X;
qrY = H_QR_Y;
} else {
qrX = V_QR_X;
qrY = V_QR_Y;
}
try {
// 使用Canvas API添加左上角二维码
addQRCodeWithCanvas(pdfDoc, page, qrCodeBytes, qrX, qrY, QR_SIZE);
} catch (Exception e) {
System.err.println("在页面 " + pageNum + " 添加二维码时出错: " + e.getMessage());
e.printStackTrace();
}
}
pdfDoc.close();
return pdfOut;
}
// 使用Canvas API添加图像
private static void addQRCodeWithCanvas(PdfDocument pdfDoc, PdfPage page,
byte[] qrCodeBytes, float x, float y, float size) throws IOException {
// 创建Canvas对象 - 使用PdfPage和Rectangle构造函数
Canvas canvas = new Canvas(page, page.getPageSize());
// 创建图像数据
ImageData imageData = ImageDataFactory.create(qrCodeBytes);
// 创建Image对象
Image image = new Image(imageData);
// 设置图像位置和大小
image.setFixedPosition(x, y);
image.setWidth(size);
image.setHeight(size);
// 添加图像到Canvas
canvas.add(image);
// 关闭Canvas
canvas.close();
System.out.println("Canvas方法设置位置: x=" + x + ", y=" + y);
}
// main方法示例
public static void main(String[] args) {
String path = "C:\\Users\\YuanJie\\Desktop\\test.pdf";
String outputPath = "C:\\Users\\YuanJie\\Desktop\\test1.pdf";
String params = "http://192.168.110.151:7788/codeDetail?id=" + "1957649652924448769";
BufferedImage image = QrCodeUtil.generate(params, 200, 200);
ByteArrayOutputStream out = new ByteArrayOutputStream();
ImgUtil.write(image, ImgUtil.IMAGE_TYPE_PNG, out);
byte[] bytes = out.toByteArray();
try {
System.out.println("二维码字节大小: " + bytes.length + " 字节");
// 先保存二维码图片用于验证
try (FileOutputStream qrOut = new FileOutputStream("C:\\Users\\YuanJie\\Desktop\\qrcode.png")) {
qrOut.write(bytes);
System.out.println("二维码图片已保存到桌面,用于验证二维码是否生成正确");
}
// 在每一页添加二维码
ByteArrayOutputStream byteArrayOutputStream = addQRCodeToPDFOnAllPages(path, bytes, false);
try (FileOutputStream fileOut = new FileOutputStream(outputPath)) {
byteArrayOutputStream.writeTo(fileOut);
}
File outputFile = new File(outputPath);
System.out.println("PDF文件已成功生成到: " + outputPath);
System.out.println("生成的PDF大小: " + outputFile.length() + " 字节");
if (outputFile.length() == new File(path).length()) {
System.out.println("警告:输出文件大小与原文件相同,可能二维码未正确添加");
}
} catch (Exception e) {
e.printStackTrace();
System.out.println("图纸管理 => 审核结束,向文件添加二维码失败");
}
}
}
//package org.dromara.common.utils;
//
//import cn.hutool.core.img.ImgUtil;
//import cn.hutool.extra.qrcode.QrCodeUtil;
//import com.google.zxing.BarcodeFormat;
//import com.google.zxing.MultiFormatWriter;
//import com.google.zxing.client.j2se.MatrixToImageWriter;
//import com.google.zxing.common.BitMatrix;
//import com.itextpdf.text.DocumentException;
//import com.itextpdf.text.Image;
//import com.itextpdf.text.pdf.PdfContentByte;
//import com.itextpdf.text.pdf.PdfReader;
//import com.itextpdf.text.pdf.PdfStamper;
//import org.dromara.system.domain.vo.SysOssVo;
//
//import java.awt.image.BufferedImage;
//import java.io.*;
//import java.net.FileNameMap;
//import java.net.URLConnection;
//import java.util.concurrent.CompletableFuture;
//
///**
// * @author lilemy
// * @date 2025/7/7 9:56
// */
//public class PdfBoxQrCodeGenerator {
//
// /**
// * 生成二维码图片并返回路径
// *
// * @param text 二维码文本
// * @param width 二维码宽度
// * @param height 二维码高度
// * @param outputPath 二维码图片保存路径
// * @return 二维码图片保存路径
// */
// public static String generateQRCodeImage(String text, int width, int height, String outputPath) throws Exception {
// BitMatrix bitMatrix = new MultiFormatWriter().encode(text, BarcodeFormat.QR_CODE, width, height);
// MatrixToImageWriter.writeToPath(bitMatrix, "PNG", new File(outputPath).toPath());
// return outputPath;
// }
//
// /**
// * 获取二维码图片字节数组
// *
// * @param text 二维码文本
// * @param width 二维码宽度
// * @param height 二维码高度
// * @return 二维码图片字节数组
// */
// public static byte[] generateQRCodeBytes(String text, int width, int height) {
// BufferedImage image = QrCodeUtil.generate(text, width, height);
// ByteArrayOutputStream out = new ByteArrayOutputStream();
// ImgUtil.write(image, ImgUtil.IMAGE_TYPE_PNG, out);
// return out.toByteArray();
// }
//
// /**
// * 获取二维码图片字节数组
// *
// * @param text 二维码文本
// * @return 二维码图片字节数组
// */
// public static byte[] generateQRCodeBytes(String text) {
// return generateQRCodeBytes(text, 200, 200);
// }
//
// /**
// * 在PDF指定位置添加二维码
// *
// * @param srcPdf 原PDF文件路径
// * @param destPdf 新PDF文件路径
// * @param qrImagePath 二维码图片路径
// * @param pageNum 页码
// * @param x 坐标
// * @param y 坐标
// */
// public static void addQRCodeToPDF(String srcPdf, String destPdf, String qrImagePath, int pageNum, float x, float y) throws IOException, DocumentException {
// PdfReader reader = new PdfReader(srcPdf);
// PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(destPdf));
// PdfContentByte content = stamper.getOverContent(pageNum);
// Image image = Image.getInstance(qrImagePath);
// image.setAbsolutePosition(x, y); // 坐标:左下角为原点
// image.scaleAbsolute(100, 100); // 设置二维码大小
//
// content.addImage(image);
// stamper.close();
// reader.close();
// }
//
// /**
// * 在PDF指定位置添加二维码
// *
// * @param srcPdf 原PDF文件路径
// * @param destPdf 新PDF文件路径
// * @param qrCodeBytes 二维码图片字节数组
// * @param pageNum 页码
// * @param x 坐标
// * @param y 坐标
// */
// public static void addQRCodeToPDF(String srcPdf, String destPdf, byte[] qrCodeBytes, int pageNum, float x, float y) throws IOException, DocumentException {
// PdfReader reader = new PdfReader(srcPdf);
// PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(destPdf));
// PdfContentByte content = stamper.getOverContent(pageNum);
//
// Image image = Image.getInstance(qrCodeBytes);
// image.setAbsolutePosition(x, y); // 坐标:左下角为原点
// image.scaleAbsolute(100, 100); // 设置二维码大小
@ -136,142 +548,168 @@ public class PdfBoxQrCodeGenerator {
// content.addImage(image);
// stamper.close();
// reader.close();
// }
//
// /**
// * 在PDF指定位置添加二维码并返回数据流
// *
// * @param srcPdf 原PDF文件路径
// * @param qrCodeBytes 二维码图片字节数组
// * @param pageNum 页码
// * @param x 坐标
// * @param y 坐标
// * @return 插入二维码后的PDF文件流
// */
//// public static ByteArrayOutputStream addQRCodeToPDF(String srcPdf, byte[] qrCodeBytes, int pageNum, float x, float y) throws IOException, DocumentException {
////
//// PdfReader reader = new PdfReader(srcPdf);
//// ByteArrayOutputStream pdfOut = new ByteArrayOutputStream();
//// PdfStamper stamper = new PdfStamper(reader, pdfOut);
////
//// PdfContentByte content = stamper.getOverContent(pageNum);
//// Image image = Image.getInstance(qrCodeBytes);
//// image.setAbsolutePosition(x, y); // 坐标:左下角为原点
//// image.scaleAbsolute(100, 100); // 设置二维码大小
////
//// content.addImage(image);
//// stamper.close();
//// reader.close();
////
//// return pdfOut;
//// }
// /**
// * 在PDF每一页的指定位置添加二维码并返回数据流根据页面方向自动调整位置
// *
// * @param srcPdf 原PDF文件路径可以是本地路径或网络地址
// * @param qrCodeBytes 二维码图片字节数组
// * @return 插入二维码后的PDF文件流
// */
// public static ByteArrayOutputStream addQRCodeToPDFOnAllPages(String srcPdf, byte[] qrCodeBytes, boolean isChangeFile)
// throws IOException, DocumentException {
//
// PdfReader reader = null;
// PdfStamper stamper = null;
// ByteArrayOutputStream pdfOut = new ByteArrayOutputStream();
//
// try {
// // 判断是网络地址还是本地文件路径
// if (srcPdf.startsWith("http://") || srcPdf.startsWith("https://")) {
// // 网络地址从URL读取PDF
// java.net.URL url = new java.net.URL(srcPdf);
// java.net.HttpURLConnection connection = (java.net.HttpURLConnection) url.openConnection();
// connection.setConnectTimeout(10000); // 连接超时10秒
// connection.setReadTimeout(30000); // 读取超时30秒
// connection.setRequestMethod("GET");
//
// int responseCode = connection.getResponseCode();
// if (responseCode != java.net.HttpURLConnection.HTTP_OK) {
// throw new IOException("无法从URL获取PDF文件HTTP响应码: " + responseCode + ", URL: " + srcPdf);
// }
//
// java.io.InputStream inputStream = connection.getInputStream();
// reader = new PdfReader(inputStream);
// } else {
// // 本地文件路径:检查文件是否存在
// File srcFile = new File(srcPdf);
// if (!srcFile.exists()) {
// throw new IOException("源PDF文件不存在: " + srcPdf);
// }
// reader = new PdfReader(srcPdf);
// }
//
// stamper = new PdfStamper(reader, pdfOut);
//
// int numberOfPages = reader.getNumberOfPages();
//
// // 遍历每一页并添加二维码
// for (int pageNum = 1; pageNum <= numberOfPages; pageNum++) {
// // 获取页面尺寸
// com.itextpdf.text.Rectangle pageSize = reader.getPageSize(pageNum);
// float pageWidth = pageSize.getWidth();
// float pageHeight = pageSize.getHeight();
//
// // 根据页面方向确定二维码位置
// float qrX, qrY;
//
// float newWidth, newHeight;
//
// // 如果是变更文件且为第一页,使用特殊位置处理
// if (isChangeFile && pageNum == 1) {
// // 变更文件第一页使用传入的坐标参数
// qrX = (pageWidth - 143);
// qrY = pageHeight - 213;
// newWidth = 67;
// newHeight = 80;
// } else {
// // 判断页面方向:宽度大于高度为横版,否则为竖版
// if (pageWidth > pageHeight) {
// // 横版页面:二维码放在右下角
// qrX = (pageWidth - 90); // 距离右边90点
// qrY = 24; // 距离底部24点
// newWidth = 67;
// newHeight = 79;
// } else {
// // 竖版页面:二维码放在左上角
// qrX = 226; // 距离左边226点
// qrY = pageHeight - 185; // 距离顶部185点
// newWidth = 69;
// newHeight = 80;
// }
// }
//
//
// PdfContentByte content = stamper.getOverContent(pageNum);
// Image image = Image.getInstance(qrCodeBytes);
// image.setAbsolutePosition(qrX, qrY); // 坐标:左下角为原点
// image.scaleAbsolute(newWidth, newHeight); // 设置二维码大小
// content.addImage(image);
// }
//
// } finally {
// if (stamper != null) {
// try {
// stamper.close();
// } catch (Exception e) {
// // 忽略关闭异常
// }
// }
// if (reader != null) {
// try {
// reader.close();
// } catch (Exception e) {
// // 忽略关闭异常
// }
// }
// }
//
// return pdfOut;
// }
/**
* 在PDF每一页的指定位置添加二维码并返回数据流根据页面方向自动调整位置
*
* @param srcPdf 原PDF文件路径可以是本地路径或网络地址
* @param qrCodeBytes 二维码图片字节数组
* @return 插入二维码后的PDF文件流
*/
public static ByteArrayOutputStream addQRCodeToPDFOnAllPages(String srcPdf, byte[] qrCodeBytes, boolean isChangeFile)
throws IOException, DocumentException {
PdfReader reader = null;
PdfStamper stamper = null;
ByteArrayOutputStream pdfOut = new ByteArrayOutputStream();
try {
// 判断是网络地址还是本地文件路径
if (srcPdf.startsWith("http://") || srcPdf.startsWith("https://")) {
// 网络地址从URL读取PDF
java.net.URL url = new java.net.URL(srcPdf);
java.net.HttpURLConnection connection = (java.net.HttpURLConnection) url.openConnection();
connection.setConnectTimeout(10000); // 连接超时10秒
connection.setReadTimeout(30000); // 读取超时30秒
connection.setRequestMethod("GET");
int responseCode = connection.getResponseCode();
if (responseCode != java.net.HttpURLConnection.HTTP_OK) {
throw new IOException("无法从URL获取PDF文件HTTP响应码: " + responseCode + ", URL: " + srcPdf);
}
java.io.InputStream inputStream = connection.getInputStream();
reader = new PdfReader(inputStream);
} else {
// 本地文件路径:检查文件是否存在
File srcFile = new File(srcPdf);
if (!srcFile.exists()) {
throw new IOException("源PDF文件不存在: " + srcPdf);
}
reader = new PdfReader(srcPdf);
}
stamper = new PdfStamper(reader, pdfOut);
int numberOfPages = reader.getNumberOfPages();
// 遍历每一页并添加二维码
for (int pageNum = 1; pageNum <= numberOfPages; pageNum++) {
// 获取页面尺寸
com.itextpdf.text.Rectangle pageSize = reader.getPageSize(pageNum);
float pageWidth = pageSize.getWidth();
float pageHeight = pageSize.getHeight();
// 根据页面方向确定二维码位置
float qrX, qrY;
float newWidth, newHeight;
// 如果是变更文件且为第一页,使用特殊位置处理
if (isChangeFile && pageNum == 1) {
// 变更文件第一页使用传入的坐标参数
qrX = (pageWidth - 143);
qrY = pageHeight - 213;
newWidth = 67;
newHeight = 80;
} else {
// 判断页面方向:宽度大于高度为横版,否则为竖版
if (pageWidth > pageHeight) {
// 横版页面:二维码放在右下角
qrX = (pageWidth - 90); // 距离右边90点
qrY = 24; // 距离底部24点
newWidth = 67;
newHeight = 79;
} else {
// 竖版页面:二维码放在左上角
qrX = 226; // 距离左边226点
qrY = pageHeight - 185; // 距离顶部185点
newWidth = 69;
newHeight = 80;
}
}
PdfContentByte content = stamper.getOverContent(pageNum);
Image image = Image.getInstance(qrCodeBytes);
image.setAbsolutePosition(qrX, qrY); // 坐标:左下角为原点
image.scaleAbsolute(newWidth, newHeight); // 设置二维码大小
content.addImage(image);
}
} finally {
if (stamper != null) {
try {
stamper.close();
} catch (Exception e) {
// 忽略关闭异常
}
}
if (reader != null) {
try {
reader.close();
} catch (Exception e) {
// 忽略关闭异常
}
}
}
return pdfOut;
}
public static void main(String[] args) {
String path = "C:\\Users\\YuanJie\\Desktop\\test.pdf";
String outputPath = "C:\\Users\\YuanJie\\Desktop\\test1.pdf";
String params = "http://192.168.110.151:7788/codeDetail?id="+"1957649652924448769";
byte[] bytes = PdfBoxQrCodeGenerator.generateQRCodeBytes(params);
try {
System.out.println("二维码字节大小: " + bytes.length + " 字节");
// 在每一页添加二维码
ByteArrayOutputStream byteArrayOutputStream = PdfBoxQrCodeGenerator.addQRCodeToPDFOnAllPages(path, bytes,false);
// 将输出流写入到指定文件
try (FileOutputStream fileOut = new FileOutputStream(outputPath)) {
byteArrayOutputStream.writeTo(fileOut);
}
System.out.println("PDF文件已成功生成到: " + outputPath);
System.out.println("生成的PDF大小: " + new File(outputPath).length() + " 字节");
} catch (Exception e) {
e.printStackTrace();
System.out.println("图纸管理 => 审核结束,向文件添加二维码失败, 错误");
}
}
}
//
//
// public static void main(String[] args) {
// String path = "C:\\Users\\YuanJie\\Desktop\\test.pdf";
// String outputPath = "C:\\Users\\YuanJie\\Desktop\\test1.pdf";
//
// String params = "http://192.168.110.151:7788/codeDetail?id="+"1957649652924448769";
// byte[] bytes = PdfBoxQrCodeGenerator.generateQRCodeBytes(params);
//
// try {
// System.out.println("二维码字节大小: " + bytes.length + " 字节");
//
// // 在每一页添加二维码
// ByteArrayOutputStream byteArrayOutputStream = PdfBoxQrCodeGenerator.addQRCodeToPDFOnAllPages(path, bytes,false);
//
// // 将输出流写入到指定文件
// try (FileOutputStream fileOut = new FileOutputStream(outputPath)) {
// byteArrayOutputStream.writeTo(fileOut);
// }
//
// System.out.println("PDF文件已成功生成到: " + outputPath);
// System.out.println("生成的PDF大小: " + new File(outputPath).length() + " 字节");
//
// } catch (Exception e) {
// e.printStackTrace();
// System.out.println("图纸管理 => 审核结束,向文件添加二维码失败, 错误");
// }
// }
//}

View File

@ -2,6 +2,7 @@ package org.dromara.common.utils.baiduUtil;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
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.HumanFaceRes;
@ -23,6 +24,7 @@ import java.util.List;
* @Version 1.0
* 处理百度人脸相关逻辑:人脸检测、人脸对比
*/
@Slf4j
@Service
public class BaiDuFace {
@ -92,7 +94,8 @@ public class BaiDuFace {
// 7. 处理API返回错误
if (faceRep.getErrorCode() != 0) {
throw new RuntimeException("错误码说明:" + faceRep.getErrorMsg());
log.warn("百度人脸API返回错误码{}", faceRep.getErrorCode());
throw new RuntimeException("未检测到有效人脸信息");
}
// 8. 验证人脸信息(无人脸时抛出异常)

View File

@ -0,0 +1,265 @@
package org.dromara.common.utils.listener.excel;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.exception.ExcelDataConvertException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.transaction.interceptor.TransactionAspectSupport;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.function.BiConsumer;
/**
* 通用Excel导入监听器支持事务性导入
* @param <T> 数据类型
* @param <P> 额外参数类型
*/
public class GenericExcelListener<T, P> extends AnalysisEventListener<T> {
private static final Logger logger = LoggerFactory.getLogger(GenericExcelListener.class);
/**
* 默认批量处理大小
*/
private static final int DEFAULT_BATCH_SIZE = 1000;
/**
* 批量处理大小
*/
private final int batchSize;
/**
* 临时存储数据列表
*/
private List<T> dataList;
/**
* 数据处理器(接收额外参数)
*/
private final BiConsumer<List<T>, P> dataConsumer;
/**
* 额外参数
*/
private final P extraParam;
/**
* 成功计数
*/
private int successCount = 0;
/**
* 失败计数
*/
private int failCount = 0;
/**
* 表头信息
*/
private List<String> headerList;
/**
* 当前工作表名称
*/
private String sheetName;
/**
* 是否发生异常
*/
private boolean hasError = false;
/**
* 异常信息
*/
private Exception exception;
/**
* 使用默认批量大小的构造函数
* @param dataConsumer 数据处理函数(接收额外参数)
* @param extraParam 额外参数
*/
public GenericExcelListener(BiConsumer<List<T>, P> dataConsumer, P extraParam) {
this(dataConsumer, extraParam, DEFAULT_BATCH_SIZE);
}
/**
* 自定义批量大小的构造函数
* @param dataConsumer 数据处理函数(接收额外参数)
* @param extraParam 额外参数
* @param batchSize 批量大小
*/
public GenericExcelListener(BiConsumer<List<T>, P> dataConsumer, P extraParam, int batchSize) {
this.dataConsumer = dataConsumer;
this.extraParam = extraParam;
this.batchSize = batchSize;
this.dataList = new ArrayList<>(batchSize);
}
/**
* 每一条数据解析都会来调用
* @param data 一行数据
* @param context 分析上下文
*/
@Override
public void invoke(T data, AnalysisContext context) {
// 如果已经发生错误,停止处理后续数据
if (hasError) {
return;
}
dataList.add(data);
// 达到batchSize了需要去存储一次数据库防止数据几万条数据在内存容易OOM
if (dataList.size() >= batchSize) {
saveData();
// 存储完成清理list
dataList.clear();
}
}
/**
* 所有数据解析完成后调用
* @param context 分析上下文
*/
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
// 如果已经发生错误,不再处理剩余数据
if (hasError) {
return;
}
// 确保所有数据都能持久化
if (!dataList.isEmpty()) {
saveData();
dataList.clear();
}
if (hasError) {
logger.error("Excel导入失败共处理: {} 条数据,成功: {} 条,失败: {} 条",
successCount + failCount, successCount, failCount);
throw new RuntimeException("Excel导入失败: " + exception.getMessage(), exception);
} else {
logger.info("Excel导入成功共处理: {} 条数据", successCount);
}
}
/**
* 在读取表头时调用
* @param headMap 表头映射
* @param context 分析上下文
*/
@Override
public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
this.headerList = new ArrayList<>(headMap.values());
this.sheetName = context.readSheetHolder().getSheetName();
logger.info("开始解析工作表: {}", sheetName);
logger.info("表头信息: {}", String.join(",", headerList));
}
/**
* 在异常处理时调用
* @param exception 异常信息
* @param context 分析上下文
*/
@Override
public void onException(Exception exception, AnalysisContext context) {
logger.error("解析失败: {}", exception.getMessage());
if (exception instanceof ExcelDataConvertException) {
ExcelDataConvertException excelDataConvertException = (ExcelDataConvertException) exception;
logger.error("第{}行,第{}列解析异常,数据为: {}",
excelDataConvertException.getRowIndex(),
excelDataConvertException.getColumnIndex(),
excelDataConvertException.getCellData());
}
// 标记错误状态
this.hasError = true;
this.exception = exception;
// 手动回滚事务
try {
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
} catch (Exception e) {
logger.warn("无法设置事务回滚,可能不在事务上下文中: {}", e.getMessage());
}
}
/**
* 保存数据
* 移除了@Transactional注解改为在外部方法上控制事务
*/
private void saveData() {
if (dataList.isEmpty() || hasError) {
return;
}
try {
logger.info("正在批量处理 {} 条数据", dataList.size());
dataConsumer.accept(dataList, extraParam);
successCount += dataList.size();
logger.info("成功处理 {} 条数据", dataList.size());
} catch (Exception e) {
logger.error("数据处理失败: {}", e.getMessage());
// 标记错误状态
this.hasError = true;
this.exception = e;
// 手动回滚事务
try {
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
} catch (Exception ex) {
logger.warn("无法设置事务回滚,可能不在事务上下文中: {}", ex.getMessage());
}
// 抛出异常,确保事务回滚
throw new RuntimeException("数据处理失败", e);
}
}
/**
* 获取成功计数
* @return 成功处理的数据条数
*/
public int getSuccessCount() {
return successCount;
}
/**
* 获取失败计数
* @return 处理失败的数据条数
*/
public int getFailCount() {
return failCount;
}
/**
* 获取表头信息
* @return 表头列表
*/
public List<String> getHeaderList() {
return headerList;
}
/**
* 获取工作表名称
* @return 工作表名称
*/
public String getSheetName() {
return sheetName;
}
/**
* 检查是否有错误发生
* @return 是否有错误
*/
public boolean hasError() {
return hasError;
}
/**
* 获取异常信息
* @return 异常信息
*/
public Exception getException() {
return exception;
}
}

View File

@ -0,0 +1,54 @@
package org.dromara.common.utils.listener.redis;
import cn.hutool.core.collection.CollUtil;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.sse.core.SseEmitterManager;
import org.dromara.message.domain.MsgNotice;
import org.dromara.message.service.IMsgNoticeService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
/**
* SSE 消息数据库存储监听器
*/
@Slf4j
@Component
public class SseMessageDbListener implements ApplicationRunner, Ordered {
@Autowired
private SseEmitterManager sseEmitterManager;
@Autowired
private IMsgNoticeService noticeService; // 注入你的数据库服务
@Override
public void run(ApplicationArguments args) throws Exception {
sseEmitterManager.subscribeMessage((message) -> {
log.info("SSE主题订阅收到消息session keys={} message={}", message.getUserIds(), message.getMessage());
if (CollUtil.isNotEmpty(message.getUserIds()) && message.getIsRecord()) {
ArrayList<MsgNotice> noticeList = new ArrayList<>();
for (Long key : message.getUserIds()) {
MsgNotice notice = new MsgNotice();
notice.setContent(message.getMessage());
notice.setProjectId(message.getProjectId());
notice.setRecipientId(key);
noticeList.add(notice);
}
noticeService.saveBatch(noticeList);
}
log.info("SSE消息已存储到数据库: {}", message.getMessage());
});
log.info("初始化SSE消息数据库存储监听器成功");
}
@Override
public int getOrder() {
return 0; // 可以调整顺序
}
}

View File

@ -1,21 +0,0 @@
package org.dromara.contractor.config;
import jakarta.annotation.Resource;
import org.dromara.contractor.interceptor.ContractorInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Resource
private ContractorInterceptor contractorInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(contractorInterceptor)
.addPathPatterns("/contractor/**") // 拦截所有 /contractor 开头
.excludePathPatterns("/contractor/contractor/**"); // 排除具体路径
}
}

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