From 3d2f4b05ffca93c9af1cb435c92413a5aa7f4cea Mon Sep 17 00:00:00 2001 From: lcj <2331845269@qq.com> Date: Thu, 24 Jul 2025 17:36:30 +0800 Subject: [PATCH] =?UTF-8?q?[add]=20app=E6=B3=A8=E5=86=8C=E3=80=81=E7=99=BB?= =?UTF-8?q?=E5=BD=95=E3=80=81=E5=AE=9E=E5=90=8D=E8=AE=A4=E8=AF=81=E3=80=81?= =?UTF-8?q?=E4=BA=BA=E5=91=98=E5=B1=95=E7=A4=BA=E3=80=81=E9=80=9A=E7=9F=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../web/controller/AuthController.java | 4 +- .../web/service/SysRegisterService.java | 19 ++ .../service/impl/PasswordAuthStrategy.java | 22 +- .../src/main/resources/application.yml | 3 +- .../core/domain/model/PasswordLoginBody.java | 6 +- .../core/domain/model/RegisterBody.java | 8 +- .../common/encrypt/filter/CryptoFilter.java | 2 +- .../dromara/app/system/domain/AppMenu.java | 14 ++ .../app/system/mapper/AppMenuMapper.java | 10 + .../app/system/service/AppMenuService.java | 35 ++- .../app/system/service/AppRoleService.java | 28 +++ .../service/impl/AppMenuServiceImpl.java | 117 ++++++++-- .../service/impl/AppRoleServiceImpl.java | 36 +++ .../resources/app/system/AppMenuMapper.xml | 22 ++ .../common/utils/baiduUtil/BaiDuCommon.java | 9 +- .../common/utils/baiduUtil/BaiDuOCR.java | 119 ++++------ .../common/utils/baiduUtil/HttpUtil.java | 77 +++++++ .../baiduUtil/entity/AccessTokenResponse.java | 10 +- .../utils/baiduUtil/entity/ocr/BankData.java | 20 +- .../utils/baiduUtil/entity/ocr/Field.java | 3 + .../baiduUtil/entity/ocr/IDCardInfo.java | 2 +- .../utils/baiduUtil/entity/ocr/Location.java | 18 +- .../utils/baiduUtil/entity/ocr/OcrReq.java | 16 +- .../utils/baiduUtil/entity/ocr/Result.java | 2 +- .../baiduUtil/entity/ocr/WordsResult.java | 33 +-- .../contractor/controller/AppController.java | 40 ---- .../app/SubConstructionUserAppController.java | 77 +++++++ .../domain/SubConstructionUser.java | 5 + .../SubConstructionUserAuthenticationReq.java | 150 +++++++++++++ .../enums/SubConstructionUserRoleEnum.java | 24 ++ .../service/ISubConstructionUserService.java | 53 ++++- .../impl/SubConstructionUserServiceImpl.java | 212 +++++++++++++++++- .../app/BusAttendanceAppController.java | 61 +++++ .../controller/app/BusLeaveAppController.java | 42 ++++ .../app/BusProjectAppController.java | 33 +++ .../app/BusReissueCardAppController.java | 41 ++++ .../dromara/project/domain/BusAttendance.java | 2 +- .../org/dromara/project/domain/BusLeave.java | 5 + .../dto/attendance/BusAttendanceByDayReq.java | 25 +++ .../attendance/BusAttendanceByMonthReq.java | 22 ++ .../dto/leave/BusLeaveGangerReviewReq.java | 41 ++++ .../project/domain/vo/leave/BusLeaveVo.java | 5 + .../service/IBusAttendanceService.java | 10 + .../project/service/IBusLeaveService.java | 9 + .../impl/BusAttendanceServiceImpl.java | 31 ++- .../service/impl/BusLeaveServiceImpl.java | 90 +++++++- .../system/domain/SysNotifications.java | 90 ++++++++ .../SysNotificationsCreateBatchReq.java | 59 +++++ .../SysNotificationsQueryReq.java | 48 ++++ .../system/domain/vo/SysNotificationsVo.java | 80 +++++++ .../system/mapper/SysNotificationsMapper.java | 17 ++ .../service/SysNotificationsService.java | 68 ++++++ .../impl/SysNotificationsServiceImpl.java | 186 +++++++++++++++ .../mapper/system/SysNotificationsMapper.xml | 28 +++ script/sql/xinnengyuan.sql | 22 ++ 55 files changed, 1998 insertions(+), 213 deletions(-) create mode 100644 ruoyi-modules/ruoyi-system/src/main/java/org/dromara/common/utils/baiduUtil/HttpUtil.java delete mode 100644 ruoyi-modules/ruoyi-system/src/main/java/org/dromara/contractor/controller/AppController.java create mode 100644 ruoyi-modules/ruoyi-system/src/main/java/org/dromara/contractor/controller/app/SubConstructionUserAppController.java create mode 100644 ruoyi-modules/ruoyi-system/src/main/java/org/dromara/contractor/domain/dto/constructionuser/SubConstructionUserAuthenticationReq.java create mode 100644 ruoyi-modules/ruoyi-system/src/main/java/org/dromara/contractor/domain/enums/SubConstructionUserRoleEnum.java create mode 100644 ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/controller/app/BusAttendanceAppController.java create mode 100644 ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/controller/app/BusLeaveAppController.java create mode 100644 ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/controller/app/BusProjectAppController.java create mode 100644 ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/controller/app/BusReissueCardAppController.java create mode 100644 ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/domain/dto/attendance/BusAttendanceByDayReq.java create mode 100644 ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/domain/dto/attendance/BusAttendanceByMonthReq.java create mode 100644 ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/domain/dto/leave/BusLeaveGangerReviewReq.java create mode 100644 ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysNotifications.java create mode 100644 ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/dto/notifications/SysNotificationsCreateBatchReq.java create mode 100644 ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/dto/notifications/SysNotificationsQueryReq.java create mode 100644 ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysNotificationsVo.java create mode 100644 ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysNotificationsMapper.java create mode 100644 ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/SysNotificationsService.java create mode 100644 ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysNotificationsServiceImpl.java create mode 100644 ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysNotificationsMapper.xml diff --git a/ruoyi-admin/src/main/java/org/dromara/web/controller/AuthController.java b/ruoyi-admin/src/main/java/org/dromara/web/controller/AuthController.java index 06797a1..64c8375 100644 --- a/ruoyi-admin/src/main/java/org/dromara/web/controller/AuthController.java +++ b/ruoyi-admin/src/main/java/org/dromara/web/controller/AuthController.java @@ -80,7 +80,7 @@ public class AuthController { * @param body 登录信息 * @return 结果 */ - @ApiEncrypt +// @ApiEncrypt @PostMapping("/login") public R login(@RequestBody String body) { LoginBody loginBody = JsonUtils.parseObject(body, LoginBody.class); @@ -183,7 +183,7 @@ public class AuthController { /** * 用户注册 */ - @ApiEncrypt +// @ApiEncrypt @PostMapping("/register") public R register(@Validated @RequestBody RegisterBody user) { if (!configService.selectRegisterEnabled(user.getTenantId())) { diff --git a/ruoyi-admin/src/main/java/org/dromara/web/service/SysRegisterService.java b/ruoyi-admin/src/main/java/org/dromara/web/service/SysRegisterService.java index c1e8300..5ee5343 100644 --- a/ruoyi-admin/src/main/java/org/dromara/web/service/SysRegisterService.java +++ b/ruoyi-admin/src/main/java/org/dromara/web/service/SysRegisterService.java @@ -1,6 +1,7 @@ package org.dromara.web.service; import cn.dev33.satoken.secure.BCrypt; +import cn.hutool.core.util.RandomUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import lombok.RequiredArgsConstructor; import org.dromara.common.core.constant.Constants; @@ -43,6 +44,7 @@ public class SysRegisterService { public void register(RegisterBody registerBody) { String tenantId = registerBody.getTenantId(); String username = registerBody.getUsername(); + String phonenumber = registerBody.getPhonenumber(); String password = registerBody.getPassword(); // 校验用户类型是否存在 String userType = UserType.getUserType(registerBody.getUserType()).getUserType(); @@ -52,6 +54,15 @@ public class SysRegisterService { if (!isValid) { throw new UserException("注册失败,密码需满足8–18位,包含大小写字母、数字、特殊字符中的至少三种组合"); } + String middleFour = phonenumber.substring(3, 7); + if (StringUtils.isBlank(username)) { + do { + // 生成 6 位随机字母,并转为小写 + String randomSix = RandomUtil.randomString(6).toLowerCase(); + username = randomSix + middleFour; + // 如果已有同名记录,循环继续 + } while (existsUsername(username)); + } boolean captchaEnabled = captchaProperties.getEnable(); // 验证码开关 if (captchaEnabled) { @@ -62,6 +73,7 @@ public class SysRegisterService { sysUser.setNickName(username); sysUser.setPassword(BCrypt.hashpw(password)); sysUser.setUserType(userType); + sysUser.setPhonenumber(phonenumber); boolean exist = TenantHelper.dynamic(tenantId, () -> { return userMapper.exists(new LambdaQueryWrapper() @@ -77,6 +89,13 @@ public class SysRegisterService { recordLogininfor(tenantId, username, Constants.REGISTER, MessageUtils.message("user.register.success")); } + private boolean existsUsername(String username) { + LambdaQueryWrapper qw = new LambdaQueryWrapper<>(); + qw.eq(SysUser::getUserName, username); + // selectCount 性能足够,若并发极高可改为 exists 查询 + return userMapper.selectCount(qw) > 0; + } + /** * 校验验证码 * diff --git a/ruoyi-admin/src/main/java/org/dromara/web/service/impl/PasswordAuthStrategy.java b/ruoyi-admin/src/main/java/org/dromara/web/service/impl/PasswordAuthStrategy.java index e8e60e1..4648ad0 100644 --- a/ruoyi-admin/src/main/java/org/dromara/web/service/impl/PasswordAuthStrategy.java +++ b/ruoyi-admin/src/main/java/org/dromara/web/service/impl/PasswordAuthStrategy.java @@ -54,17 +54,31 @@ public class PasswordAuthStrategy implements IAuthStrategy { String tenantId = loginBody.getTenantId(); String username = loginBody.getUsername(); String password = loginBody.getPassword(); + String phonenumber = loginBody.getPhonenumber(); String code = loginBody.getCode(); String uuid = loginBody.getUuid(); - + // 2. 如果没传用户名,用手机号去查,并写到新的局部变量里 + String finalUsername; + if (StringUtils.isAllBlank(username, phonenumber)) { + throw new UserException("用户名或手机号不能为空"); + } + if (StringUtils.isBlank(username)) { + SysUserVo sysUserVo = userMapper.selectVoOne( + new LambdaQueryWrapper() + .eq(SysUser::getPhonenumber, phonenumber) + ); + finalUsername = sysUserVo.getUserName(); + } else { + finalUsername = username; + } boolean captchaEnabled = captchaProperties.getEnable(); // 验证码开关 if (captchaEnabled) { - validateCaptcha(tenantId, username, code, uuid); + validateCaptcha(tenantId, finalUsername, code, uuid); } LoginUser loginUser = TenantHelper.dynamic(tenantId, () -> { - SysUserVo user = loadUserByUsername(username); - loginService.checkLogin(LoginType.PASSWORD, tenantId, username, () -> !BCrypt.checkpw(password, user.getPassword())); + SysUserVo user = loadUserByUsername(finalUsername); + loginService.checkLogin(LoginType.PASSWORD, tenantId, finalUsername, () -> !BCrypt.checkpw(password, user.getPassword())); // 此处可根据登录用户的数据不同 自行创建 loginUser return loginService.buildLoginUser(user); }); diff --git a/ruoyi-admin/src/main/resources/application.yml b/ruoyi-admin/src/main/resources/application.yml index 253717a..c46ab8a 100644 --- a/ruoyi-admin/src/main/resources/application.yml +++ b/ruoyi-admin/src/main/resources/application.yml @@ -8,7 +8,7 @@ ruoyi: copyrightYear: 2024 captcha: - enable: true + enable: false # 页面 <参数设置> 可开启关闭 验证码校验 # 验证码类型 math 数组计算 char 字符验证 type: MATH @@ -193,7 +193,6 @@ api-decrypt: - /v3/api-docs/** # 放行OpenAPI文档 - /actuator/** # 放行监控接口 - /other/ys7Device/webhook # 放行萤石云设备回调接口 - - /auth/register # 放行注册接口 springdoc: api-docs: diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/model/PasswordLoginBody.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/model/PasswordLoginBody.java index 87d0e8e..943a780 100644 --- a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/model/PasswordLoginBody.java +++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/model/PasswordLoginBody.java @@ -17,7 +17,6 @@ public class PasswordLoginBody extends LoginBody { /** * 用户名 */ - @NotBlank(message = "{user.username.not.blank}") @Length(min = 2, max = 20, message = "{user.username.length.valid}") private String username; @@ -28,4 +27,9 @@ public class PasswordLoginBody extends LoginBody { @Length(min = 5, max = 20, message = "{user.password.length.valid}") private String password; + /** + * 手机号 + */ + private String phonenumber; + } diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/model/RegisterBody.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/model/RegisterBody.java index 6ea8a76..3866534 100644 --- a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/model/RegisterBody.java +++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/model/RegisterBody.java @@ -17,7 +17,7 @@ public class RegisterBody extends LoginBody { /** * 用户名 */ - @NotBlank(message = "{user.username.not.blank}") +// @NotBlank(message = "{user.username.not.blank}") @Length(min = 2, max = 20, message = "{user.username.length.valid}") private String username; @@ -28,6 +28,12 @@ public class RegisterBody extends LoginBody { @Length(min = 5, max = 20, message = "{user.password.length.valid}") private String password; + /** + * 手机号码 + */ + @NotBlank(message = "{user.phonenumber.not.blank}") + private String phonenumber; + private String userType; } diff --git a/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/filter/CryptoFilter.java b/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/filter/CryptoFilter.java index 39dbc5c..e085fc1 100644 --- a/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/filter/CryptoFilter.java +++ b/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/filter/CryptoFilter.java @@ -77,7 +77,7 @@ public class CryptoFilter implements Filter { // new ServiceException("没有访问权限,请联系管理员授权", HttpStatus.FORBIDDEN)); // return; // } - throw new ServiceException("无权访问接口", HttpStatus.BAD_METHOD); +// throw new ServiceException("无权访问接口", HttpStatus.BAD_METHOD); } } diff --git a/ruoyi-modules/ruoyi-app/src/main/java/org/dromara/app/system/domain/AppMenu.java b/ruoyi-modules/ruoyi-app/src/main/java/org/dromara/app/system/domain/AppMenu.java index 9743689..0d08371 100644 --- a/ruoyi-modules/ruoyi-app/src/main/java/org/dromara/app/system/domain/AppMenu.java +++ b/ruoyi-modules/ruoyi-app/src/main/java/org/dromara/app/system/domain/AppMenu.java @@ -8,6 +8,8 @@ import lombok.EqualsAndHashCode; import org.dromara.common.mybatis.core.domain.BaseEntity; import java.io.Serial; +import java.util.ArrayList; +import java.util.List; /** * app菜单 @@ -85,6 +87,18 @@ public class AppMenu extends BaseEntity { */ private String remark; + /** + * 父菜单名称 + */ + @TableField(exist = false) + private String parentName; + + /** + * 子菜单 + */ + @TableField(exist = false) + private List children = new ArrayList<>(); + @Serial @TableField(exist = false) private static final long serialVersionUID = 1L; diff --git a/ruoyi-modules/ruoyi-app/src/main/java/org/dromara/app/system/mapper/AppMenuMapper.java b/ruoyi-modules/ruoyi-app/src/main/java/org/dromara/app/system/mapper/AppMenuMapper.java index 7a39e8e..ebf05e1 100644 --- a/ruoyi-modules/ruoyi-app/src/main/java/org/dromara/app/system/mapper/AppMenuMapper.java +++ b/ruoyi-modules/ruoyi-app/src/main/java/org/dromara/app/system/mapper/AppMenuMapper.java @@ -3,6 +3,8 @@ package org.dromara.app.system.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import org.dromara.app.system.domain.AppMenu; +import java.util.List; + /** * @author lilemy * @description 针对表【app_menu(app菜单)】的数据库操作Mapper @@ -10,6 +12,14 @@ import org.dromara.app.system.domain.AppMenu; */ public interface AppMenuMapper extends BaseMapper { + /** + * 根据用户ID查询菜单 + * + * @param userId 用户ID + * @return 菜单列表 + */ + List selectMenuTreeByUserId(Long userId); + } diff --git a/ruoyi-modules/ruoyi-app/src/main/java/org/dromara/app/system/service/AppMenuService.java b/ruoyi-modules/ruoyi-app/src/main/java/org/dromara/app/system/service/AppMenuService.java index 8e1dbf9..a4743aa 100644 --- a/ruoyi-modules/ruoyi-app/src/main/java/org/dromara/app/system/service/AppMenuService.java +++ b/ruoyi-modules/ruoyi-app/src/main/java/org/dromara/app/system/service/AppMenuService.java @@ -1,7 +1,6 @@ package org.dromara.app.system.service; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; -import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.IService; import org.dromara.app.system.domain.AppMenu; import org.dromara.app.system.domain.dto.menu.AppMenuCreateReq; @@ -9,6 +8,8 @@ import org.dromara.app.system.domain.dto.menu.AppMenuQueryReq; import org.dromara.app.system.domain.dto.menu.AppMenuUpdateReq; import org.dromara.app.system.domain.vo.menu.AppMenuVo; +import java.util.List; + /** * @author lilemy * @description 针对表【app_menu(app菜单)】的数据库操作Service @@ -16,6 +17,22 @@ import org.dromara.app.system.domain.vo.menu.AppMenuVo; */ public interface AppMenuService extends IService { + /** + * 根据id查询app菜单 + * + * @param id app菜单id + * @return app菜单视图 + */ + AppMenuVo queryById(Long id); + + /** + * 根据用户ID查询菜单树信息 + * + * @param userId 用户ID + * @return 菜单列表 + */ + List selectMenuTreeByUserId(Long userId); + /** * 新增app菜单 * @@ -32,6 +49,14 @@ public interface AppMenuService extends IService { */ Boolean updateByReq(AppMenuUpdateReq req); + /** + * 校验app菜单名称唯一 + * + * @param menu app菜单 + * @return 是否唯一 + */ + Boolean checkMenuNameUnique(AppMenu menu); + /** * 获取app菜单视图 * @@ -49,11 +74,11 @@ public interface AppMenuService extends IService { LambdaQueryWrapper buildQueryWrapper(AppMenuQueryReq req); /** - * 获取app菜单分页对象视图 + * 获取app菜单列表对象视图 * - * @param menuPage app菜单分页对象 - * @return app菜单分页对象视图 + * @param menuList app菜单列表对象 + * @return app菜单列表对象视图 */ - Page getVoPage(Page menuPage); + List getVoList(List menuList); } diff --git a/ruoyi-modules/ruoyi-app/src/main/java/org/dromara/app/system/service/AppRoleService.java b/ruoyi-modules/ruoyi-app/src/main/java/org/dromara/app/system/service/AppRoleService.java index 43047b6..25fccdb 100644 --- a/ruoyi-modules/ruoyi-app/src/main/java/org/dromara/app/system/service/AppRoleService.java +++ b/ruoyi-modules/ruoyi-app/src/main/java/org/dromara/app/system/service/AppRoleService.java @@ -1,7 +1,11 @@ package org.dromara.app.system.service; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.IService; import org.dromara.app.system.domain.AppRole; +import org.dromara.app.system.domain.dto.role.AppRoleQueryReq; +import org.dromara.app.system.domain.vo.role.AppRoleVo; /** * @author lilemy @@ -10,4 +14,28 @@ import org.dromara.app.system.domain.AppRole; */ public interface AppRoleService extends IService { + /** + * 获取app角色视图 + * + * @param menu app角色 + * @return app角色视图 + */ + AppRoleVo getVo(AppRole menu); + + /** + * 构建查询条件封装 + * + * @param req 查询条件 + * @return 查询条件封装 + */ + LambdaQueryWrapper buildQueryWrapper(AppRoleQueryReq req); + + /** + * 获取app角色视图列表 + * + * @param rolePage app角色列表 + * @return app角色视图列表 + */ + Page getVoList(Page rolePage); + } diff --git a/ruoyi-modules/ruoyi-app/src/main/java/org/dromara/app/system/service/impl/AppMenuServiceImpl.java b/ruoyi-modules/ruoyi-app/src/main/java/org/dromara/app/system/service/impl/AppMenuServiceImpl.java index 6292ca2..7124a0f 100644 --- a/ruoyi-modules/ruoyi-app/src/main/java/org/dromara/app/system/service/impl/AppMenuServiceImpl.java +++ b/ruoyi-modules/ruoyi-app/src/main/java/org/dromara/app/system/service/impl/AppMenuServiceImpl.java @@ -1,8 +1,7 @@ package org.dromara.app.system.service.impl; -import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.ObjectUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; -import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import org.dromara.app.system.domain.AppMenu; import org.dromara.app.system.domain.dto.menu.AppMenuCreateReq; @@ -11,10 +10,16 @@ import org.dromara.app.system.domain.dto.menu.AppMenuUpdateReq; import org.dromara.app.system.domain.vo.menu.AppMenuVo; import org.dromara.app.system.mapper.AppMenuMapper; import org.dromara.app.system.service.AppMenuService; +import org.dromara.common.core.constant.HttpStatus; +import org.dromara.common.core.constant.SystemConstants; +import org.dromara.common.core.exception.ServiceException; +import org.dromara.common.core.utils.StreamUtils; import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.satoken.utils.LoginHelper; import org.springframework.beans.BeanUtils; import org.springframework.stereotype.Service; +import java.util.ArrayList; import java.util.List; /** @@ -26,6 +31,43 @@ import java.util.List; public class AppMenuServiceImpl extends ServiceImpl implements AppMenuService { + /** + * 根据id查询app菜单 + * + * @param id app菜单id + * @return app菜单视图 + */ + @Override + public AppMenuVo queryById(Long id) { + AppMenu menu = this.getById(id); + if (menu == null) { + throw new ServiceException("app菜单不存在", HttpStatus.NOT_FOUND); + } + return this.getVo(menu); + } + + /** + * 根据用户ID查询菜单树信息 + * + * @param userId 用户ID + * @return 菜单列表 + */ + @Override + public List selectMenuTreeByUserId(Long userId) { + List menus; + if (LoginHelper.isSuperAdmin(userId)) { + menus = this.lambdaQuery() + .in(AppMenu::getMenuType, SystemConstants.TYPE_DIR, SystemConstants.TYPE_MENU) + .eq(AppMenu::getStatus, SystemConstants.NORMAL) + .orderByAsc(AppMenu::getParentId) + .orderByAsc(AppMenu::getOrderNum) + .list(); + } else { + menus = baseMapper.selectMenuTreeByUserId(userId); + } + return getChildPerms(menus, 0); + } + /** * 新增app菜单 * @@ -48,6 +90,23 @@ public class AppMenuServiceImpl extends ServiceImpl return null; } + /** + * 校验app菜单名称唯一 + * + * @param menu app菜单 + * @return 是否唯一 + */ + @Override + public Boolean checkMenuNameUnique(AppMenu menu) { + boolean exists = this.exists( + new LambdaQueryWrapper() + .eq(AppMenu::getMenuName, menu.getMenuName()) + .eq(AppMenu::getParentId, menu.getParentId()) + .ne(ObjectUtil.isNotNull(menu.getId()), AppMenu::getId, menu.getId()) + ); + return !exists; + } + /** * 获取app菜单视图 * @@ -88,29 +147,53 @@ public class AppMenuServiceImpl extends ServiceImpl } /** - * 获取app菜单分页对象视图 + * 获取app菜单列表对象视图 * - * @param menuPage app菜单分页对象 - * @return app菜单分页对象视图 + * @param menuList app菜单列表对象 + * @return app菜单列表对象视图 */ @Override - public Page getVoPage(Page menuPage) { - List menuList = menuPage.getRecords(); - Page menuVoPage = new Page<>( - menuPage.getCurrent(), - menuPage.getSize(), - menuPage.getTotal()); - if (CollUtil.isEmpty(menuList)) { - return menuVoPage; - } - List menuVoList = menuList.stream().map(entity -> { + public List getVoList(List menuList) { + return menuList.stream().map(entity -> { AppMenuVo menuVo = new AppMenuVo(); BeanUtils.copyProperties(entity, menuVo); return menuVo; }).toList(); - menuVoPage.setRecords(menuVoList); - return menuVoPage; } + + /* 根据父节点的ID获取所有子节点 + * + * @param list 分类表 + * @param parentId 传入的父节点ID + * @return String + */ + private List getChildPerms(List list, int parentId) { + List returnList = new ArrayList<>(); + for (AppMenu t : list) { + // 一、根据传入的某个父节点ID,遍历该父节点的所有子节点 + if (t.getParentId() == parentId) { + recursionFn(list, t); + returnList.add(t); + } + } + return returnList; + } + + /** + * 递归列表 + */ + private void recursionFn(List list, AppMenu t) { + // 得到子节点列表 + List childList = StreamUtils.filter(list, n -> n.getParentId().equals(t.getId())); + t.setChildren(childList); + for (AppMenu tChild : childList) { + // 判断是否有子节点 + if (list.stream().anyMatch(n -> n.getParentId().equals(tChild.getId()))) { + recursionFn(list, tChild); + } + } + } + } diff --git a/ruoyi-modules/ruoyi-app/src/main/java/org/dromara/app/system/service/impl/AppRoleServiceImpl.java b/ruoyi-modules/ruoyi-app/src/main/java/org/dromara/app/system/service/impl/AppRoleServiceImpl.java index 0393e50..c44e8df 100644 --- a/ruoyi-modules/ruoyi-app/src/main/java/org/dromara/app/system/service/impl/AppRoleServiceImpl.java +++ b/ruoyi-modules/ruoyi-app/src/main/java/org/dromara/app/system/service/impl/AppRoleServiceImpl.java @@ -1,7 +1,11 @@ package org.dromara.app.system.service.impl; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import org.dromara.app.system.domain.AppRole; +import org.dromara.app.system.domain.dto.role.AppRoleQueryReq; +import org.dromara.app.system.domain.vo.role.AppRoleVo; import org.dromara.app.system.mapper.AppRoleMapper; import org.dromara.app.system.service.AppRoleService; import org.springframework.stereotype.Service; @@ -15,6 +19,38 @@ import org.springframework.stereotype.Service; public class AppRoleServiceImpl extends ServiceImpl implements AppRoleService { + /** + * 获取app角色视图 + * + * @param menu app角色 + * @return app角色视图 + */ + @Override + public AppRoleVo getVo(AppRole menu) { + return null; + } + + /** + * 构建查询条件封装 + * + * @param req 查询条件 + * @return 查询条件封装 + */ + @Override + public LambdaQueryWrapper buildQueryWrapper(AppRoleQueryReq req) { + return null; + } + + /** + * 获取app角色视图列表 + * + * @param rolePage app角色列表 + * @return app角色视图列表 + */ + @Override + public Page getVoList(Page rolePage) { + return null; + } } diff --git a/ruoyi-modules/ruoyi-app/src/main/resources/app/system/AppMenuMapper.xml b/ruoyi-modules/ruoyi-app/src/main/resources/app/system/AppMenuMapper.xml index 347ca53..b2b15a9 100644 --- a/ruoyi-modules/ruoyi-app/src/main/resources/app/system/AppMenuMapper.xml +++ b/ruoyi-modules/ruoyi-app/src/main/resources/app/system/AppMenuMapper.xml @@ -31,4 +31,26 @@ icon,create_dept,remark,create_by,update_by, create_time,update_time + + diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/common/utils/baiduUtil/BaiDuCommon.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/common/utils/baiduUtil/BaiDuCommon.java index d2e41c5..af959bb 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/common/utils/baiduUtil/BaiDuCommon.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/common/utils/baiduUtil/BaiDuCommon.java @@ -1,12 +1,13 @@ package org.dromara.common.utils.baiduUtil; +import cn.hutool.json.JSONUtil; import com.google.gson.Gson; import org.dromara.common.utils.baiduUtil.entity.AccessTokenResponse; -import org.glassfish.jaxb.runtime.v2.runtime.reflect.opt.Const; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.stereotype.Service; -import org.springframework.beans.factory.annotation.Value; + import java.io.IOException; import java.net.URI; import java.net.http.HttpClient; @@ -20,7 +21,7 @@ import static org.dromara.common.constant.businessConstant.REDIS_BAIDU_KEY; * @Author 铁憨憨 * @Date 2025/7/18 9:46 * @Version 1.0 - * + *

* 获取百度AccessToken */ @Service @@ -88,7 +89,7 @@ public class BaiDuCommon { HttpResponse response = httpClient.send(request, HttpResponse.BodyHandlers.ofString()); if (response.statusCode() == 200) { - AccessTokenResponse tokenResponse = gson.fromJson(response.body(), AccessTokenResponse.class); + AccessTokenResponse tokenResponse = JSONUtil.toBean(response.body(), AccessTokenResponse.class); return tokenResponse.getAccessToken(); } else { throw new IOException("获取AccessToken失败,状态码: " + response.statusCode()); diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/common/utils/baiduUtil/BaiDuOCR.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/common/utils/baiduUtil/BaiDuOCR.java index 65a45da..49ce843 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/common/utils/baiduUtil/BaiDuOCR.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/common/utils/baiduUtil/BaiDuOCR.java @@ -1,6 +1,8 @@ package org.dromara.common.utils.baiduUtil; +import com.alibaba.fastjson.JSON; import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.extern.slf4j.Slf4j; import org.dromara.common.utils.baiduUtil.entity.ocr.IDCardInfo; import org.dromara.common.utils.baiduUtil.entity.ocr.OcrReq; import org.dromara.common.utils.baiduUtil.entity.ocr.Result; @@ -9,20 +11,18 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.io.IOException; -import java.net.URI; import java.net.URLEncoder; import java.net.http.HttpClient; -import java.net.http.HttpRequest; -import java.net.http.HttpResponse; import java.nio.charset.StandardCharsets; /** * @Author 铁憨憨 * @Date 2025/7/18 10:45 * @Version 1.0 - * + *

* 处理百度OCR相关逻辑:身份证识别、银行卡识别 */ +@Slf4j @Service public class BaiDuOCR { @Autowired @@ -30,20 +30,19 @@ public class BaiDuOCR { @Autowired private HttpClient httpClient; @Autowired - private ObjectMapper objectMapper; //ObjectMapper 是 Java 处理 JSON 的核心工具,项目中使用它进行 JSON 与 Java 对象的相互转换。 + private ObjectMapper objectMapper; //ObjectMapper 是 Java 处理 JSON 的核心工具,项目中使用它进行 JSON 与 Java 对象的相互转换。 - String baseUrlTemplate = "https://aip.baidubce.com/rest/2.0/ocr/v1/bankcard?access_token=%s"; - String BASE_URL = "https://aip.baidubce.com/rest/2.0/ocr/v1/idcard?access_token=%s"; - + String baseUrlTemplate = "https://aip.baidubce.com/rest/2.0/ocr/v1/bankcard"; + String BASE_URL = "https://aip.baidubce.com/rest/2.0/ocr/v1/idcard"; /** + * @param vr 请求参数 + * @return 识别结果Result * @description 银行卡OCR识别 * @author 铁憨憨 * @date 2025/7/18 11:50 - * @param vr 请求参数 - * @return 识别结果Result **/ public Result bankCardOCRRecognition(OcrReq vr) { // 先从缓存里面捞取token,若为空直接抛出异常 @@ -53,62 +52,41 @@ public class BaiDuOCR { } try { - // 构建请求URL(包含token参数) - String requestUrl = String.format(baseUrlTemplate, URLEncoder.encode(atStr, StandardCharsets.UTF_8)); - - // 准备请求体(将请求参数转为JSON) - String requestBody = objectMapper.writeValueAsString(vr); - if (requestBody == null) { - throw new RuntimeException("请求参数序列化失败"); + String param; + if (vr.getImage() != null) { + String imgParam = URLEncoder.encode(vr.getImage(), StandardCharsets.UTF_8); + param = "image=" + imgParam; + } else if (vr.getUrl() != null) { + param = "url=" + vr.getUrl(); + } else { + throw new RuntimeException("请传入图片或图片URL"); } // 构建HTTP请求 - HttpRequest request = HttpRequest.newBuilder() - .uri(URI.create(requestUrl)) - .header("Content-Type", "application/x-www-form-urlencoded") - .POST(HttpRequest.BodyPublishers.ofString(requestBody)) - .build(); + String resultStr = HttpUtil.post(baseUrlTemplate, atStr, param); - // 发送请求并获取响应 - HttpResponse response = httpClient.send(request, HttpResponse.BodyHandlers.ofString()); - - // 处理响应状态码(非200直接抛出异常) - if (response.statusCode() != 200) { - throw new RuntimeException("接口请求失败,状态码:" + response.statusCode() + ",响应信息:" + response.body()); - } - - // 解析响应结果(若解析失败抛出异常) - Result result = objectMapper.readValue(response.body(), Result.class); + log.info("百度OCR识别结果:{}", resultStr); + Result result = JSON.parseObject(resultStr, Result.class); if (result == null) { - throw new RuntimeException("响应结果解析失败"); + throw new RuntimeException("未识别到银行卡信息"); } - -// // 若接口返回错误码(如百度API的error_code),抛出异常 -// if (result.getErrorCode() != null && result.getErrorCode() != 0) { -// throw new RuntimeException("接口返回错误:" + result.getErrorMessage() + "(错误码:" + result.getErrorCode() + ")"); -// } - return result; } catch (IOException e) { // IO异常(如序列化失败、网络IO错误) throw new RuntimeException("IO处理异常:" + e.getMessage(), e); - } catch (InterruptedException e) { - // 线程中断异常 - Thread.currentThread().interrupt(); // 恢复中断状态 - throw new RuntimeException("请求被中断:" + e.getMessage(), e); + } catch (Exception e) { + throw new RuntimeException(e); } } - - /** + * @param request 请求参数 + * @return 识别结果WordsResult * @description 身份证OCR识别 * @author 铁憨憨 * @date 2025/7/18 16:53 - * @param request 请求参数 - * @return 识别结果WordsResult **/ public WordsResult idCardOCR(OcrReq request) { // 获取AccessToken,为空直接抛异常 @@ -117,41 +95,28 @@ public class BaiDuOCR { throw new RuntimeException("获取访问令牌失败:token为空"); } - // 构建请求URL - String requestUrl = String.format(BASE_URL, accessToken); - try { // 准备请求体(序列化失败抛异常) String requestBody = objectMapper.writeValueAsString(request); if (requestBody == null) { throw new RuntimeException("请求参数序列化失败"); } - - // 构建HTTP请求 - HttpRequest httpRequest = HttpRequest.newBuilder() - .uri(URI.create(requestUrl)) - .header("Content-Type", "application/x-www-form-urlencoded") - .POST(HttpRequest.BodyPublishers.ofString(requestBody)) - .build(); - - // 发送请求并获取响应 - HttpResponse response = httpClient.send( - httpRequest, - HttpResponse.BodyHandlers.ofString() - ); - - // 处理响应状态码(非200直接抛异常) - if (response.statusCode() != 200) { - throw new RuntimeException("接口请求失败,状态码:" + response.statusCode() + ",响应信息:" + response.body()); + String param = "id_card_side=" + request.getIdCardSide(); + if (request.getImage() != null) { + String imgParam = URLEncoder.encode(request.getImage(), StandardCharsets.UTF_8); + param = param + "&image=" + imgParam; + } else if (request.getUrl() != null) { + param = param + "&url=" + request.getUrl(); + } else { + throw new RuntimeException("请传入图片或图片URL"); } - - // 处理响应内容(去除空格并解析) - String responseBody = response.body().replaceAll("\\s+", ""); - IDCardInfo idCardInfo = objectMapper.readValue(responseBody, IDCardInfo.class); + // 构建HTTP请求 + String result = HttpUtil.post(BASE_URL, accessToken, param); + // 处理响应内容 + IDCardInfo idCardInfo = JSON.parseObject(result, IDCardInfo.class); if (idCardInfo == null) { throw new RuntimeException("响应结果解析失败"); } - // 检查身份证状态(状态异常由validateImageStatus抛异常) validateImageStatus(idCardInfo.getImageStatus()); @@ -166,20 +131,18 @@ public class BaiDuOCR { } catch (IOException e) { // IO异常(序列化、网络IO等) throw new RuntimeException("IO处理异常:" + e.getMessage(), e); - } catch (InterruptedException e) { - // 线程中断异常 - Thread.currentThread().interrupt(); // 恢复中断状态 - throw new RuntimeException("请求被中断:" + e.getMessage(), e); + } catch (Exception e) { + throw new RuntimeException(e); } } /** + * @param imageStatus 图片状态 + * @throws RuntimeException 当状态不正常时抛出运行时异常 * @description 验证身份证图片状态 * @author 铁憨憨 * @date 2025/7/18 17:08 - * @param imageStatus 图片状态 - * @throws RuntimeException 当状态不正常时抛出运行时异常 **/ private void validateImageStatus(String imageStatus) { if (imageStatus == null || "normal".equals(imageStatus)) { diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/common/utils/baiduUtil/HttpUtil.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/common/utils/baiduUtil/HttpUtil.java new file mode 100644 index 0000000..1ae094d --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/common/utils/baiduUtil/HttpUtil.java @@ -0,0 +1,77 @@ +package org.dromara.common.utils.baiduUtil; + +import java.io.BufferedReader; +import java.io.DataOutputStream; +import java.io.InputStreamReader; +import java.net.HttpURLConnection; +import java.net.URL; +import java.util.List; +import java.util.Map; + +/** + * http 工具类 + */ +public class HttpUtil { + + public static String post(String requestUrl, String accessToken, String params) + throws Exception { + String contentType = "application/x-www-form-urlencoded"; + return HttpUtil.post(requestUrl, accessToken, contentType, params); + } + + public static String post(String requestUrl, String accessToken, String contentType, String params) + throws Exception { + String encoding = "UTF-8"; + if (requestUrl.contains("nlp")) { + encoding = "GBK"; + } + return HttpUtil.post(requestUrl, accessToken, contentType, params, encoding); + } + + public static String post(String requestUrl, String accessToken, String contentType, String params, String encoding) + throws Exception { + String url = requestUrl + "?access_token=" + accessToken; + return HttpUtil.postGeneralUrl(url, contentType, params, encoding); + } + + public static String postGeneralUrl(String generalUrl, String contentType, String params, String encoding) + throws Exception { + URL url = new URL(generalUrl); + // 打开和URL之间的连接 + HttpURLConnection connection = (HttpURLConnection) url.openConnection(); + connection.setRequestMethod("POST"); + // 设置通用的请求属性 + connection.setRequestProperty("Content-Type", contentType); + connection.setRequestProperty("Connection", "Keep-Alive"); + connection.setUseCaches(false); + connection.setDoOutput(true); + connection.setDoInput(true); + + // 得到请求的输出流对象 + DataOutputStream out = new DataOutputStream(connection.getOutputStream()); + out.write(params.getBytes(encoding)); + out.flush(); + out.close(); + + // 建立实际的连接 + connection.connect(); + // 获取所有响应头字段 + Map> headers = connection.getHeaderFields(); + // 遍历所有的响应头字段 + for (String key : headers.keySet()) { + System.err.println(key + "--->" + headers.get(key)); + } + // 定义 BufferedReader输入流来读取URL的响应 + BufferedReader in = null; + in = new BufferedReader( + new InputStreamReader(connection.getInputStream(), encoding)); + String result = ""; + String getLine; + while ((getLine = in.readLine()) != null) { + result += getLine; + } + in.close(); + System.err.println("result:" + result); + return result; + } +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/common/utils/baiduUtil/entity/AccessTokenResponse.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/common/utils/baiduUtil/entity/AccessTokenResponse.java index d0fec39..c17404b 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/common/utils/baiduUtil/entity/AccessTokenResponse.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/common/utils/baiduUtil/entity/AccessTokenResponse.java @@ -1,10 +1,11 @@ package org.dromara.common.utils.baiduUtil.entity; -import lombok.AllArgsConstructor; import lombok.Data; -import lombok.NoArgsConstructor; import lombok.experimental.Accessors; +import java.io.Serial; +import java.io.Serializable; + /** * @Author 铁憨憨 * @Date 2025/7/18 9:56 @@ -12,7 +13,10 @@ import lombok.experimental.Accessors; */ @Data @Accessors(chain = true) -public class AccessTokenResponse { +public class AccessTokenResponse implements Serializable { + @Serial + private static final long serialVersionUID = 5429166046159819095L; + private String accessToken; private int expiresIn; private String scope; diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/common/utils/baiduUtil/entity/ocr/BankData.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/common/utils/baiduUtil/entity/ocr/BankData.java index e74f846..8732c05 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/common/utils/baiduUtil/entity/ocr/BankData.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/common/utils/baiduUtil/entity/ocr/BankData.java @@ -1,13 +1,14 @@ package org.dromara.common.utils.baiduUtil.entity.ocr; -import com.alibaba.fastjson2.annotation.JSONField; +import com.alibaba.fastjson.annotation.JSONField; import lombok.Data; +import org.dromara.system.domain.vo.SysOssVo; /** * @Author 铁憨憨 * @Date 2025/7/18 10:58 * @Version 1.0 - * + *

* 银行卡具体信息 */ @Data @@ -22,18 +23,23 @@ import lombok.Data; public class BankData { - @JSONField(name = "bank_card_number",label = "银行卡卡号") + @JSONField(name = "bank_card_number", label = "银行卡卡号") private String bankCardNumber; - @JSONField(name = "valid_date",label = "有效期") + @JSONField(name = "valid_date", label = "有效期") private String validDate; - @JSONField(name = "bank_card_type",label = "银行卡类型,0:不能识别; 1:借记卡; 2:贷记卡(原信用卡大部分为贷记卡); 3:准贷记卡; 4:预付费卡") + @JSONField(name = "bank_card_type", label = "银行卡类型,0:不能识别; 1:借记卡; 2:贷记卡(原信用卡大部分为贷记卡); 3:准贷记卡; 4:预付费卡") private int bankCardType; - @JSONField(name = "bank_name",label = "银行名,不能识别时为空") + @JSONField(name = "bank_name", label = "银行名,不能识别时为空") private String bankName; - @JSONField(name = "holder_name",label = "持卡人姓名,不能识别时为空") + @JSONField(name = "holder_name", label = "持卡人姓名,不能识别时为空") private String holderName; + + /** + * 银行卡图片信息 + */ + private SysOssVo image; } diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/common/utils/baiduUtil/entity/ocr/Field.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/common/utils/baiduUtil/entity/ocr/Field.java index ebc77a3..6a0b539 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/common/utils/baiduUtil/entity/ocr/Field.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/common/utils/baiduUtil/entity/ocr/Field.java @@ -1,5 +1,6 @@ package org.dromara.common.utils.baiduUtil.entity.ocr; +import com.alibaba.fastjson.annotation.JSONField; import lombok.Data; /** @@ -11,6 +12,8 @@ import lombok.Data; */ @Data public class Field { + @JSONField(name = "location") private Location location; // 位置信息 + @JSONField(name = "words") private String words; // 识别文字 } diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/common/utils/baiduUtil/entity/ocr/IDCardInfo.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/common/utils/baiduUtil/entity/ocr/IDCardInfo.java index fbd30a2..4ba8a1a 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/common/utils/baiduUtil/entity/ocr/IDCardInfo.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/common/utils/baiduUtil/entity/ocr/IDCardInfo.java @@ -1,6 +1,6 @@ package org.dromara.common.utils.baiduUtil.entity.ocr; -import com.alibaba.fastjson2.annotation.JSONField; +import com.alibaba.fastjson.annotation.JSONField; import lombok.Data; /** diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/common/utils/baiduUtil/entity/ocr/Location.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/common/utils/baiduUtil/entity/ocr/Location.java index fc61151..d0fc638 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/common/utils/baiduUtil/entity/ocr/Location.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/common/utils/baiduUtil/entity/ocr/Location.java @@ -1,18 +1,26 @@ package org.dromara.common.utils.baiduUtil.entity.ocr; +import com.alibaba.fastjson.annotation.JSONField; import lombok.Data; /** * @Author 铁憨憨 * @Date 2025/7/18 10:56 * @Version 1.0 - * + *

* 位置信息(坐标) */ @Data public class Location { - private int top; // 顶部坐标 - private int left; // 左侧坐标 - private int width; // 宽度 - private int height; // 高度 + @JSONField(name = "top") + private int top; + + @JSONField(name = "left") + private int left; + + @JSONField(name = "width") + private int width; + + @JSONField(name = "height") + private int height; } diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/common/utils/baiduUtil/entity/ocr/OcrReq.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/common/utils/baiduUtil/entity/ocr/OcrReq.java index a785503..5bf4aff 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/common/utils/baiduUtil/entity/ocr/OcrReq.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/common/utils/baiduUtil/entity/ocr/OcrReq.java @@ -1,24 +1,30 @@ package org.dromara.common.utils.baiduUtil.entity.ocr; -import com.alibaba.fastjson2.annotation.JSONField; +import com.fasterxml.jackson.annotation.JsonProperty; import lombok.Data; +import java.io.Serial; +import java.io.Serializable; + /** * @Author 铁憨憨 * @Date 2025/7/18 10:47 * @Version 1.0 - * + *

* OCR请求参数(身份证/银行卡通用) */ @Data -public class OcrReq { +public class OcrReq implements Serializable { + @Serial + private static final long serialVersionUID = -8670697823104813789L; + private String image; // 图像base64(与url二选一) private String url; // 图像URL(与image二选一) - @JSONField(name = "id_card_side") + @JsonProperty("id_card_side") private String idCardSide; // 身份证正反面(front/back,银行卡无需) - @JSONField(name = "detect_photo") + @JsonProperty("detect_photo") private boolean detectPhoto;// 是是否开启银行卡质量类型(清晰模糊、边框/四角不完整)检测功能,默认不开启,即:false。 } diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/common/utils/baiduUtil/entity/ocr/Result.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/common/utils/baiduUtil/entity/ocr/Result.java index b92420c..0ca7d5f 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/common/utils/baiduUtil/entity/ocr/Result.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/common/utils/baiduUtil/entity/ocr/Result.java @@ -1,6 +1,6 @@ package org.dromara.common.utils.baiduUtil.entity.ocr; -import com.alibaba.fastjson2.annotation.JSONField; +import com.alibaba.fastjson.annotation.JSONField; import lombok.Data; /** diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/common/utils/baiduUtil/entity/ocr/WordsResult.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/common/utils/baiduUtil/entity/ocr/WordsResult.java index 43de74b..482c237 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/common/utils/baiduUtil/entity/ocr/WordsResult.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/common/utils/baiduUtil/entity/ocr/WordsResult.java @@ -1,33 +1,38 @@ package org.dromara.common.utils.baiduUtil.entity.ocr; -import com.alibaba.fastjson2.annotation.JSONField; +import com.alibaba.fastjson.annotation.JSONField; import lombok.Data; +import org.dromara.system.domain.vo.SysOssVo; /** * @Author 铁憨憨 * @Date 2025/7/18 10:50 * @Version 1.0 - * + *

* 身份证识别字段集合 */ @Data public class WordsResult { @JSONField(name = "姓名") - private Field name; // 姓名 + private Field name; + @JSONField(name = "民族") - private Field nation; // 民族 + private Field nation; + @JSONField(name = "住址") - private Field address; // 住址 + private Field address; + @JSONField(name = "公民身份号码") - private Field citizenIdentification; // 身份证号 + private Field citizenIdentification; + @JSONField(name = "出生") - private Field birth; // 出生日期 + private Field birth; + @JSONField(name = "性别") - private Field gender; // 性别 - @JSONField(name = "失效日期") - private Field expirationDate; // 失效日期 - @JSONField(name = "签发机关") - private Field issuingAuthority; // 签发机关 - @JSONField(name = "签发日期") - private Field issueDate; // 签发日期 + private Field gender; + + /** + * 身份证图片信息 + */ + private SysOssVo image; } diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/contractor/controller/AppController.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/contractor/controller/AppController.java deleted file mode 100644 index 6b33b02..0000000 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/contractor/controller/AppController.java +++ /dev/null @@ -1,40 +0,0 @@ -package org.dromara.contractor.controller; - -import cn.dev33.satoken.annotation.SaCheckPermission; -import lombok.RequiredArgsConstructor; -import org.dromara.common.core.domain.R; -import org.dromara.common.core.validate.EditGroup; -import org.dromara.common.idempotent.annotation.RepeatSubmit; -import org.dromara.common.log.annotation.Log; -import org.dromara.common.log.enums.BusinessType; -import org.dromara.common.web.core.BaseController; -import org.dromara.contractor.domain.dto.constructionuser.AppRealNameAuthenticationReq; -import org.dromara.contractor.domain.dto.constructionuser.SubConstructionUserUpdateClockReq; -import org.dromara.contractor.service.ISubConstructionUserService; -import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.*; - -/** - * @Author 铁憨憨 - * @Date 2025/7/23 16:48 - * @Version 1.0 - */ - -@Validated -@RequiredArgsConstructor -@RestController -@RequestMapping("/contractor/app") -public class AppController extends BaseController { - - private final ISubConstructionUserService constructionUserService; - - @Log(title = "施工人员-实名认证", businessType = BusinessType.UPDATE) - @RepeatSubmit() - @PutMapping("/realNameAuthentication") - public R realNameAuthentication(@RequestBody AppRealNameAuthenticationReq req) { -// return toAjax(constructionUserService.updateClock(req)); - return toAjax(constructionUserService.realNameAuthentication(req)); - } - - -} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/contractor/controller/app/SubConstructionUserAppController.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/contractor/controller/app/SubConstructionUserAppController.java new file mode 100644 index 0000000..b6deda7 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/contractor/controller/app/SubConstructionUserAppController.java @@ -0,0 +1,77 @@ +package org.dromara.contractor.controller.app; + +import jakarta.annotation.Resource; +import org.dromara.common.core.domain.R; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.satoken.utils.LoginHelper; +import org.dromara.common.utils.baiduUtil.entity.ocr.BankData; +import org.dromara.common.utils.baiduUtil.entity.ocr.WordsResult; +import org.dromara.contractor.domain.SubConstructionUser; +import org.dromara.contractor.domain.dto.constructionuser.SubConstructionUserAuthenticationReq; +import org.dromara.contractor.domain.vo.constructionuser.SubConstructionUserVo; +import org.dromara.contractor.service.ISubConstructionUserService; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +/** + * 施工人员app + * + * @author lilemy + * @date 2025-07-23 18:44 + */ +@Validated +@RestController +@RequestMapping("/app/contractor/constructionUser") +public class SubConstructionUserAppController { + + @Resource + private ISubConstructionUserService constructionUserService; + + /** + * 获取当前登录用户实名信息 + */ + @GetMapping("/loginUser") + public R getLoginUserInfo() { + SubConstructionUser constructionUser = constructionUserService.getBySysUserId(LoginHelper.getUserId()); + return R.ok(constructionUserService.getVo(constructionUser)); + } + + /** + * 根据身份证图片获取身份证信息 + */ + @Log(title = "施工人员", businessType = BusinessType.OTHER) + @PostMapping("/idCard") + public R getIdCardMessage(@RequestParam("file") MultipartFile file, String idCardSide) { + return R.ok(constructionUserService.getIdCardMessageByPic(file, idCardSide)); + } + + /** + * 根据银行卡图片获取银行卡信息 + */ + @Log(title = "施工人员", businessType = BusinessType.OTHER) + @PostMapping("/bankCard") + public R getBankCardMessage(@RequestParam("file") MultipartFile file) { + return R.ok(constructionUserService.getBankCardMessageByPic(file)); + } + + /** + * 施工人员实名认证 + */ + @Log(title = "施工人员", businessType = BusinessType.INSERT) + @PostMapping("/authentication") + public R insertByAuthentication(@RequestParam("file") MultipartFile file, @Validated SubConstructionUserAuthenticationReq req) { + return R.ok(constructionUserService.insertByAuthentication(file, req)); + } + + /** + * 施工人员人脸比对 + */ + @Log(title = "施工人员", businessType = BusinessType.OTHER) + @PostMapping("/face/comparison") + public R faceComparison(@RequestParam("file") MultipartFile file) { + return R.ok(constructionUserService.faceComparison(file)); + } + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/contractor/domain/SubConstructionUser.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/contractor/domain/SubConstructionUser.java index 6d73f44..53e0454 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/contractor/domain/SubConstructionUser.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/contractor/domain/SubConstructionUser.java @@ -34,6 +34,11 @@ public class SubConstructionUser extends BaseEntity { */ private String facePic; + /** + * 系统用户id + */ + private Long userId; + /** * 人员姓名 */ diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/contractor/domain/dto/constructionuser/SubConstructionUserAuthenticationReq.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/contractor/domain/dto/constructionuser/SubConstructionUserAuthenticationReq.java new file mode 100644 index 0000000..60d72f1 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/contractor/domain/dto/constructionuser/SubConstructionUserAuthenticationReq.java @@ -0,0 +1,150 @@ +package org.dromara.contractor.domain.dto.constructionuser; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Pattern; +import lombok.Data; +import org.dromara.common.core.constant.RegexConstants; + +import java.io.Serial; +import java.io.Serializable; + +/** + * @author lilemy + * @date 2025-07-24 10:48 + */ +@Data +public class SubConstructionUserAuthenticationReq implements Serializable { + + @Serial + private static final long serialVersionUID = 1001024234468070802L; + + /** + * 人员姓名 + */ + @NotBlank(message = "人员姓名不能为空") + private String userName; + + /** + * 项目id + */ + @NotNull(message = "项目id不能为空") + private Long projectId; + + /** + * 分包公司id + */ + @NotNull(message = "分包公司id不能为空") + private Long contractorId; + + /** + * 联系电话 + */ + @NotBlank(message = "联系电话不能为空") + @Pattern(regexp = RegexConstants.MOBILE, message = "手机号格式不正确") + private String phone; + + /** + * 0:保密 1:男 2女 + */ + @NotBlank(message = "性别不能为空") + private String sex; + + /** + * 民族 + */ + @NotBlank(message = "民族不能为空") + private String nation; + + /** + * 身份证正面图片 + */ + @NotBlank(message = "身份证正面图片不能为空") + private String sfzFrontPic; + + /** + * 身份证反面图片 + */ + @NotBlank(message = "身份证反面图片不能为空") + private String sfzBackPic; + + /** + * 身份证号码 + */ + @NotBlank(message = "身份证号码不能为空") + private String sfzNumber; + + /** + * 身份证有效开始期 + */ + @NotBlank(message = "身份证有效开始期不能为空") + private String sfzStart; + + /** + * 身份证有效结束期 + */ + @NotBlank(message = "身份证有效结束期不能为空") + private String sfzEnd; + + /** + * 身份证地址 + */ + @NotBlank(message = "身份证地址不能为空") + private String sfzSite; + + /** + * 身份证出生日期 + */ + @NotBlank(message = "身份证出生日期不能为空") + private String sfzBirth; + + /** + * 籍贯 + */ + @NotBlank(message = "籍贯不能为空") + private String nativePlace; + + /** + * 银行卡图片 + */ + @NotBlank(message = "银行卡图片不能为空") + private String yhkPic; + + /** + * 银行卡号 + */ + @NotBlank(message = "银行卡号不能为空") + private String yhkNumber; + + /** + * 开户行 + */ + private String yhkOpeningBank; + + /** + * 持卡人 + */ + private String yhkCardholder; + + /** + * 工种 + */ + @NotBlank(message = "工种不能为空") + private String typeOfWork; + + /** + * 工资计量单位 + */ + private String wageMeasureUnit; + + /** + * 特种工作证图片 + */ + private String specialWorkPic; + + /** + * 备注 + */ + private String remark; + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/contractor/domain/enums/SubConstructionUserRoleEnum.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/contractor/domain/enums/SubConstructionUserRoleEnum.java new file mode 100644 index 0000000..e32ddc2 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/contractor/domain/enums/SubConstructionUserRoleEnum.java @@ -0,0 +1,24 @@ +package org.dromara.contractor.domain.enums; + +import lombok.Getter; + +/** + * @author lilemy + * @date 2025-07-24 15:52 + */ +@Getter +public enum SubConstructionUserRoleEnum { + + NORMAL("普通用户", "1"), + ADMIN("管理员", "2"); + + private final String text; + + private final String value; + + SubConstructionUserRoleEnum(String text, String value) { + this.text = text; + this.value = value; + } + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/contractor/service/ISubConstructionUserService.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/contractor/service/ISubConstructionUserService.java index bf2ca07..efbdddf 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/contractor/service/ISubConstructionUserService.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/contractor/service/ISubConstructionUserService.java @@ -5,14 +5,16 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.IService; import org.dromara.common.mybatis.core.page.PageQuery; import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.utils.baiduUtil.entity.ocr.BankData; +import org.dromara.common.utils.baiduUtil.entity.ocr.WordsResult; import org.dromara.contractor.domain.SubConstructionUser; import org.dromara.contractor.domain.dto.constructionuser.*; -import org.dromara.contractor.domain.vo.constructionuser.SubConstructionUserVo; import org.dromara.contractor.domain.exportvo.BusConstructionUserExportVo; import org.dromara.contractor.domain.vo.constructionuser.SubConstructionUserAttendanceMonthVo; import org.dromara.contractor.domain.vo.constructionuser.SubConstructionUserAttendanceTotalVo; import org.dromara.contractor.domain.vo.constructionuser.SubConstructionUserGisVo; -import org.springframework.web.bind.annotation.RequestBody; +import org.dromara.contractor.domain.vo.constructionuser.SubConstructionUserVo; +import org.springframework.web.multipart.MultipartFile; import java.util.Collection; import java.util.List; @@ -174,9 +176,46 @@ public interface ISubConstructionUserService extends IService 0) { + throw new ServiceException("当前系统用户已关联施工人员信息"); + } + user.setUserId(userId); + // 保存施工人员信息 + boolean save = this.save(user); + if (!save) { + throw new ServiceException("施工人员信息保存失败"); + } + return user.getId(); + } + + /** + * 人脸识别 + * + * @param file 图片文件 + * @return 是否匹配成功 + */ + @Override + public Boolean faceComparison(MultipartFile file) { + if (file == null) { + throw new ServiceException("请上传图片", HttpStatus.BAD_REQUEST); + } + String reqBase64; + try { + // 获取文件字节数组 + byte[] bytes = file.getBytes(); + // Base64 编码 + reqBase64 = URLEncoder.encode(Base64.getEncoder().encodeToString(bytes), StandardCharsets.UTF_8); + } catch (IOException e) { + throw new ServiceException("图片转换失败,请重新上传"); + } + HumanFaceReq request = new HumanFaceReq(); + request.setImage(reqBase64); + Long userId = LoginHelper.getUserId(); + SubConstructionUser constructionUser = this.getById(userId); + if (constructionUser == null || StringUtils.isBlank(constructionUser.getFacePic())) { + throw new ServiceException("未进行实名认证"); + } + String facePic = constructionUser.getFacePic(); + SysOssVo sysOssVo = ossService.getById(Long.parseLong(facePic)); + if (sysOssVo == null) { + throw new ServiceException("未进行实名认证"); + } + // 获取文件输入流 + OssClient storage = OssFactory.instance(sysOssVo.getService()); + String path = sysOssVo.getUrl(); + String faceBase64; + try (InputStream is = storage.getObjectContent(path)) { + byte[] bytes = IoUtil.readBytes(is); + // Base64 编码 + faceBase64 = URLEncoder.encode(Base64.getEncoder().encodeToString(bytes), StandardCharsets.UTF_8); + } catch (IOException e) { + // 针对单个文件处理异常,可以选择记录日志或终止处理 + throw new OssException("处理文件[" + path + "]失败,错误信息: " + e.getMessage()); + } + HumanFaceReq faceReq = new HumanFaceReq(); + faceReq.setImage(faceBase64); + List list = List.of(request, faceReq); + double comparison = baiDuFace.comparison(list); + return comparison > 80; + } + + /** + * 根据系统用户id查询施工人员 + * + * @param sysUserId 系统用户id + * @return 施工人员 + */ + @Override + public SubConstructionUser getBySysUserId(Long sysUserId) { + SubConstructionUser constructionUser = this.lambdaQuery() + .eq(SubConstructionUser::getUserId, sysUserId) + .one(); + if (constructionUser == null) { + throw new ServiceException("实名认证信息不存在", HttpStatus.NOT_FOUND); + } + return constructionUser; } } diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/controller/app/BusAttendanceAppController.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/controller/app/BusAttendanceAppController.java new file mode 100644 index 0000000..544878f --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/controller/app/BusAttendanceAppController.java @@ -0,0 +1,61 @@ +package org.dromara.project.controller.app; + +import jakarta.annotation.Resource; +import org.dromara.common.core.domain.R; +import org.dromara.common.satoken.utils.LoginHelper; +import org.dromara.contractor.domain.SubConstructionUser; +import org.dromara.contractor.service.ISubConstructionUserService; +import org.dromara.project.domain.dto.attendance.BusAttendanceByDayReq; +import org.dromara.project.domain.dto.attendance.BusAttendanceByMonthReq; +import org.dromara.project.domain.dto.attendance.BusAttendanceMonthByUserIdReq; +import org.dromara.project.domain.vo.attendance.BusAttendanceMonthByUserIdVo; +import org.dromara.project.domain.vo.attendance.BusAttendanceVo; +import org.dromara.project.service.IBusAttendanceService; +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.util.List; + +/** + * 考勤app + * + * @author lilemy + * @date 2025-07-24 11:53 + */ +@Validated +@RestController +@RequestMapping("/app/project/attendance") +public class BusAttendanceAppController { + + @Resource + private IBusAttendanceService attendanceService; + + @Resource + private ISubConstructionUserService constructionUserService; + + /** + * 获取当前登录用户的考勤列表 + * + * @param req 查询参数 + * @return 考勤列表 + */ + @GetMapping("/list/loginUser") + public R> listLoginUser(BusAttendanceByDayReq req) { + return R.ok(attendanceService.getDayByUserId(LoginHelper.getUserId(), req.getClockDate())); + } + + /** + * 获取当前登录用户月份考勤列表 + */ + @GetMapping("/list/month/loginUser") + public R> listAttendanceMonthListByUserId(BusAttendanceByMonthReq req) { + Long userId = LoginHelper.getUserId(); + SubConstructionUser constructionUser = constructionUserService.getBySysUserId(userId); + BusAttendanceMonthByUserIdReq dto = new BusAttendanceMonthByUserIdReq(); + dto.setUserId(constructionUser.getId()); + dto.setClockMonth(req.getClockMonth()); + return R.ok(attendanceService.listAttendanceMonthListByUserId(dto)); + } +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/controller/app/BusLeaveAppController.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/controller/app/BusLeaveAppController.java new file mode 100644 index 0000000..d0b48d4 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/controller/app/BusLeaveAppController.java @@ -0,0 +1,42 @@ +package org.dromara.project.controller.app; + +import jakarta.annotation.Resource; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.satoken.utils.LoginHelper; +import org.dromara.contractor.domain.SubConstructionUser; +import org.dromara.contractor.service.ISubConstructionUserService; +import org.dromara.project.domain.dto.leave.BusLeaveQueryReq; +import org.dromara.project.domain.vo.leave.BusLeaveVo; +import org.dromara.project.service.IBusLeaveService; +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; + +/** + * @author lilemy + * @date 2025-07-24 14:51 + */ +@Validated +@RestController +@RequestMapping("/app/project/leave") +public class BusLeaveAppController { + + @Resource + private IBusLeaveService leaveService; + + @Resource + private ISubConstructionUserService constructionUserService; + + /** + * 查询当前登录用户请假申请列表 + */ + @GetMapping("/list/loginUser") + public TableDataInfo listByLoginUser(BusLeaveQueryReq req, PageQuery pageQuery) { + SubConstructionUser constructionUser = constructionUserService.getBySysUserId(LoginHelper.getUserId()); + req.setUserId(constructionUser.getId()); + return leaveService.queryPageList(req, pageQuery); + } + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/controller/app/BusProjectAppController.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/controller/app/BusProjectAppController.java new file mode 100644 index 0000000..ded4be9 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/controller/app/BusProjectAppController.java @@ -0,0 +1,33 @@ +package org.dromara.project.controller.app; + +import jakarta.annotation.Resource; +import org.dromara.common.core.domain.R; +import org.dromara.project.domain.vo.project.BusProjectContractorListVo; +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.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +/** + * @author lilemy + * @date 2025-07-24 15:14 + */ +@Validated +@RestController +@RequestMapping("/app/project/project") +public class BusProjectAppController { + + @Resource + private IBusProjectService projectService; + + /** + * 查询项目以及项目下的分包公司列表 + */ + @GetMapping("/list/project/contractorList") + public R> listProjectContractorList() { + return R.ok(projectService.queryProjectContractorList()); + } +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/controller/app/BusReissueCardAppController.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/controller/app/BusReissueCardAppController.java new file mode 100644 index 0000000..8eb3af5 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/controller/app/BusReissueCardAppController.java @@ -0,0 +1,41 @@ +package org.dromara.project.controller.app; + +import jakarta.annotation.Resource; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.satoken.utils.LoginHelper; +import org.dromara.contractor.domain.SubConstructionUser; +import org.dromara.contractor.service.ISubConstructionUserService; +import org.dromara.project.domain.dto.reissuecard.BusReissueCardQueryReq; +import org.dromara.project.domain.vo.reissuecard.BusReissueCardVo; +import org.dromara.project.service.IBusReissueCardService; +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; + +/** + * @author lilemy + * @date 2025-07-24 14:51 + */ +@Validated +@RestController +@RequestMapping("/app/project/reissueCard") +public class BusReissueCardAppController { + + @Resource + private IBusReissueCardService reissueCardService; + + @Resource + private ISubConstructionUserService constructionUserService; + + /** + * 查询当前登录用户补卡申请列表 + */ + @GetMapping("/list/loginUser") + public TableDataInfo listByLoginUser(BusReissueCardQueryReq req, PageQuery pageQuery) { + SubConstructionUser constructionUser = constructionUserService.getBySysUserId(LoginHelper.getUserId()); + req.setUserId(constructionUser.getId()); + return reissueCardService.queryPageList(req, pageQuery); + } +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/domain/BusAttendance.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/domain/BusAttendance.java index 4a00c5d..031bd0c 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/domain/BusAttendance.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/domain/BusAttendance.java @@ -60,7 +60,7 @@ public class BusAttendance extends BaseEntity { private Date clockDate; /** - * 打卡状态(1正常,2迟到,3早退,4缺勤,5补卡) + * 打卡状态 */ private String clockStatus; diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/domain/BusLeave.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/domain/BusLeave.java index fcab96a..5a41103 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/domain/BusLeave.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/domain/BusLeave.java @@ -124,6 +124,11 @@ public class BusLeave extends BaseEntity { */ private Long teamId; + /** + * 分包公司id + */ + private Long contractorId; + /** * 备注 */ diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/domain/dto/attendance/BusAttendanceByDayReq.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/domain/dto/attendance/BusAttendanceByDayReq.java new file mode 100644 index 0000000..ac9a9ff --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/domain/dto/attendance/BusAttendanceByDayReq.java @@ -0,0 +1,25 @@ +package org.dromara.project.domain.dto.attendance; + +import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + +/** + * @author lilemy + * @date 2025-07-24 12:28 + */ +@Data +public class BusAttendanceByDayReq implements Serializable { + + @Serial + private static final long serialVersionUID = 1800816047235448533L; + + /** + * 打卡日期 + */ + @DateTimeFormat(pattern = "yyyy-MM-dd") + private Date clockDate; +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/domain/dto/attendance/BusAttendanceByMonthReq.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/domain/dto/attendance/BusAttendanceByMonthReq.java new file mode 100644 index 0000000..bd63741 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/domain/dto/attendance/BusAttendanceByMonthReq.java @@ -0,0 +1,22 @@ +package org.dromara.project.domain.dto.attendance; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** + * @author lilemy + * @date 2025-07-24 13:51 + */ +@Data +public class BusAttendanceByMonthReq implements Serializable { + + @Serial + private static final long serialVersionUID = 4257724767091558549L; + + /** + * 打卡月份 + */ + private String clockMonth; +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/domain/dto/leave/BusLeaveGangerReviewReq.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/domain/dto/leave/BusLeaveGangerReviewReq.java new file mode 100644 index 0000000..7e74ff9 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/domain/dto/leave/BusLeaveGangerReviewReq.java @@ -0,0 +1,41 @@ +package org.dromara.project.domain.dto.leave; + +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** + * @author lilemy + * @date 2025-07-24 15:28 + */ +@Data +public class BusLeaveGangerReviewReq implements Serializable { + + @Serial + private static final long serialVersionUID = 6403690469809708710L; + + /** + * 主键id + */ + @NotNull(message = "主键不能为空") + private Long id; + + /** + * 管理员意见(1未读 2同意 3拒绝) + */ + @NotNull(message = "管理员意见不能为空") + private String gangerOpinion; + + /** + * 管理员说明 + */ + private String gangerExplain; + + /** + * 备注 + */ + private String remark; + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/domain/vo/leave/BusLeaveVo.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/domain/vo/leave/BusLeaveVo.java index f0cfb95..298a559 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/domain/vo/leave/BusLeaveVo.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/domain/vo/leave/BusLeaveVo.java @@ -89,6 +89,11 @@ public class BusLeaveVo implements Serializable { */ private Long projectId; + /** + * 分包公司id + */ + private Long contractorId; + /** * 班组长id */ diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/service/IBusAttendanceService.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/service/IBusAttendanceService.java index c0a35a3..7ee0598 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/service/IBusAttendanceService.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/service/IBusAttendanceService.java @@ -13,6 +13,7 @@ import org.dromara.project.domain.vo.attendance.BusAttendanceClockDateForTwoWeek import org.dromara.project.domain.vo.attendance.BusAttendanceMonthByUserIdVo; import org.dromara.project.domain.vo.attendance.BusAttendanceVo; +import java.util.Date; import java.util.List; /** @@ -96,4 +97,13 @@ public interface IBusAttendanceService extends IService { */ Page getVoPage(Page attendancePage); + /** + * 根据系统用户id和日期查询考勤 + * + * @param userId 用户id + * @param clockDate 日期 + * @return 考勤 + */ + List getDayByUserId(Long userId, Date clockDate); + } diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/service/IBusLeaveService.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/service/IBusLeaveService.java index c6d4d00..04620ae 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/service/IBusLeaveService.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/service/IBusLeaveService.java @@ -6,6 +6,7 @@ import com.baomidou.mybatisplus.extension.service.IService; import org.dromara.common.mybatis.core.page.PageQuery; import org.dromara.common.mybatis.core.page.TableDataInfo; import org.dromara.project.domain.BusLeave; +import org.dromara.project.domain.dto.leave.BusLeaveGangerReviewReq; import org.dromara.project.domain.dto.leave.BusLeaveManagerReviewReq; import org.dromara.project.domain.dto.leave.BusLeaveQueryReq; import org.dromara.project.domain.vo.leave.BusLeaveVo; @@ -46,6 +47,14 @@ public interface IBusLeaveService extends IService { */ List queryList(BusLeaveQueryReq req); + /** + * 班长审核施工人员请假申请 + * + * @param req 班长审核施工人员请假申请 + * @return 是否审核成功 + */ + Boolean gangerReview(BusLeaveGangerReviewReq req); + /** * 管理员审核施工人员请假申请 * diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/service/impl/BusAttendanceServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/service/impl/BusAttendanceServiceImpl.java index 2a792da..6690f9e 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/service/impl/BusAttendanceServiceImpl.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/service/impl/BusAttendanceServiceImpl.java @@ -19,18 +19,21 @@ import org.dromara.contractor.domain.SubConstructionUser; import org.dromara.contractor.service.ISubConstructionUserService; import org.dromara.project.domain.BusAttendance; import org.dromara.project.domain.BusProjectTeamMember; -import org.dromara.project.domain.enums.BusAttendanceClockStatusEnum; -import org.dromara.project.domain.enums.BusAttendanceCommuterEnum; -import org.dromara.project.domain.enums.BusAttendanceStatusEnum; import org.dromara.project.domain.dto.attendance.BusAttendanceMonthByUserIdReq; import org.dromara.project.domain.dto.attendance.BusAttendanceQueryReq; import org.dromara.project.domain.dto.attendance.BusAttendanceQueryTwoWeekReq; +import org.dromara.project.domain.enums.BusAttendanceClockStatusEnum; +import org.dromara.project.domain.enums.BusAttendanceCommuterEnum; +import org.dromara.project.domain.enums.BusAttendanceStatusEnum; import org.dromara.project.domain.vo.attendance.BusAttendanceClockDateForTwoWeekVo; import org.dromara.project.domain.vo.attendance.BusAttendanceListByDay; import org.dromara.project.domain.vo.attendance.BusAttendanceMonthByUserIdVo; import org.dromara.project.domain.vo.attendance.BusAttendanceVo; import org.dromara.project.mapper.BusAttendanceMapper; -import org.dromara.project.service.*; +import org.dromara.project.service.IBusAttendanceService; +import org.dromara.project.service.IBusConstructionBlacklistService; +import org.dromara.project.service.IBusProjectService; +import org.dromara.project.service.IBusProjectTeamMemberService; import org.springframework.beans.BeanUtils; import org.springframework.stereotype.Service; @@ -404,4 +407,24 @@ public class BusAttendanceServiceImpl extends ServiceImpl getDayByUserId(Long userId, Date clockDate) { + SubConstructionUser constructionUser = constructionUserService.getBySysUserId(userId); + // 当日期未指定时,默认为今天 + if (clockDate == null) { + clockDate = DateUtils.parseDateTime(FormatsType.YYYY_MM_DD, DateUtils.getDate()); + } + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(BusAttendance::getUserId, constructionUser.getId()) + .eq(BusAttendance::getClockDate, clockDate); + return this.list(queryWrapper).stream().map(this::getVo).toList(); + } + } diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/service/impl/BusLeaveServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/service/impl/BusLeaveServiceImpl.java index ceaff8c..96746f6 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/service/impl/BusLeaveServiceImpl.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/service/impl/BusLeaveServiceImpl.java @@ -12,19 +12,24 @@ import org.dromara.common.core.utils.ObjectUtils; import org.dromara.common.core.utils.StringUtils; import org.dromara.common.mybatis.core.page.PageQuery; import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.satoken.utils.LoginHelper; +import org.dromara.contractor.domain.SubConstructionUser; +import org.dromara.contractor.domain.enums.SubConstructionUserRoleEnum; +import org.dromara.contractor.service.ISubConstructionUserService; +import org.dromara.contractor.service.ISubContractorService; import org.dromara.project.domain.BusAttendance; import org.dromara.project.domain.BusLeave; import org.dromara.project.domain.BusProjectTeam; -import org.dromara.project.domain.enums.BusAttendanceClockStatusEnum; -import org.dromara.project.domain.enums.BusAttendanceCommuterEnum; -import org.dromara.project.domain.enums.BusOpinionStatusEnum; -import org.dromara.project.domain.enums.BusReviewStatusEnum; +import org.dromara.project.domain.BusProjectTeamMember; +import org.dromara.project.domain.dto.leave.BusLeaveGangerReviewReq; import org.dromara.project.domain.dto.leave.BusLeaveManagerReviewReq; import org.dromara.project.domain.dto.leave.BusLeaveQueryReq; +import org.dromara.project.domain.enums.*; import org.dromara.project.domain.vo.leave.BusLeaveVo; import org.dromara.project.mapper.BusLeaveMapper; import org.dromara.project.service.IBusAttendanceService; import org.dromara.project.service.IBusLeaveService; +import org.dromara.project.service.IBusProjectTeamMemberService; import org.dromara.project.service.IBusProjectTeamService; import org.springframework.beans.BeanUtils; import org.springframework.context.annotation.Lazy; @@ -48,6 +53,15 @@ public class BusLeaveServiceImpl extends ServiceImpl @Resource private IBusProjectTeamService projectTeamService; + @Resource + private IBusProjectTeamMemberService projectTeamMemberService; + + @Resource + private ISubConstructionUserService constructionUserService; + + @Resource + private ISubContractorService contractorService; + @Lazy @Resource private IBusAttendanceService attendanceService; @@ -88,6 +102,63 @@ public class BusLeaveServiceImpl extends ServiceImpl return baseMapper.selectVoList(lqw); } + /** + * 班长审核施工人员请假申请 + * + * @param req 班长审核施工人员请假申请 + * @return 是否审核成功 + */ + @Override + public Boolean gangerReview(BusLeaveGangerReviewReq req) { + Long id = req.getId(); + String gangerOpinion = req.getGangerOpinion(); + // 判断该请假记录是否存在 + BusLeave oldLeave = this.getById(id); + if (oldLeave == null) { + throw new ServiceException("施工人员请假申请不存在", HttpStatus.NOT_FOUND); + } + // 如果审核状态相同,则返回 + if (Objects.equals(gangerOpinion, oldLeave.getGangerOpinion())) { + throw new ServiceException("请勿重复操作", HttpStatus.BAD_REQUEST); + } + // 如果已经审核过,则返回 + if (BusOpinionStatusEnum.PASS.getValue().equals(oldLeave.getManagerOpinion())) { + throw new ServiceException("该请假已审核通过,请勿重复操作", HttpStatus.BAD_REQUEST); + } + // 获取当前用户实名信息 + SubConstructionUser constructionUser = constructionUserService.getBySysUserId(LoginHelper.getUserId()); + // 判断当前用户是否有权审核 + Long teamId = oldLeave.getTeamId(); + Long count = projectTeamMemberService.lambdaQuery() + .eq(BusProjectTeamMember::getProjectId, oldLeave.getProjectId()) + .eq(BusProjectTeamMember::getMemberId, constructionUser.getId()) + .eq(BusProjectTeamMember::getTeamId, teamId) + .eq(BusProjectTeamMember::getPostId, BusProjectTeamMemberPostEnum.FOREMAN.getValue()) + .count(); + if (count <= 0) { + throw new ServiceException("您无权审核该请假申请", HttpStatus.FORBIDDEN); + } + BusLeave newLeave = new BusLeave(); + newLeave.setId(id); + newLeave.setGangerOpinion(gangerOpinion); + newLeave.setGangerExplain(req.getGangerExplain()); + newLeave.setGangerTime(new Date()); + newLeave.setRemark(req.getRemark()); + boolean result = this.updateById(newLeave); + if (!result) { + throw new ServiceException("更新班长审核操作失败", HttpStatus.ERROR); + } + // 向分包管理员发送通知 + List constructionAdminUsers = constructionUserService.lambdaQuery() + .eq(SubConstructionUser::getContractorId, oldLeave.getContractorId()) + .eq(SubConstructionUser::getUserRole, SubConstructionUserRoleEnum.ADMIN.getValue()) + .list(); + if (CollUtil.isNotEmpty(constructionAdminUsers)){ + + } + return true; + } + /** * 管理员审核施工人员请假申请 * @@ -114,9 +185,20 @@ public class BusLeaveServiceImpl extends ServiceImpl throw new ServiceException("请等待班组长审核通过后再进行操作", HttpStatus.BAD_REQUEST); } // todo 判断当前用户是否为项目管理员 + // 判断是否为分包公司管理员 + Long contractorId = oldLeave.getContractorId(); + SubConstructionUser constructionUser = constructionUserService.lambdaQuery() + .eq(SubConstructionUser::getUserId, LoginHelper.getUserId()) + .eq(SubConstructionUser::getContractorId, contractorId) + .eq(SubConstructionUser::getUserRole, SubConstructionUserRoleEnum.ADMIN.getValue()) + .one(); + if (constructionUser == null) { + throw new ServiceException("您无权审核该请假申请", HttpStatus.FORBIDDEN); + } // 填充默认值,更新数据 BusLeave leave = new BusLeave(); leave.setId(id); + leave.setManagerId(constructionUser.getId()); leave.setManagerOpinion(managerOpinion); leave.setManagerExplain(req.getManagerExplain()); leave.setManagerTime(new Date()); diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysNotifications.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysNotifications.java new file mode 100644 index 0000000..dc9919b --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysNotifications.java @@ -0,0 +1,90 @@ +package org.dromara.system.domain; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + +/** + * 系统通知 + * + * @TableName sys_notifications + */ +@Data +@TableName(value = "sys_notifications") +public class SysNotifications implements Serializable { + + /** + * 主键ID + */ + @TableId(value = "id") + private Long id; + + /** + * 接收通知的用户ID + */ + private Long recipientId; + + /** + * 发送通知的用户ID(系统通知则为null) + */ + private Long senderId; + + /** + * 通知类型 + */ + private String type; + + /** + * 通知标题 + */ + private String title; + + /** + * 通知的主要内容 + */ + private String content; + + /** + * 通知状态(0未读 1已读) + */ + private String status; + + /** + * 用户读取通知的时间 + */ + private Date readAt; + + /** + * 点击通知后跳转的目标URL + */ + private String actionUrl; + + /** + * 通知附件 + */ + private String file; + + /** + * 备注 + */ + private String remark; + + /** + * 创建时间 + */ + private Date createTime; + + /** + * 更新时间 + */ + private Date updateTime; + + @Serial + @TableField(exist = false) + private static final long serialVersionUID = 1L; +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/dto/notifications/SysNotificationsCreateBatchReq.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/dto/notifications/SysNotificationsCreateBatchReq.java new file mode 100644 index 0000000..7d550db --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/dto/notifications/SysNotificationsCreateBatchReq.java @@ -0,0 +1,59 @@ +package org.dromara.system.domain.dto.notifications; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.List; + +/** + * @author lilemy + * @date 2025-07-24 16:35 + */ +@Data +public class SysNotificationsCreateBatchReq implements Serializable { + + @Serial + private static final long serialVersionUID = 7786269708778844999L; + + /** + * 接收通知的用户ID + */ + private List recipientIds; + + /** + * 发送通知的用户ID(系统通知则为null) + */ + private Long senderId; + + /** + * 通知类型 + */ + private String type; + + /** + * 通知标题 + */ + private String title; + + /** + * 通知的主要内容 + */ + private String content; + + /** + * 点击通知后跳转的目标URL + */ + private String actionUrl; + + /** + * 通知附件 + */ + private String file; + + /** + * 备注 + */ + private String remark; + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/dto/notifications/SysNotificationsQueryReq.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/dto/notifications/SysNotificationsQueryReq.java new file mode 100644 index 0000000..0030f86 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/dto/notifications/SysNotificationsQueryReq.java @@ -0,0 +1,48 @@ +package org.dromara.system.domain.dto.notifications; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** + * @author lilemy + * @date 2025-07-24 16:37 + */ +@Data +public class SysNotificationsQueryReq implements Serializable { + + @Serial + private static final long serialVersionUID = 2227178507874081416L; + + /** + * 接收通知的用户ID + */ + private Long recipientId; + + /** + * 发送通知的用户ID(系统通知则为null) + */ + private Long senderId; + + /** + * 通知类型 + */ + private String type; + + /** + * 通知标题 + */ + private String title; + + /** + * 通知的主要内容 + */ + private String content; + + /** + * 通知状态(0未读 1已读) + */ + private String status; + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysNotificationsVo.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysNotificationsVo.java new file mode 100644 index 0000000..0ee388b --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysNotificationsVo.java @@ -0,0 +1,80 @@ +package org.dromara.system.domain.vo; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; +import java.util.List; + +/** + * @author lilemy + * @date 2025-07-24 16:31 + */ +@Data +public class SysNotificationsVo implements Serializable { + + @Serial + private static final long serialVersionUID = -2755920219118735240L; + + /** + * 主键ID + */ + private Long id; + + /** + * 接收通知的用户ID + */ + private Long recipientId; + + /** + * 发送通知的用户ID(系统通知则为null) + */ + private Long senderId; + + /** + * 通知类型 + */ + private String type; + + /** + * 通知标题 + */ + private String title; + + /** + * 通知的主要内容 + */ + private String content; + + /** + * 通知状态(0未读 1已读) + */ + private String status; + + /** + * 用户读取通知的时间 + */ + private Date readAt; + + /** + * 点击通知后跳转的目标URL + */ + private String actionUrl; + + /** + * 通知附件 + */ + private String file; + + /** + * 附件列表 + */ + private List fileVo; + + /** + * 备注 + */ + private String remark; + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysNotificationsMapper.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysNotificationsMapper.java new file mode 100644 index 0000000..a08a11b --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysNotificationsMapper.java @@ -0,0 +1,17 @@ +package org.dromara.system.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.dromara.system.domain.SysNotifications; + +/** + * @author lilemy + * @description 针对表【sys_notifications(系统通知)】的数据库操作Mapper + * @createDate 2025-07-24 16:19:58 + */ +public interface SysNotificationsMapper extends BaseMapper { + +} + + + + diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/SysNotificationsService.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/SysNotificationsService.java new file mode 100644 index 0000000..8a01168 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/SysNotificationsService.java @@ -0,0 +1,68 @@ +package org.dromara.system.service; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.IService; +import org.dromara.system.domain.SysNotifications; +import org.dromara.system.domain.dto.notifications.SysNotificationsCreateBatchReq; +import org.dromara.system.domain.dto.notifications.SysNotificationsQueryReq; +import org.dromara.system.domain.vo.SysNotificationsVo; + +import java.util.Collection; + +/** + * @author lilemy + * @description 针对表【sys_notifications(系统通知)】的数据库操作Service + * @createDate 2025-07-24 16:19:58 + */ +public interface SysNotificationsService extends IService { + + /** + * 查询系统通知 + * + * @param id 系统通知主键 + * @return 系统通知 + */ + SysNotificationsVo queryById(Long id); + + /** + * 批量发送通知 + * + * @param req 通知 + * @return 是否成功 + */ + Boolean sendBatch(SysNotificationsCreateBatchReq req); + + /** + * 删除通知 + * + * @param ids 通知ID串 + * @return 结果 + */ + Boolean deleteByIds(Collection ids); + + /** + * 获取系统通知 + * + * @param notifications 系统通知 + * @return 系统通知 + */ + SysNotificationsVo getVo(SysNotifications notifications); + + /** + * 构建查询条件 + * + * @param req 查询参数 + * @return 查询条件 + */ + LambdaQueryWrapper buildQueryWrapper(SysNotificationsQueryReq req); + + /** + * 获取系统通知分页 + * + * @param notificationsPage 系统通知分页 + * @return 系统通知分页 + */ + Page getVoPage(Page notificationsPage); + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysNotificationsServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysNotificationsServiceImpl.java new file mode 100644 index 0000000..0473e89 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysNotificationsServiceImpl.java @@ -0,0 +1,186 @@ +package org.dromara.system.service.impl; + +import cn.hutool.core.collection.CollUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import jakarta.annotation.Resource; +import org.dromara.common.core.constant.HttpStatus; +import org.dromara.common.core.exception.ServiceException; +import org.dromara.common.core.utils.ObjectUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.sse.dto.SseMessageDto; +import org.dromara.common.sse.utils.SseMessageUtils; +import org.dromara.system.domain.SysNotifications; +import org.dromara.system.domain.dto.notifications.SysNotificationsCreateBatchReq; +import org.dromara.system.domain.dto.notifications.SysNotificationsQueryReq; +import org.dromara.system.domain.vo.SysNotificationsVo; +import org.dromara.system.mapper.SysNotificationsMapper; +import org.dromara.system.service.ISysOssService; +import org.dromara.system.service.SysNotificationsService; +import org.springframework.beans.BeanUtils; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.Set; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; + +/** + * @author lilemy + * @description 针对表【sys_notifications(系统通知)】的数据库操作Service实现 + * @createDate 2025-07-24 16:19:58 + */ +@Service +public class SysNotificationsServiceImpl extends ServiceImpl + implements SysNotificationsService { + + @Resource + private ISysOssService ossService; + + @Resource + private ScheduledExecutorService scheduledExecutorService; + + /** + * 查询系统通知 + * + * @param id 系统通知主键 + * @return 系统通知 + */ + @Override + public SysNotificationsVo queryById(Long id) { + SysNotifications notifications = this.getById(id); + if (notifications == null) { + throw new ServiceException("当前通知不存在", HttpStatus.NOT_FOUND); + } + return this.getVo(notifications); + } + + /** + * 批量发送通知 + * + * @param req 通知 + * @return 是否成功 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean sendBatch(SysNotificationsCreateBatchReq req) { + List recipientIds = req.getRecipientIds(); + if (CollUtil.isEmpty(recipientIds)) { + throw new ServiceException("请选择接收人", HttpStatus.BAD_REQUEST); + } + List sysNotificationsList = recipientIds.stream().map(recipientId -> { + SysNotifications notifications = new SysNotifications(); + notifications.setRecipientId(recipientId); + notifications.setSenderId(req.getSenderId()); + notifications.setType(req.getType()); + notifications.setTitle(req.getTitle()); + notifications.setContent(req.getContent()); + notifications.setActionUrl(req.getActionUrl()); + notifications.setFile(req.getFile()); + notifications.setRemark(req.getRemark()); + return notifications; + }).toList(); + boolean result = this.saveBatch(sysNotificationsList); + if (!result) { + throw new ServiceException("通知发送失败", HttpStatus.ERROR); + } + // 通过 sse 发送通知 + scheduledExecutorService.schedule(() -> { + SseMessageDto dto = new SseMessageDto(); + dto.setMessage(StringUtils.isNotBlank(req.getTitle()) ? req.getTitle() : req.getContent().substring(0, 50)); + dto.setUserIds(recipientIds); + SseMessageUtils.publishMessage(dto); + }, 2, TimeUnit.SECONDS); + return true; + } + + /** + * 删除通知 + * + * @param ids 通知ID串 + * @return 结果 + */ + @Override + public Boolean deleteByIds(Collection ids) { + return null; + } + + /** + * 获取系统通知 + * + * @param notifications 系统通知 + * @return 系统通知 + */ + @Override + public SysNotificationsVo getVo(SysNotifications notifications) { + SysNotificationsVo sysNotificationsVo = new SysNotificationsVo(); + if (notifications == null) { + return sysNotificationsVo; + } + BeanUtils.copyProperties(notifications, sysNotificationsVo); + // 关联文件信息 + Set ossIds = Arrays.stream(notifications.getFile().split(",")).map(Long::parseLong).collect(Collectors.toSet()); + if (CollUtil.isNotEmpty(ossIds)) { + sysNotificationsVo.setFileVo(ossService.listByIds(ossIds)); + } + return sysNotificationsVo; + } + + /** + * 构建查询条件 + * + * @param req 查询参数 + * @return 查询条件 + */ + @Override + public LambdaQueryWrapper buildQueryWrapper(SysNotificationsQueryReq req) { + LambdaQueryWrapper lqw = new LambdaQueryWrapper<>(); + if (req == null) { + return lqw; + } + Long recipientId = req.getRecipientId(); + Long senderId = req.getSenderId(); + String type = req.getType(); + String title = req.getTitle(); + String content = req.getContent(); + String status = req.getStatus(); + lqw.like(StringUtils.isNotBlank(title), SysNotifications::getTitle, title); + lqw.like(StringUtils.isNotBlank(content), SysNotifications::getContent, content); + lqw.eq(StringUtils.isNotBlank(type), SysNotifications::getType, type); + lqw.eq(StringUtils.isNotBlank(status), SysNotifications::getStatus, status); + lqw.eq(ObjectUtils.isNotEmpty(recipientId), SysNotifications::getRecipientId, recipientId); + lqw.eq(ObjectUtils.isNotEmpty(senderId), SysNotifications::getSenderId, senderId); + return lqw; + } + + /** + * 获取系统通知分页 + * + * @param notificationsPage 系统通知分页 + * @return 系统通知分页 + */ + @Override + public Page getVoPage(Page notificationsPage) { + List notificationsList = notificationsPage.getRecords(); + Page notificationsVoPage = new Page<>( + notificationsPage.getCurrent(), + notificationsPage.getSize(), + notificationsPage.getTotal() + ); + if (CollUtil.isEmpty(notificationsList)) { + return notificationsVoPage; + } + List notificationsVoList = notificationsList.stream().map(this::getVo).toList(); + notificationsVoPage.setRecords(notificationsVoList); + return notificationsVoPage; + } +} + + + + diff --git a/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysNotificationsMapper.xml b/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysNotificationsMapper.xml new file mode 100644 index 0000000..19f340b --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysNotificationsMapper.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + id,recipient_id,sender_id,type,title,content, + status,read_at,action_url,file,remark, + create_time,update_time + + diff --git a/script/sql/xinnengyuan.sql b/script/sql/xinnengyuan.sql index 081e499..4200031 100644 --- a/script/sql/xinnengyuan.sql +++ b/script/sql/xinnengyuan.sql @@ -1590,3 +1590,25 @@ CREATE TABLE `hse_violation_record` index `idx_project_id` (`project_id` asc) using btree comment '项目id' ) comment '违规记录' collate = utf8mb4_unicode_ci; +DROP TABLE IF EXISTS `sys_notifications`; +CREATE TABLE `sys_notifications` +( + `id` bigint not null auto_increment comment '主键ID', + `recipient_id` bigint not null comment '接收通知的用户ID', + `sender_id` bigint default 0 not null comment '发送通知的用户ID(系统通知则为null)', + `type` char(2) not null comment '通知类型', + `title` varchar(255) default '' not null comment '通知标题', + `content` text not null comment '通知的主要内容', + `status` char(1) default '0' not null comment '通知状态(0未读 1已读)', + `read_at` datetime null comment '用户读取通知的时间', + `action_url` varchar(1024) default '' not null comment '点击通知后跳转的目标URL', + `file` varchar(1024) null comment '通知附件', + `remark` varchar(255) null comment '备注', + `create_time` datetime default CURRENT_TIMESTAMP null comment '创建时间', + `update_time` datetime default CURRENT_TIMESTAMP null on update CURRENT_TIMESTAMP comment '更新时间', + primary key (`id`) using btree, + INDEX `idx_recipient_id` (`recipient_id`), + INDEX `idx_sender_id` (`sender_id`), + index `idx_type` (`type`) +) comment '系统通知' collate = utf8mb4_unicode_ci; +