From 5fe7a5c48ab143406f9526f6204e5511701fd395 Mon Sep 17 00:00:00 2001 From: xuqiuyun <1113560936@qq.com> Date: Tue, 21 Oct 2025 09:01:11 +0800 Subject: [PATCH] Completely fix tradeCattle folder --- tradeCattle | 1 - tradeCattle/.gitignore | 29 + tradeCattle/DATABASE_MIGRATION_README.md | 68 + tradeCattle/README.md | 98 ++ tradeCattle/add_id_card_field.sql | 24 + tradeCattle/add_id_card_field_safe.sql | 80 + .../add_landing_entruck_weight_field.sql | 24 + tradeCattle/aiotagro-cattle-trade/pom.xml | 264 +++ .../AiotagroCattleTradeApplication.java | 22 + .../business/controller/CommonControl.java | 42 + .../controller/DeliveryController.java | 545 ++++++ .../controller/DeliveryDeviceController.java | 153 ++ .../controller/JbqClientController.java | 147 ++ .../controller/JbqClientLogController.java | 34 + .../controller/JbqServerController.java | 114 ++ .../controller/JbqServerLogController.java | 20 + .../business/controller/LoginControl.java | 65 + .../business/controller/MemberController.java | 655 ++++++++ .../controller/SysMenuController.java | 209 +++ .../controller/SysRoleController.java | 90 + .../controller/SysTenantController.java | 113 ++ .../controller/SysUserController.java | 51 + .../controller/WarningLogController.java | 82 + .../controller/WechatDeliveryController.java | 199 +++ .../controller/XqClientController.java | 107 ++ .../cattletrade/business/dto/BaseDto.java | 18 + .../business/dto/DeliverListDto.java | 44 + .../business/dto/DeliveryAddDto.java | 84 + .../business/dto/DeliveryCreateDto.java | 114 ++ .../business/dto/DeliveryEditDto.java | 48 + .../business/dto/DeliveryQueryDto.java | 23 + .../business/dto/DeviceAssignDto.java | 38 + .../cattletrade/business/dto/DeviceDto.java | 32 + .../cattletrade/business/dto/FileDto.java | 10 + .../cattletrade/business/dto/JbqLogDto.java | 8 + .../cattletrade/business/dto/LoginDto.java | 25 + .../business/dto/ServerLocationDto.java | 15 + .../cattletrade/business/dto/SysRoleDto.java | 16 + .../cattletrade/business/dto/SysUserDto.java | 16 + .../business/dto/WarningDetailDto.java | 110 ++ .../cattletrade/business/entity/Delivery.java | 412 +++++ .../business/entity/DeliveryDevice.java | 112 ++ .../business/entity/JbqClient.java | 123 ++ .../business/entity/JbqClientLog.java | 110 ++ .../business/entity/JbqServer.java | 106 ++ .../business/entity/JbqServerLog.java | 100 ++ .../cattletrade/business/entity/Member.java | 39 + .../cattletrade/business/entity/SysMenu.java | 104 ++ .../cattletrade/business/entity/SysRole.java | 73 + .../business/entity/SysRoleMenu.java | 43 + .../business/entity/SysTenant.java | 31 + .../cattletrade/business/entity/SysUser.java | 109 ++ .../business/entity/WarningLog.java | 79 + .../cattletrade/business/entity/XqClient.java | 118 ++ .../business/mapper/DeliveryDeviceMapper.java | 18 + .../business/mapper/DeliveryMapper.java | 25 + .../business/mapper/JbqClientLogMapper.java | 23 + .../business/mapper/JbqClientMapper.java | 29 + .../business/mapper/JbqServerLogMapper.java | 24 + .../business/mapper/JbqServerMapper.java | 24 + .../business/mapper/MemberDriverMapper.java | 138 ++ .../business/mapper/MemberMapper.java | 209 +++ .../business/mapper/SysMenuMapper.java | 45 + .../business/mapper/SysRoleMapper.java | 18 + .../business/mapper/SysRoleMenuMapper.java | 18 + .../business/mapper/SysTenantMapper.java | 16 + .../business/mapper/SysUserMapper.java | 44 + .../business/mapper/WarningLogMapper.java | 24 + .../business/mapper/XqClientMapper.java | 74 + .../service/IDeliveryDeviceService.java | 16 + .../business/service/IDeliveryService.java | 51 + .../service/IJbqClientLogService.java | 19 + .../business/service/IJbqClientService.java | 29 + .../service/IJbqServerLogService.java | 16 + .../business/service/IJbqServerService.java | 25 + .../business/service/ISysRoleService.java | 50 + .../business/service/ISysUserService.java | 43 + .../business/service/IWarningLogService.java | 40 + .../business/service/IXqClientService.java | 43 + .../business/service/LoginService.java | 37 + .../service/TencentSmsCodeService.java | 6 + .../impl/DeliveryDeviceServiceImpl.java | 20 + .../service/impl/DeliveryServiceImpl.java | 1284 ++++++++++++++ .../service/impl/JbqClientLogServiceImpl.java | 53 + .../service/impl/JbqClientServiceImpl.java | 125 ++ .../service/impl/JbqServerLogServiceImpl.java | 20 + .../service/impl/JbqServerServiceImpl.java | 142 ++ .../service/impl/LoginServiceImpl.java | 180 ++ .../service/impl/SysRoleServiceImpl.java | 117 ++ .../service/impl/SysUserServiceImpl.java | 86 + .../impl/TencentSmsCodeServiceImpl.java | 102 ++ .../service/impl/WarningLogServiceImpl.java | 239 +++ .../service/impl/XqClientServiceImpl.java | 294 ++++ .../business/utils/DataScopeUtil.java | 59 + .../business/vo/DeliveryLogVo.java | 86 + .../cattletrade/business/vo/JbqClientVo.java | 47 + .../business/vo/ServerClientVo.java | 50 + .../business/vo/ServerLocationVo.java | 29 + .../cattletrade/config/CustomStpLogic.java | 115 ++ .../config/DruidDataSourceConfig.java | 35 + .../config/LogRecordConfiguration.java | 22 + .../cattletrade/config/MybatisPlusConfig.java | 69 + .../cattletrade/config/SaTokenConfig.java | 42 + .../cattletrade/config/StpInterfaceImpl.java | 99 ++ .../cattletrade/config/XxlJobConfig.java | 78 + .../cattletrade/constant/HttpStatus.java | 94 ++ .../cattletrade/constant/SaobeiConstant.java | 10 + .../constant/WarningStatusAdminEnum.java | 22 + .../constant/WarningStatusEnum.java | 22 + .../exception/GlobalException.java | 47 + .../generater/FastCodeGenerator.java | 346 ++++ .../cattletrade/job/AutoNumWarningJob.java | 109 ++ .../PlatformExpensesProperties.java | 37 + .../properties/TenantConfigProperties.java | 32 + .../aiotagro/cattletrade/remote/TestApi.java | 12 + .../src/main/resources/application-demo.yml | 108 ++ .../src/main/resources/application-dev.yml | 118 ++ .../src/main/resources/application-prod.yml | 110 ++ .../src/main/resources/application.yml | 97 ++ .../resources/mapper/DeliveryDeviceMapper.xml | 18 + .../main/resources/mapper/DeliveryMapper.xml | 93 ++ .../resources/mapper/JbqClientLogMapper.xml | 42 + .../main/resources/mapper/JbqClientMapper.xml | 92 + .../resources/mapper/JbqServerLogMapper.xml | 33 + .../main/resources/mapper/JbqServerMapper.xml | 58 + .../main/resources/mapper/SysMenuMapper.xml | 56 + .../main/resources/mapper/SysRoleMapper.xml | 20 + .../resources/mapper/SysRoleMenuMapper.xml | 17 + .../main/resources/mapper/SysUserMapper.xml | 54 + .../resources/mapper/WarningLogMapper.xml | 43 + .../main/resources/mybatis/mybatis-config.xml | 20 + tradeCattle/aiotagro-core/pom.xml | 156 ++ .../common/core/annotation/Excel.java | 183 ++ .../common/core/annotation/Excels.java | 18 + .../common/core/constant/CacheConstants.java | 59 + .../common/core/constant/Constants.java | 142 ++ .../common/core/constant/GenConstants.java | 117 ++ .../common/core/constant/HttpStatus.java | 94 ++ .../LogRecordType/CustomerRecordType.java | 16 + .../LogRecordType/ProductsRecordType.java | 13 + .../LogRecordType/TradeMarketRecordType.java | 16 + .../TradeMarketRuleRecordType.java | 15 + .../LogRecordType/UserRecordType.java | 19 + .../common/core/constant/RoleConstants.java | 26 + .../core/constant/ScheduleConstants.java | 50 + .../core/constant/SecurityConstants.java | 54 + .../core/constant/ServiceNameConstants.java | 24 + .../core/constant/TencentCloudConstants.java | 224 +++ .../common/core/constant/TokenConstants.java | 25 + .../common/core/constant/UserConstants.java | 80 + .../core/context/SecurityContextHolder.java | 105 ++ .../com/aiotagro/common/core/domain/R.java | 116 ++ .../common/core/enums/UserStatus.java | 30 + .../core/exception/CaptchaException.java | 16 + .../core/exception/CheckedException.java | 31 + .../core/exception/DemoModeException.java | 15 + .../core/exception/GlobalException.java | 58 + .../core/exception/InnerAuthException.java | 16 + .../core/exception/PreAuthorizeException.java | 15 + .../core/exception/ServiceException.java | 74 + .../common/core/exception/UtilException.java | 26 + .../exception/auth/NotLoginException.java | 16 + .../auth/NotPermissionException.java | 23 + .../core/exception/auth/NotRoleException.java | 23 + .../core/exception/base/BaseException.java | 79 + .../core/exception/file/FileException.java | 19 + .../FileNameLengthLimitExceededException.java | 16 + .../file/FileSizeLimitExceededException.java | 16 + .../exception/file/FileUploadException.java | 61 + .../file/InvalidExtensionException.java | 80 + .../core/exception/job/TaskException.java | 34 + .../user/CaptchaExpireException.java | 16 + .../core/exception/user/UserException.java | 18 + .../user/UserPasswordNotMatchException.java | 16 + .../aiotagro/common/core/text/CharsetKit.java | 87 + .../aiotagro/common/core/text/Convert.java | 1013 +++++++++++ .../common/core/text/StrFormatter.java | 92 + .../aiotagro/common/core/utils/DateUtils.java | 214 +++ .../aiotagro/common/core/utils/EnumUtil.java | 25 + .../common/core/utils/ExceptionUtil.java | 40 + .../aiotagro/common/core/utils/JwtUtils.java | 142 ++ .../common/core/utils/LngLonUtil.java | 158 ++ .../aiotagro/common/core/utils/Md5Utils.java | 54 + .../common/core/utils/NewDateUtils.java | 354 ++++ .../common/core/utils/SecurityUtil.java | 83 + .../common/core/utils/ServletUtils.java | 326 ++++ .../aiotagro/common/core/utils/SmsUtils.java | 67 + .../common/core/utils/SpringBeanUtils.java | 33 + .../common/core/utils/SpringUtils.java | 114 ++ .../common/core/utils/StringUtils.java | 562 +++++++ .../common/core/utils/TencentCloudUtil.java | 85 + .../common/core/utils/bean/BeanUtils.java | 110 ++ .../core/utils/bean/BeanValidators.java | 24 + .../common/core/utils/file/FileTypeUtils.java | 96 ++ .../common/core/utils/file/FileUtils.java | 249 +++ .../common/core/utils/file/ImageUtils.java | 85 + .../common/core/utils/file/MimeTypeUtils.java | 59 + .../common/core/utils/html/EscapeUtil.java | 167 ++ .../common/core/utils/html/HTMLFilter.java | 566 +++++++ .../common/core/utils/ip/IpUtils.java | 383 +++++ .../common/core/utils/poi/EasyExcelUtil.java | 108 ++ .../core/utils/poi/ExcelHandlerAdapter.java | 24 + .../common/core/utils/poi/ExcelUtil.java | 1475 +++++++++++++++++ .../core/utils/reflect/ReflectUtils.java | 406 +++++ .../common/core/utils/sign/Base64.java | 291 ++++ .../common/core/utils/sql/SqlUtil.java | 70 + .../common/core/utils/uuid/DistinctUtil.java | 76 + .../common/core/utils/uuid/IdUtils.java | 49 + .../aiotagro/common/core/utils/uuid/Seq.java | 87 + .../aiotagro/common/core/utils/uuid/UUID.java | 485 ++++++ .../common/core/web/domain/AjaxResult.java | 217 +++ .../common/core/web/domain/BaseEntity.java | 119 ++ .../common/core/web/domain/BaseResponse.java | 21 + .../core/web/domain/PageResultResponse.java | 90 + .../common/core/web/domain/TreeEntity.java | 79 + .../common/core/web/page/PageDomain.java | 101 ++ .../common/core/web/page/TableDataInfo.java | 85 + .../common/core/web/page/TableSupport.java | 56 + .../com/aiotagro/common/core/xss/Xss.java | 27 + .../common/core/xss/XssValidator.java | 35 + tradeCattle/aiotagro-redis/pom.xml | 35 + .../FastJson2JsonRedisSerializer.java | 53 + .../common/redis/configure/RedisConfig.java | 43 + .../common/redis/service/RedisService.java | 328 ++++ .../compile/default-compile/createdFiles.lst | 3 + .../compile/default-compile/inputFiles.lst | 3 + .../database_migration_add_id_card_field.sql | 27 + tradeCattle/pom.xml | 328 ++++ tradeCattle/verify_id_card_field.sql | 50 + 229 files changed, 23811 insertions(+), 1 deletion(-) delete mode 160000 tradeCattle create mode 100644 tradeCattle/.gitignore create mode 100644 tradeCattle/DATABASE_MIGRATION_README.md create mode 100644 tradeCattle/README.md create mode 100644 tradeCattle/add_id_card_field.sql create mode 100644 tradeCattle/add_id_card_field_safe.sql create mode 100644 tradeCattle/add_landing_entruck_weight_field.sql create mode 100644 tradeCattle/aiotagro-cattle-trade/pom.xml create mode 100644 tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/AiotagroCattleTradeApplication.java create mode 100644 tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/controller/CommonControl.java create mode 100644 tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/controller/DeliveryController.java create mode 100644 tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/controller/DeliveryDeviceController.java create mode 100644 tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/controller/JbqClientController.java create mode 100644 tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/controller/JbqClientLogController.java create mode 100644 tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/controller/JbqServerController.java create mode 100644 tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/controller/JbqServerLogController.java create mode 100644 tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/controller/LoginControl.java create mode 100644 tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/controller/MemberController.java create mode 100644 tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/controller/SysMenuController.java create mode 100644 tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/controller/SysRoleController.java create mode 100644 tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/controller/SysTenantController.java create mode 100644 tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/controller/SysUserController.java create mode 100644 tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/controller/WarningLogController.java create mode 100644 tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/controller/WechatDeliveryController.java create mode 100644 tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/controller/XqClientController.java create mode 100644 tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/dto/BaseDto.java create mode 100644 tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/dto/DeliverListDto.java create mode 100644 tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/dto/DeliveryAddDto.java create mode 100644 tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/dto/DeliveryCreateDto.java create mode 100644 tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/dto/DeliveryEditDto.java create mode 100644 tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/dto/DeliveryQueryDto.java create mode 100644 tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/dto/DeviceAssignDto.java create mode 100644 tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/dto/DeviceDto.java create mode 100644 tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/dto/FileDto.java create mode 100644 tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/dto/JbqLogDto.java create mode 100644 tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/dto/LoginDto.java create mode 100644 tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/dto/ServerLocationDto.java create mode 100644 tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/dto/SysRoleDto.java create mode 100644 tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/dto/SysUserDto.java create mode 100644 tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/dto/WarningDetailDto.java create mode 100644 tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/entity/Delivery.java create mode 100644 tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/entity/DeliveryDevice.java create mode 100644 tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/entity/JbqClient.java create mode 100644 tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/entity/JbqClientLog.java create mode 100644 tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/entity/JbqServer.java create mode 100644 tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/entity/JbqServerLog.java create mode 100644 tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/entity/Member.java create mode 100644 tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/entity/SysMenu.java create mode 100644 tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/entity/SysRole.java create mode 100644 tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/entity/SysRoleMenu.java create mode 100644 tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/entity/SysTenant.java create mode 100644 tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/entity/SysUser.java create mode 100644 tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/entity/WarningLog.java create mode 100644 tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/entity/XqClient.java create mode 100644 tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/mapper/DeliveryDeviceMapper.java create mode 100644 tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/mapper/DeliveryMapper.java create mode 100644 tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/mapper/JbqClientLogMapper.java create mode 100644 tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/mapper/JbqClientMapper.java create mode 100644 tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/mapper/JbqServerLogMapper.java create mode 100644 tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/mapper/JbqServerMapper.java create mode 100644 tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/mapper/MemberDriverMapper.java create mode 100644 tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/mapper/MemberMapper.java create mode 100644 tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/mapper/SysMenuMapper.java create mode 100644 tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/mapper/SysRoleMapper.java create mode 100644 tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/mapper/SysRoleMenuMapper.java create mode 100644 tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/mapper/SysTenantMapper.java create mode 100644 tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/mapper/SysUserMapper.java create mode 100644 tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/mapper/WarningLogMapper.java create mode 100644 tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/mapper/XqClientMapper.java create mode 100644 tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/service/IDeliveryDeviceService.java create mode 100644 tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/service/IDeliveryService.java create mode 100644 tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/service/IJbqClientLogService.java create mode 100644 tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/service/IJbqClientService.java create mode 100644 tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/service/IJbqServerLogService.java create mode 100644 tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/service/IJbqServerService.java create mode 100644 tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/service/ISysRoleService.java create mode 100644 tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/service/ISysUserService.java create mode 100644 tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/service/IWarningLogService.java create mode 100644 tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/service/IXqClientService.java create mode 100644 tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/service/LoginService.java create mode 100644 tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/service/TencentSmsCodeService.java create mode 100644 tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/service/impl/DeliveryDeviceServiceImpl.java create mode 100644 tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/service/impl/DeliveryServiceImpl.java create mode 100644 tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/service/impl/JbqClientLogServiceImpl.java create mode 100644 tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/service/impl/JbqClientServiceImpl.java create mode 100644 tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/service/impl/JbqServerLogServiceImpl.java create mode 100644 tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/service/impl/JbqServerServiceImpl.java create mode 100644 tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/service/impl/LoginServiceImpl.java create mode 100644 tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/service/impl/SysRoleServiceImpl.java create mode 100644 tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/service/impl/SysUserServiceImpl.java create mode 100644 tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/service/impl/TencentSmsCodeServiceImpl.java create mode 100644 tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/service/impl/WarningLogServiceImpl.java create mode 100644 tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/service/impl/XqClientServiceImpl.java create mode 100644 tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/utils/DataScopeUtil.java create mode 100644 tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/vo/DeliveryLogVo.java create mode 100644 tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/vo/JbqClientVo.java create mode 100644 tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/vo/ServerClientVo.java create mode 100644 tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/vo/ServerLocationVo.java create mode 100644 tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/config/CustomStpLogic.java create mode 100644 tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/config/DruidDataSourceConfig.java create mode 100644 tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/config/LogRecordConfiguration.java create mode 100644 tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/config/MybatisPlusConfig.java create mode 100644 tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/config/SaTokenConfig.java create mode 100644 tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/config/StpInterfaceImpl.java create mode 100644 tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/config/XxlJobConfig.java create mode 100644 tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/constant/HttpStatus.java create mode 100644 tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/constant/SaobeiConstant.java create mode 100644 tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/constant/WarningStatusAdminEnum.java create mode 100644 tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/constant/WarningStatusEnum.java create mode 100644 tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/exception/GlobalException.java create mode 100644 tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/generater/FastCodeGenerator.java create mode 100644 tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/job/AutoNumWarningJob.java create mode 100644 tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/properties/PlatformExpensesProperties.java create mode 100644 tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/properties/TenantConfigProperties.java create mode 100644 tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/remote/TestApi.java create mode 100644 tradeCattle/aiotagro-cattle-trade/src/main/resources/application-demo.yml create mode 100644 tradeCattle/aiotagro-cattle-trade/src/main/resources/application-dev.yml create mode 100644 tradeCattle/aiotagro-cattle-trade/src/main/resources/application-prod.yml create mode 100644 tradeCattle/aiotagro-cattle-trade/src/main/resources/application.yml create mode 100644 tradeCattle/aiotagro-cattle-trade/src/main/resources/mapper/DeliveryDeviceMapper.xml create mode 100644 tradeCattle/aiotagro-cattle-trade/src/main/resources/mapper/DeliveryMapper.xml create mode 100644 tradeCattle/aiotagro-cattle-trade/src/main/resources/mapper/JbqClientLogMapper.xml create mode 100644 tradeCattle/aiotagro-cattle-trade/src/main/resources/mapper/JbqClientMapper.xml create mode 100644 tradeCattle/aiotagro-cattle-trade/src/main/resources/mapper/JbqServerLogMapper.xml create mode 100644 tradeCattle/aiotagro-cattle-trade/src/main/resources/mapper/JbqServerMapper.xml create mode 100644 tradeCattle/aiotagro-cattle-trade/src/main/resources/mapper/SysMenuMapper.xml create mode 100644 tradeCattle/aiotagro-cattle-trade/src/main/resources/mapper/SysRoleMapper.xml create mode 100644 tradeCattle/aiotagro-cattle-trade/src/main/resources/mapper/SysRoleMenuMapper.xml create mode 100644 tradeCattle/aiotagro-cattle-trade/src/main/resources/mapper/SysUserMapper.xml create mode 100644 tradeCattle/aiotagro-cattle-trade/src/main/resources/mapper/WarningLogMapper.xml create mode 100644 tradeCattle/aiotagro-cattle-trade/src/main/resources/mybatis/mybatis-config.xml create mode 100644 tradeCattle/aiotagro-core/pom.xml create mode 100644 tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/annotation/Excel.java create mode 100644 tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/annotation/Excels.java create mode 100644 tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/constant/CacheConstants.java create mode 100644 tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/constant/Constants.java create mode 100644 tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/constant/GenConstants.java create mode 100644 tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/constant/HttpStatus.java create mode 100644 tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/constant/LogRecordType/CustomerRecordType.java create mode 100644 tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/constant/LogRecordType/ProductsRecordType.java create mode 100644 tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/constant/LogRecordType/TradeMarketRecordType.java create mode 100644 tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/constant/LogRecordType/TradeMarketRuleRecordType.java create mode 100644 tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/constant/LogRecordType/UserRecordType.java create mode 100644 tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/constant/RoleConstants.java create mode 100644 tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/constant/ScheduleConstants.java create mode 100644 tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/constant/SecurityConstants.java create mode 100644 tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/constant/ServiceNameConstants.java create mode 100644 tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/constant/TencentCloudConstants.java create mode 100644 tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/constant/TokenConstants.java create mode 100644 tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/constant/UserConstants.java create mode 100644 tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/context/SecurityContextHolder.java create mode 100644 tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/domain/R.java create mode 100644 tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/enums/UserStatus.java create mode 100644 tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/exception/CaptchaException.java create mode 100644 tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/exception/CheckedException.java create mode 100644 tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/exception/DemoModeException.java create mode 100644 tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/exception/GlobalException.java create mode 100644 tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/exception/InnerAuthException.java create mode 100644 tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/exception/PreAuthorizeException.java create mode 100644 tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/exception/ServiceException.java create mode 100644 tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/exception/UtilException.java create mode 100644 tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/exception/auth/NotLoginException.java create mode 100644 tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/exception/auth/NotPermissionException.java create mode 100644 tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/exception/auth/NotRoleException.java create mode 100644 tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/exception/base/BaseException.java create mode 100644 tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/exception/file/FileException.java create mode 100644 tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/exception/file/FileNameLengthLimitExceededException.java create mode 100644 tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/exception/file/FileSizeLimitExceededException.java create mode 100644 tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/exception/file/FileUploadException.java create mode 100644 tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/exception/file/InvalidExtensionException.java create mode 100644 tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/exception/job/TaskException.java create mode 100644 tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/exception/user/CaptchaExpireException.java create mode 100644 tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/exception/user/UserException.java create mode 100644 tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/exception/user/UserPasswordNotMatchException.java create mode 100644 tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/text/CharsetKit.java create mode 100644 tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/text/Convert.java create mode 100644 tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/text/StrFormatter.java create mode 100644 tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/utils/DateUtils.java create mode 100644 tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/utils/EnumUtil.java create mode 100644 tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/utils/ExceptionUtil.java create mode 100644 tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/utils/JwtUtils.java create mode 100644 tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/utils/LngLonUtil.java create mode 100644 tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/utils/Md5Utils.java create mode 100644 tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/utils/NewDateUtils.java create mode 100644 tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/utils/SecurityUtil.java create mode 100644 tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/utils/ServletUtils.java create mode 100644 tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/utils/SmsUtils.java create mode 100644 tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/utils/SpringBeanUtils.java create mode 100644 tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/utils/SpringUtils.java create mode 100644 tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/utils/StringUtils.java create mode 100644 tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/utils/TencentCloudUtil.java create mode 100644 tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/utils/bean/BeanUtils.java create mode 100644 tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/utils/bean/BeanValidators.java create mode 100644 tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/utils/file/FileTypeUtils.java create mode 100644 tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/utils/file/FileUtils.java create mode 100644 tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/utils/file/ImageUtils.java create mode 100644 tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/utils/file/MimeTypeUtils.java create mode 100644 tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/utils/html/EscapeUtil.java create mode 100644 tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/utils/html/HTMLFilter.java create mode 100644 tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/utils/ip/IpUtils.java create mode 100644 tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/utils/poi/EasyExcelUtil.java create mode 100644 tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/utils/poi/ExcelHandlerAdapter.java create mode 100644 tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/utils/poi/ExcelUtil.java create mode 100644 tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/utils/reflect/ReflectUtils.java create mode 100644 tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/utils/sign/Base64.java create mode 100644 tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/utils/sql/SqlUtil.java create mode 100644 tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/utils/uuid/DistinctUtil.java create mode 100644 tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/utils/uuid/IdUtils.java create mode 100644 tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/utils/uuid/Seq.java create mode 100644 tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/utils/uuid/UUID.java create mode 100644 tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/web/domain/AjaxResult.java create mode 100644 tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/web/domain/BaseEntity.java create mode 100644 tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/web/domain/BaseResponse.java create mode 100644 tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/web/domain/PageResultResponse.java create mode 100644 tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/web/domain/TreeEntity.java create mode 100644 tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/web/page/PageDomain.java create mode 100644 tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/web/page/TableDataInfo.java create mode 100644 tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/web/page/TableSupport.java create mode 100644 tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/xss/Xss.java create mode 100644 tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/xss/XssValidator.java create mode 100644 tradeCattle/aiotagro-redis/pom.xml create mode 100644 tradeCattle/aiotagro-redis/src/main/java/com/aiotagro/common/redis/configure/FastJson2JsonRedisSerializer.java create mode 100644 tradeCattle/aiotagro-redis/src/main/java/com/aiotagro/common/redis/configure/RedisConfig.java create mode 100644 tradeCattle/aiotagro-redis/src/main/java/com/aiotagro/common/redis/service/RedisService.java create mode 100644 tradeCattle/aiotagro-redis/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst create mode 100644 tradeCattle/aiotagro-redis/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst create mode 100644 tradeCattle/database_migration_add_id_card_field.sql create mode 100644 tradeCattle/pom.xml create mode 100644 tradeCattle/verify_id_card_field.sql diff --git a/tradeCattle b/tradeCattle deleted file mode 160000 index 30bb0da..0000000 --- a/tradeCattle +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 30bb0dad3b625aaca4ce1926d4e4eb35e60b6306 diff --git a/tradeCattle/.gitignore b/tradeCattle/.gitignore new file mode 100644 index 0000000..6a55bfe --- /dev/null +++ b/tradeCattle/.gitignore @@ -0,0 +1,29 @@ +# ---> Java +# Compiled class file +*.class + +# Log file +*.log + +# BlueJ files +*.ctxt + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ + +# Package Files # +*.jar +*.war +*.nar +*.ear +*.zip +*.tar.gz +*.rar + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* +replay_pid* + +/.idea/ +/aiotagro-cattle-trade/target/ +/aiotagro-core/target/ diff --git a/tradeCattle/DATABASE_MIGRATION_README.md b/tradeCattle/DATABASE_MIGRATION_README.md new file mode 100644 index 0000000..349fa9c --- /dev/null +++ b/tradeCattle/DATABASE_MIGRATION_README.md @@ -0,0 +1,68 @@ +# 数据库迁移:添加身份证字段 + +## 概述 +为 `member_driver` 表添加 `id_card` 字段,用于存储司机身份证前后面照片的URL地址。 + +## 字段信息 +- **字段名**: `id_card` +- **数据类型**: `TEXT` +- **允许空值**: `YES` +- **默认值**: `NULL` +- **注释**: 身份证前后面照片地址(多个URL用逗号分隔) +- **位置**: 在 `car_img` 字段之后 + +## 执行步骤 + +### 方法1:使用 MySQL 命令行 +```bash +# 连接到数据库 +mysql -h 129.211.213.226 -P 3306 -u root -p cattletrade + +# 执行 SQL 脚本 +source /path/to/add_id_card_field.sql +``` + +### 方法2:使用 MySQL Workbench 或其他数据库管理工具 +1. 连接到数据库服务器:`129.211.213.226:3306` +2. 选择数据库:`cattletrade` +3. 执行 `add_id_card_field.sql` 文件中的 SQL 语句 + +### 方法3:直接在应用服务器上执行 +```bash +# 在服务器上执行 +mysql -h 129.211.213.226 -P 3306 -u root -p cattletrade < add_id_card_field.sql +``` + +## 验证 +执行完成后,可以通过以下 SQL 验证字段是否添加成功: + +```sql +-- 查看字段信息 +SELECT COLUMN_NAME, DATA_TYPE, IS_NULLABLE, COLUMN_DEFAULT, COLUMN_COMMENT +FROM INFORMATION_SCHEMA.COLUMNS +WHERE TABLE_SCHEMA = 'cattletrade' + AND TABLE_NAME = 'member_driver' + AND COLUMN_NAME = 'id_card'; + +-- 查看完整表结构 +DESCRIBE member_driver; +``` + +## 注意事项 +1. 执行前请备份数据库 +2. 确保有足够的数据库权限 +3. 建议在维护时间窗口执行 +4. 执行后重启应用服务以确保更改生效 + +## 相关代码更改 +此数据库更改配合以下代码更改: +- ✅ 后端 Mapper 层已更新,支持 `id_card` 字段的增删改查 +- ✅ 后端 Controller 层已更新,处理 `idCard` 参数 +- ✅ 前端表单已添加身份证图片上传组件 +- ✅ 前端详情页面已添加身份证图片显示组件 + +## 回滚方案 +如果需要回滚,可以执行: +```sql +ALTER TABLE member_driver DROP COLUMN id_card; +``` diff --git a/tradeCattle/README.md b/tradeCattle/README.md new file mode 100644 index 0000000..68281a8 --- /dev/null +++ b/tradeCattle/README.md @@ -0,0 +1,98 @@ +# 牛只交易管理系统 + +## 项目简介 +牛只交易管理系统是一个基于 Spring Boot 的现代化交易管理平台,提供牛只交付管理、设备监控、用户权限管理等功能。系统采用前后端分离架构,后端使用 Spring Boot + MyBatis-Plus 技术栈,集成多种高效的第三方服务和组件。 + +## 技术栈 +- **基础框架**:Spring Boot 2.6.0 +- **ORM 框架**:MyBatis-Plus 3.5.3.2 +- **权限认证**:Sa-Token 1.37.0 +- **数据库连接池**:Druid +- **任务调度**:XXL-Job +- **API 文档**:Swagger Fox 3.0.0 +- **工具库**:Hutool 5.8.25 +- **云服务**:腾讯云短信、对象存储服务 + +## 项目结构 +``` +tradeCattle +├── aiotagro-core // 核心模块 +├── aiotagro-redis // Redis 操作模块 +└── aiotagro-cattle-trade // 业务模块 + ├── controller // 控制器层 + ├── service // 服务层 + ├── mapper // 数据访问层 + ├── entity // 实体类 + ├── dto // 数据传输对象 + ├── vo // 视图对象 + ├── config // 配置类 + └── constant // 常量定义 +``` + +## 核心功能 +1. **交付管理** + - 交付信息录入和管理 + - 设备绑定和监控 + - 交付状态追踪 + +2. **设备管理** + - 客户端设备管理(JbqClient) + - 服务器设备管理(JbqServer) + - 设备日志记录和监控 + - 设备告警管理 + +3. **用户权限管理** + - 用户管理 + - 角色管理 + - 菜单权限管理 + - 基于 Sa-Token 的认证授权 + +4. **系统监控** + - 设备运行状态监控 + - 告警日志管理 + - 系统操作日志 + +## 特色功能 +- **短信通知**:集成腾讯云短信服务,支持验证码发送和通知推送 +- **文件存储**:使用腾讯云对象存储服务,提供可靠的文件存储方案 +- **任务调度**:集成 XXL-Job,支持分布式任务调度 +- **接口文档**:集成 Swagger Fox,提供在线 API 文档和调试功能 + +## 项目配置 +1. **数据库配置** + - 使用 Druid 数据库连接池 + - 支持多数据源配置 + +2. **Redis 配置** + - 独立的 Redis 模块 + - 支持缓存和会话管理 + +3. **权限配置** + - 基于 Sa-Token 的权限认证 + - 支持多种登录方式 + - 灵活的权限控制策略 + +## 开发环境要求 +- JDK 8+ +- Maven 3.6+ +- MySQL 5.7+ +- Redis 5.0+ + +## 部署说明 +1. 克隆项目到本地 +2. 配置数据库连接信息 +3. 配置 Redis 连接信息 +4. 配置腾讯云相关服务密钥 +5. 执行数据库初始化脚本 +6. 使用 Maven 打包项目 +7. 部署运行 + +## 注意事项 +- 确保已正确配置腾讯云服务相关密钥 +- 注意数据库的字符集设置 +- 建议在生产环境使用 Redis 集群 +- 建议配置适当的日志级别和日志存储策略 + +## 版本信息 +- 当前版本:1.0.1 +- 更新日期:2024年 diff --git a/tradeCattle/add_id_card_field.sql b/tradeCattle/add_id_card_field.sql new file mode 100644 index 0000000..9474cba --- /dev/null +++ b/tradeCattle/add_id_card_field.sql @@ -0,0 +1,24 @@ +-- ============================================= +-- 数据库迁移脚本:为 member_driver 表添加 id_card 字段 +-- 用途:存储司机身份证前后面照片地址 +-- 创建时间:2025-10-20 +-- 数据库:MySQL +-- ============================================= + +-- 为 member_driver 表添加 id_card 字段 +-- 字段类型:TEXT(支持长文本) +-- 位置:在 car_img 字段之后 +-- 注释:身份证前后面照片地址(多个URL用逗号分隔) +ALTER TABLE member_driver +ADD COLUMN id_card TEXT COMMENT '身份证前后面照片地址(多个URL用逗号分隔)' +AFTER car_img; + +-- 验证字段是否添加成功 +SELECT COLUMN_NAME, DATA_TYPE, IS_NULLABLE, COLUMN_DEFAULT, COLUMN_COMMENT +FROM INFORMATION_SCHEMA.COLUMNS +WHERE TABLE_SCHEMA = DATABASE() + AND TABLE_NAME = 'member_driver' + AND COLUMN_NAME = 'id_card'; + +-- 显示完整的表结构 +DESCRIBE member_driver; diff --git a/tradeCattle/add_id_card_field_safe.sql b/tradeCattle/add_id_card_field_safe.sql new file mode 100644 index 0000000..9ecec6f --- /dev/null +++ b/tradeCattle/add_id_card_field_safe.sql @@ -0,0 +1,80 @@ +-- ============================================= +-- 数据库迁移脚本:为 member_driver 表添加 id_card 字段 +-- 用途:存储司机身份证前后面照片地址 +-- 创建时间:2025-10-20 +-- 数据库:MySQL +-- 版本:安全版本(包含错误处理) +-- ============================================= + +-- 开始事务 +START TRANSACTION; + +-- 检查表是否存在 +SET @table_exists = ( + SELECT COUNT(*) + FROM INFORMATION_SCHEMA.TABLES + WHERE TABLE_SCHEMA = DATABASE() + AND TABLE_NAME = 'member_driver' +); + +-- 如果表不存在,回滚并退出 +IF @table_exists = 0 THEN + ROLLBACK; + SELECT 'ERROR: member_driver 表不存在' AS result; + LEAVE; +END IF; + +-- 检查字段是否已存在 +SET @column_exists = ( + SELECT COUNT(*) + FROM INFORMATION_SCHEMA.COLUMNS + WHERE TABLE_SCHEMA = DATABASE() + AND TABLE_NAME = 'member_driver' + AND COLUMN_NAME = 'id_card' +); + +-- 如果字段已存在,回滚并退出 +IF @column_exists > 0 THEN + ROLLBACK; + SELECT 'WARNING: id_card 字段已存在,无需添加' AS result; + LEAVE; +END IF; + +-- 添加 id_card 字段 +ALTER TABLE member_driver +ADD COLUMN id_card TEXT COMMENT '身份证前后面照片地址(多个URL用逗号分隔)' +AFTER car_img; + +-- 验证字段是否添加成功 +SET @verify_result = ( + SELECT COUNT(*) + FROM INFORMATION_SCHEMA.COLUMNS + WHERE TABLE_SCHEMA = DATABASE() + AND TABLE_NAME = 'member_driver' + AND COLUMN_NAME = 'id_card' +); + +-- 如果验证失败,回滚 +IF @verify_result = 0 THEN + ROLLBACK; + SELECT 'ERROR: id_card 字段添加失败' AS result; + LEAVE; +END IF; + +-- 提交事务 +COMMIT; + +-- 显示成功信息 +SELECT 'SUCCESS: id_card 字段添加成功' AS result; + +-- 显示字段信息 +SELECT + COLUMN_NAME as '字段名', + DATA_TYPE as '数据类型', + IS_NULLABLE as '允许空值', + COLUMN_DEFAULT as '默认值', + COLUMN_COMMENT as '注释' +FROM INFORMATION_SCHEMA.COLUMNS +WHERE TABLE_SCHEMA = DATABASE() + AND TABLE_NAME = 'member_driver' + AND COLUMN_NAME = 'id_card'; diff --git a/tradeCattle/add_landing_entruck_weight_field.sql b/tradeCattle/add_landing_entruck_weight_field.sql new file mode 100644 index 0000000..88660cf --- /dev/null +++ b/tradeCattle/add_landing_entruck_weight_field.sql @@ -0,0 +1,24 @@ +-- ============================================= +-- 数据库迁移脚本:为 delivery 表添加 landing_entruck_weight 字段 +-- 用途:存储约定单价(元/公斤) +-- 创建时间:2025-01-27 +-- 数据库:MySQL +-- ============================================= + +-- 为 delivery 表添加 landing_entruck_weight 字段 +-- 字段类型:DECIMAL(10,2)(支持小数点后2位) +-- 位置:在 sale_price 字段之后 +-- 注释:约定单价(元/公斤) +ALTER TABLE delivery +ADD COLUMN firm_price DECIMAL(10,2) COMMENT '约定单价(元/公斤)' +AFTER sale_price; + +-- 验证字段是否添加成功 +SELECT COLUMN_NAME, DATA_TYPE, IS_NULLABLE, COLUMN_DEFAULT, COLUMN_COMMENT +FROM INFORMATION_SCHEMA.COLUMNS +WHERE TABLE_SCHEMA = DATABASE() + AND TABLE_NAME = 'delivery' + AND COLUMN_NAME = 'firm_price'; + +-- 显示完整的表结构 +DESCRIBE delivery; diff --git a/tradeCattle/aiotagro-cattle-trade/pom.xml b/tradeCattle/aiotagro-cattle-trade/pom.xml new file mode 100644 index 0000000..8b31452 --- /dev/null +++ b/tradeCattle/aiotagro-cattle-trade/pom.xml @@ -0,0 +1,264 @@ + + + 4.0.0 + + com.aiotagro + cattletrade + 1.0.1 + + + aiotagro-cattletrade + + + UTF-8 + 1.8 + 2.6.13 + + + + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-starter-validation + + + + + + org.springframework.boot + spring-boot-devtools + true + + + + org.projectlombok + lombok + + + + + org.springframework.boot + spring-boot-dependencies + ${spring-boot.version} + pom + import + + + + + com.github.tobato + fastdfs-client + + + + + + com.alibaba + druid + + + + + + com.mysql + mysql-connector-j + 8.4.0 + + + + + + com.baomidou + mybatis-plus-boot-starter + + + + + com.github.pagehelper + pagehelper-spring-boot-starter + 1.4.7 + + + + + + eu.bitwalker + UserAgentUtils + + + + + com.github.oshi + oshi-core + + + + + commons-io + commons-io + + + + + org.apache.poi + poi-ooxml + + + + + com.alibaba.fastjson2 + fastjson2 + + + + + + com.alibaba + transmittable-thread-local + + + + + + org.apache.commons + commons-pool2 + + + + cn.hutool + hutool-all + + + + + + com.aiotagro + aiotagro-redis + + + + + com.github.lianjiatech + retrofit-spring-boot-starter + + + + + + com.alibaba + easyexcel + 3.1.0 + + + poi-ooxml-schemas + org.apache.poi + + + + + org.projectlombok + lombok + + + + com.github.binarywang + weixin-java-miniapp + 3.3.0 + + + + + + + org.apache.commons + commons-lang3 + + + + org.apache.commons + commons-collections4 + 4.4 + + + + com.xuxueli + xxl-job-core + 2.4.1 + + + + + + org.springframework.boot + spring-boot-dependencies + ${spring-boot.version} + pom + import + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.8.1 + + ${java.version} + ${java.version} + ${project.build.sourceEncoding} + + + + org.springframework.boot + spring-boot-maven-plugin + ${spring-boot.version} + + true + true + + + + + repackage + + + + + + + + + + public + aliyun nexus + https://maven.aliyun.com/repository/public + + true + + + + + + + public + aliyun nexus + https://maven.aliyun.com/repository/public + + true + + + false + + + + + \ No newline at end of file diff --git a/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/AiotagroCattleTradeApplication.java b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/AiotagroCattleTradeApplication.java new file mode 100644 index 0000000..0d3b50b --- /dev/null +++ b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/AiotagroCattleTradeApplication.java @@ -0,0 +1,22 @@ +package com.aiotagro.cattletrade; + +import cn.dev33.satoken.SaManager; +import com.mzt.logapi.starter.annotation.EnableLogRecord; +import org.mybatis.spring.annotation.MapperScan; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.scheduling.annotation.EnableScheduling; +import org.springframework.transaction.annotation.EnableTransactionManagement; + +@SpringBootApplication(scanBasePackages = {"com.aiotagro.cattletrade", "com.aiotagro.common"}) +@EnableTransactionManagement +@EnableScheduling +@EnableLogRecord(tenant = "com.aiotagro.cattletrade") +@MapperScan("com.aiotagro.cattletrade.domain.mapper") +public class AiotagroCattleTradeApplication { + public static void main(String[] args) { + SpringApplication.run(AiotagroCattleTradeApplication.class, args); + System.out.println(SaManager.getConfig()); + } + +} diff --git a/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/controller/CommonControl.java b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/controller/CommonControl.java new file mode 100644 index 0000000..3ca06c6 --- /dev/null +++ b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/controller/CommonControl.java @@ -0,0 +1,42 @@ +package com.aiotagro.cattletrade.business.controller; + +import com.aiotagro.cattletrade.business.dto.FileDto; +import com.aiotagro.common.core.utils.TencentCloudUtil; +import com.aiotagro.common.core.web.domain.AjaxResult; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.multipart.MultipartFile; + +@RestController +@RequestMapping("/common") +public class CommonControl { + + + private static final Logger logger = LoggerFactory.getLogger(CommonControl.class); + + /** + * 文件上传 + * + * @param file + * @return + */ + @PostMapping("/upload") + public AjaxResult upload(@RequestParam("file") MultipartFile file) { + logger.info("文件上传..."); + try { + String fileUrl = TencentCloudUtil.upload(file); + FileDto fileDto = new FileDto(); + fileDto.setSrc(fileUrl); + return AjaxResult.success("上传成功!", fileDto); + } catch (Exception e) { + logger.error("图片上传异常", e); + } + return AjaxResult.error("上传失败!"); + } + + +} diff --git a/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/controller/DeliveryController.java b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/controller/DeliveryController.java new file mode 100644 index 0000000..d96d98e --- /dev/null +++ b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/controller/DeliveryController.java @@ -0,0 +1,545 @@ +package com.aiotagro.cattletrade.business.controller; + + +import cn.dev33.satoken.annotation.SaCheckPermission; +import com.aiotagro.cattletrade.business.dto.DeliverListDto; +import com.aiotagro.cattletrade.business.dto.DeliveryAddDto; +import com.aiotagro.cattletrade.business.dto.DeliveryCreateDto; +import com.aiotagro.cattletrade.business.dto.DeliveryEditDto; +import com.aiotagro.cattletrade.business.dto.DeliveryQueryDto; +import com.aiotagro.cattletrade.business.dto.DeviceAssignDto; +import com.aiotagro.cattletrade.business.entity.Delivery; +import com.aiotagro.cattletrade.business.entity.DeliveryDevice; +import com.aiotagro.cattletrade.business.entity.JbqClient; +import com.aiotagro.cattletrade.business.entity.XqClient; +import com.aiotagro.cattletrade.business.service.IDeliveryService; +import com.aiotagro.cattletrade.business.service.IDeliveryDeviceService; +import com.aiotagro.cattletrade.business.service.IJbqClientService; +import com.aiotagro.cattletrade.business.service.IXqClientService; +import com.aiotagro.common.core.context.SecurityContextHolder; +import com.aiotagro.common.core.utils.SecurityUtil; +import com.aiotagro.common.core.web.domain.AjaxResult; +import com.aiotagro.common.core.web.domain.PageResultResponse; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * 运送清单表 前端控制器 + * + * @author admin + * @since 2024-12-26 + */ +@RestController +@RequestMapping("/delivery") +public class DeliveryController { + + @Autowired + private IDeliveryService deliveryService; + + @Autowired + private IDeliveryDeviceService deliveryDeviceService; + + @Autowired + private IJbqClientService jbqClientService; + + @Autowired + private IXqClientService xqClientService; + + + + /** + * 小程序运送清单-分页查询 + * + * @param dto + * @return + */ + @SaCheckPermission("delivery:list") + @PostMapping(value = "/pageQuery") + public PageResultResponse pageQuery(@RequestBody DeliveryQueryDto dto) { + return deliveryService.pageQuery(dto); + } + + /** + * 小程序运送清单-我要装车 + * @param dto + * @return + */ + @SaCheckPermission("delivery:add") + @PostMapping(value = "/add") + public AjaxResult add(@RequestBody DeliveryAddDto dto) { + try{ + return deliveryService.addDelivery(dto); + }catch (Exception e){ + e.printStackTrace(); + return AjaxResult.error(e.getMessage()); + } + } + + /** + * 运送清单-创建(PC端新增接口) + * @param dto + * @return + */ + @SaCheckPermission("delivery:add") + @PostMapping(value = "/create") + public AjaxResult create(@Validated @RequestBody DeliveryCreateDto dto) { + try{ + return deliveryService.createDelivery(dto); + }catch (Exception e){ + e.printStackTrace(); + return AjaxResult.error(e.getMessage()); + } + } + + /** + * 装车订单-新增(PC端装车订单创建接口) + * @param params + * @return + */ + @SaCheckPermission("delivery:add") + @PostMapping(value = "/addDeliveryOrder") + public AjaxResult addDeliveryOrder(@RequestBody Map params) { + try { + // 获取参数值 + String deliveryTitle = (String) params.get("deliveryTitle"); + Integer ratedQuantity = null; + if (params.get("ratedQuantity") != null) { + Object ratedQuantityObj = params.get("ratedQuantity"); + if (ratedQuantityObj instanceof Integer) { + ratedQuantity = (Integer) ratedQuantityObj; + } else if (ratedQuantityObj instanceof String) { + try { + ratedQuantity = Integer.parseInt((String) ratedQuantityObj); + } catch (NumberFormatException e) { + return AjaxResult.error("装车数量格式不正确"); + } + } + } + + String supplierId = (String) params.get("supplierId"); + + Integer fundId = null; + if (params.get("fundId") != null) { + Object fundIdObj = params.get("fundId"); + if (fundIdObj instanceof Integer) { + fundId = (Integer) fundIdObj; + } else if (fundIdObj instanceof String) { + try { + fundId = Integer.parseInt((String) fundIdObj); + } catch (NumberFormatException e) { + return AjaxResult.error("资金方ID格式不正确"); + } + } + } + + Integer driverId = null; + if (params.get("driverId") != null) { + Object driverIdObj = params.get("driverId"); + if (driverIdObj instanceof Integer) { + driverId = (Integer) driverIdObj; + } else if (driverIdObj instanceof String) { + try { + driverId = Integer.parseInt((String) driverIdObj); + } catch (NumberFormatException e) { + return AjaxResult.error("司机ID格式不正确"); + } + } + } + + Integer buyerId = null; + if (params.get("buyerId") != null) { + Object buyerIdObj = params.get("buyerId"); + if (buyerIdObj instanceof Integer) { + buyerId = (Integer) buyerIdObj; + } else if (buyerIdObj instanceof String) { + try { + buyerId = Integer.parseInt((String) buyerIdObj); + } catch (NumberFormatException e) { + return AjaxResult.error("采购商ID格式不正确"); + } + } + } + + Double buyerPrice = null; + if (params.get("buyerPrice") != null) { + Object buyerPriceObj = params.get("buyerPrice"); + if (buyerPriceObj instanceof Double) { + buyerPrice = (Double) buyerPriceObj; + } else if (buyerPriceObj instanceof String) { + try { + buyerPrice = Double.parseDouble((String) buyerPriceObj); + } catch (NumberFormatException e) { + return AjaxResult.error("采购价格格式不正确"); + } + } + } + + Double salePrice = null; + if (params.get("salePrice") != null) { + Object salePriceObj = params.get("salePrice"); + if (salePriceObj instanceof Double) { + salePrice = (Double) salePriceObj; + } else if (salePriceObj instanceof String) { + try { + salePrice = Double.parseDouble((String) salePriceObj); + } catch (NumberFormatException e) { + return AjaxResult.error("销售价格格式不正确"); + } + } + } + + Double firmPrice = null; + if (params.get("firmPrice") != null) { + Object firmPriceObj = params.get("firmPrice"); + if (firmPriceObj instanceof Double) { + firmPrice = (Double) firmPriceObj; + } else if (firmPriceObj instanceof String) { + try { + firmPrice = Double.parseDouble((String) firmPriceObj); + } catch (NumberFormatException e) { + return AjaxResult.error("约定单价格式不正确"); + } + } + } + String startLocation = (String) params.get("startLocation"); + + String startLat = null; + if (params.get("startLat") != null) { + Object startLatObj = params.get("startLat"); + if (startLatObj instanceof String) { + startLat = (String) startLatObj; + } else if (startLatObj instanceof Double) { + startLat = String.valueOf((Double) startLatObj); + } else if (startLatObj instanceof Integer) { + startLat = String.valueOf((Integer) startLatObj); + } + } + + String startLon = null; + if (params.get("startLon") != null) { + Object startLonObj = params.get("startLon"); + if (startLonObj instanceof String) { + startLon = (String) startLonObj; + } else if (startLonObj instanceof Double) { + startLon = String.valueOf((Double) startLonObj); + } else if (startLonObj instanceof Integer) { + startLon = String.valueOf((Integer) startLonObj); + } + } + + String endLocation = (String) params.get("endLocation"); + + String endLat = null; + if (params.get("endLat") != null) { + Object endLatObj = params.get("endLat"); + if (endLatObj instanceof String) { + endLat = (String) endLatObj; + } else if (endLatObj instanceof Double) { + endLat = String.valueOf((Double) endLatObj); + } else if (endLatObj instanceof Integer) { + endLat = String.valueOf((Integer) endLatObj); + } + } + + String endLon = null; + if (params.get("endLon") != null) { + Object endLonObj = params.get("endLon"); + if (endLonObj instanceof String) { + endLon = (String) endLonObj; + } else if (endLonObj instanceof Double) { + endLon = String.valueOf((Double) endLonObj); + } else if (endLonObj instanceof Integer) { + endLon = String.valueOf((Integer) endLonObj); + } + } + + // 参数验证 + if (deliveryTitle == null || deliveryTitle.trim().isEmpty()) { + return AjaxResult.error("订单标题不能为空"); + } + if (ratedQuantity == null || ratedQuantity <= 0) { + return AjaxResult.error("装车数量必须大于0"); + } + if (startLocation == null || startLocation.trim().isEmpty()) { + return AjaxResult.error("起始地不能为空"); + } + if (endLocation == null || endLocation.trim().isEmpty()) { + return AjaxResult.error("目的地不能为空"); + } + + // 获取当前登录用户 + Integer userId = SecurityUtil.getCurrentUserId(); + String userName = SecurityContextHolder.getUserName(); // 使用SecurityContextHolder获取用户名 + + // 生成运输单号 + LocalDateTime now = LocalDateTime.now(); + String deliveryNumber = "ZC" + now.format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss")); + + // 创建装车订单 + Delivery delivery = new Delivery(); + delivery.setDeliveryNumber(deliveryNumber); + delivery.setDeliveryTitle(deliveryTitle); + delivery.setRatedQuantity(ratedQuantity); + delivery.setSupplierId(supplierId); + delivery.setFundId(fundId); + delivery.setDriverId(driverId); + delivery.setBuyerId(buyerId); + delivery.setBuyerPrice(buyerPrice); + delivery.setSalePrice(salePrice); + delivery.setFirmPrice(firmPrice); + delivery.setStartLocation(startLocation); + delivery.setStartLat(startLat); + delivery.setStartLon(startLon); + delivery.setEndLocation(endLocation); + delivery.setEndLat(endLat); + delivery.setEndLon(endLon); + delivery.setStatus(1); // 待装车 + delivery.setCreatedBy(userId); + delivery.setCreateByName(userName); + delivery.setCreateTime(new Date()); + + // 保存装车订单 + boolean saved = deliveryService.save(delivery); + if (!saved) { + return AjaxResult.error("创建装车订单失败"); + } + + return AjaxResult.success("装车订单创建成功"); + } catch (Exception e) { + e.printStackTrace(); + return AjaxResult.error("创建装车订单失败:" + e.getMessage()); + } + } + + /** + * 运送清单-编辑(PC端编辑接口) + * @param dto + * @return + */ + @SaCheckPermission("delivery:edit") + @PostMapping(value = "/updateDeliveryInfo") + public AjaxResult updateDeliveryInfo(@Validated @RequestBody DeliveryEditDto dto) { + try{ + return deliveryService.updateDeliveryInfo(dto); + }catch (Exception e){ + e.printStackTrace(); + return AjaxResult.error(e.getMessage()); + } + } + + /** + * 小程序运送清单-查询详情 + * @param id + * @return + */ + @SaCheckPermission("delivery:view") + @GetMapping(value = "/view") + public AjaxResult view(@RequestParam Integer id) { + return deliveryService.viewDelivery(id); + } + + + /** + * 小程序运送清单-核验提交 + * @param delivery + * @return + */ + @SaCheckPermission("delivery:check") + @PostMapping(value = "/submitCheck") + public AjaxResult submitCheck(@RequestBody Delivery delivery) { + try{ + //查询当前的登录用户,即核验人 + delivery.setCheckBy(SecurityUtil.getCurrentUserId()); + delivery.setCheckTime(new Date()); + delivery.setCheckByName(SecurityUtil.getUserName()); + boolean b = deliveryService.updateById(delivery); + if(b){ + return AjaxResult.success("核验提交成功!"); + }else{ + return AjaxResult.error("核验提交失败!"); + } + }catch (Exception e){ + return AjaxResult.error("核验提交失败!"); + } + } + + /** + * 后台系统运送清单-分页查询 + * + * @param dto + * @return + */ + @SaCheckPermission("delivery:list") + @PostMapping(value = "/pageQueryList") + public PageResultResponse pageQueryList(@RequestBody DeliveryQueryDto dto) { + return deliveryService.pageQuery(dto); + } + + /** + * 后台系统运送清单-分页查询(别名) + * + * @param dto + * @return + */ + @SaCheckPermission("delivery:list") + @PostMapping(value = "/pageDeliveryOrderList") + public PageResultResponse pageDeliveryOrderList(@RequestBody DeliverListDto dto) { + return deliveryService.pageQueryListLog(dto); + } + + /** + * 后台运单详情 + */ + @SaCheckPermission("delivery:view") + @GetMapping("/detail") + public AjaxResult detail(@RequestParam Integer id) { + return deliveryService.detail(id); + } + + /** + * 查看运送订单详情(别名) + */ + @SaCheckPermission("delivery:view") + @GetMapping("/viewDeliveryOrder") + public AjaxResult viewDeliveryOrder(@RequestParam Integer deliveryId) { + return deliveryService.detail(deliveryId); + } + + /** + * 测试司机信息查询 + */ + @GetMapping("/testDriverQuery/{deliveryId}") + public AjaxResult testDriverQuery(@PathVariable("deliveryId") Integer deliveryId) { + try { + System.out.println("测试司机信息查询,deliveryId: " + deliveryId); + AjaxResult result = deliveryService.detail(deliveryId); + System.out.println("查询结果: " + result); + return result; + } catch (Exception e) { + e.printStackTrace(); + return AjaxResult.error("测试失败: " + e.getMessage()); + } + } + + /** + * 分配设备(支持智能耳标和智能项圈) + * @param dto + * @return + */ + @SaCheckPermission("delivery:assign") + @PostMapping(value = "/arrangeJbq") + public AjaxResult arrangeJbq(@RequestBody DeviceAssignDto dto) { + try { + if (dto.getDeviceIds() == null || dto.getDeviceIds().isEmpty()) { + return AjaxResult.error("请选择要分配的设备"); + } + + // 按设备类型分组 + Map> deviceGroups = dto.getDeviceIds().stream() + .collect(Collectors.groupingBy(DeviceAssignDto.DeviceInfo::getDeviceTypeId)); + + // 处理每种设备类型 + for (Map.Entry> entry : deviceGroups.entrySet()) { + Integer deviceType = entry.getKey(); + List devices = entry.getValue(); + + // 先删除该运单的现有设备分配(按设备类型) + deliveryDeviceService.lambdaUpdate() + .eq(DeliveryDevice::getDeliveryId, dto.getDeliveryId()) + .eq(DeliveryDevice::getDeviceType, deviceType) + .remove(); + + // 添加新的设备分配 + for (DeviceAssignDto.DeviceInfo deviceInfo : devices) { + DeliveryDevice deliveryDevice = new DeliveryDevice(); + deliveryDevice.setDeliveryId(dto.getDeliveryId()); + deliveryDevice.setDeviceId(deviceInfo.getDeviceId()); + deliveryDevice.setDeviceType(deviceType); + deliveryDeviceService.save(deliveryDevice); + + // 更新设备的运单号和车牌号 + updateDeviceDeliveryInfo(deviceInfo.getDeviceId(), deviceType, dto.getDeliveryId(), dto.getLicensePlate()); + } + } + + return AjaxResult.success("设备分配成功!"); + } catch (Exception e) { + e.printStackTrace(); + return AjaxResult.error("设备分配失败:" + e.getMessage()); + } + } + + /** + * 更新设备的运单号和车牌号 + */ + private void updateDeviceDeliveryInfo(String deviceId, Integer deviceType, Integer deliveryId, String licensePlate) { + try { + // 获取运单号 + Delivery delivery = deliveryService.getById(deliveryId); + String deliveryNumber = delivery != null ? delivery.getDeliveryNumber() : ""; + + if (deviceType == 2) { + // 更新智能耳标 + LambdaUpdateWrapper jbqUpdateWrapper = new LambdaUpdateWrapper<>(); + jbqUpdateWrapper.eq(JbqClient::getDeviceId, deviceId); + jbqUpdateWrapper.set(JbqClient::getDeliveryNumber, deliveryNumber); + jbqUpdateWrapper.set(JbqClient::getLicensePlate, licensePlate); + jbqClientService.update(jbqUpdateWrapper); + } else if (deviceType == 3) { + // 更新智能项圈 - 兼容deviceId和sn两种字段 + LambdaUpdateWrapper xqUpdateWrapper = new LambdaUpdateWrapper<>(); + xqUpdateWrapper.and(w -> w.eq(XqClient::getDeviceId, deviceId) + .or() + .eq(XqClient::getSn, Long.parseLong(deviceId))); + xqUpdateWrapper.set(XqClient::getDeliveryId, deliveryId); + xqUpdateWrapper.set(XqClient::getDeliveryNumber, deliveryNumber); + xqUpdateWrapper.set(XqClient::getLicensePlate, licensePlate); + xqClientService.update(xqUpdateWrapper); + } + } catch (Exception e) { + System.out.println("更新设备运单信息失败: " + e.getMessage()); + e.printStackTrace(); + } + } + + /** + * 更新运单状态 + */ + @SaCheckPermission("delivery:status") + @PostMapping("/updateStatus") + public AjaxResult updateStatus(@RequestBody Map params) { + try { + Integer id = (Integer) params.get("id"); + Integer status = (Integer) params.get("status"); + + if (id == null || status == null) { + return AjaxResult.error("运单ID和状态不能为空"); + } + + Delivery delivery = new Delivery(); + delivery.setId(id); + delivery.setStatus(status); + + boolean success = deliveryService.updateById(delivery); + + if (success) { + return AjaxResult.success("状态更新成功"); + } else { + return AjaxResult.error("状态更新失败"); + } + } catch (Exception e) { + e.printStackTrace(); + return AjaxResult.error("状态更新失败:" + e.getMessage()); + } + } + +} diff --git a/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/controller/DeliveryDeviceController.java b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/controller/DeliveryDeviceController.java new file mode 100644 index 0000000..71ef7f9 --- /dev/null +++ b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/controller/DeliveryDeviceController.java @@ -0,0 +1,153 @@ +package com.aiotagro.cattletrade.business.controller; + +import cn.dev33.satoken.annotation.SaCheckPermission; +import com.aiotagro.cattletrade.business.entity.DeliveryDevice; +import com.aiotagro.cattletrade.business.entity.JbqClient; +import com.aiotagro.cattletrade.business.entity.XqClient; +import com.aiotagro.cattletrade.business.service.IDeliveryDeviceService; +import com.aiotagro.cattletrade.business.service.IJbqClientService; +import com.aiotagro.cattletrade.business.service.IXqClientService; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.aiotagro.common.core.web.domain.AjaxResult; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + *

+ * 运单设备表 前端控制器 + *

+ * + * @author admin + * @since 2024-12-26 + */ +@RestController +@RequestMapping("/deliveryDevice") +public class DeliveryDeviceController { + + @Autowired + private IDeliveryDeviceService deliveryDeviceService; + + @Autowired + private IJbqClientService jbqClientService; + + @Autowired + private IXqClientService xqClientService; + + /** + * 根据运送清单ID查询耳标列表 + */ + @SaCheckPermission("delivery:view") + @PostMapping(value = "/pageJbqList") + public AjaxResult pageJbqList(@RequestBody Map params) { + Integer deliveryId = (Integer) params.get("deliveryId"); + return jbqClientService.getDevicesByDeliveryId(deliveryId, 2); // 2表示耳标类型 + } + + /** + * 根据运送清单ID查询所有设备列表(耳标+项圈) + */ + @SaCheckPermission("delivery:view") + @PostMapping(value = "/pageDeviceList") + public AjaxResult pageDeviceList(@RequestBody Map params) { + Integer deliveryId = (Integer) params.get("deliveryId"); + + if (deliveryId == null) { + return AjaxResult.error("运送清单ID不能为空"); + } + + List> allDevices = new ArrayList<>(); + + try { + // 查询delivery_device表中的图片数据 + LambdaQueryWrapper deliveryDeviceWrapper = new LambdaQueryWrapper<>(); + deliveryDeviceWrapper.eq(DeliveryDevice::getDeliveryId, deliveryId); + List deliveryDevices = deliveryDeviceService.list(deliveryDeviceWrapper); + + // 创建设备ID到图片数据的映射 + Map deviceImageMap = new HashMap<>(); + for (DeliveryDevice dd : deliveryDevices) { + deviceImageMap.put(dd.getDeviceId(), dd); + } + + // 查询耳标设备 + AjaxResult earTagResult = jbqClientService.getDevicesByDeliveryId(deliveryId, 2); + if (earTagResult.get("code").equals(200) && earTagResult.get("data") != null) { + Object earDataObj = earTagResult.get("data"); + List earTagDevices; + if (earDataObj instanceof Map) { + Map earTagData = (Map) earDataObj; + earTagDevices = (List) earTagData.get("rows"); + } else { + earTagDevices = (List) earDataObj; // 兼容空列表直接返回的情况 + } + for (JbqClient device : earTagDevices) { + Map deviceMap = new HashMap<>(); + deviceMap.put("deviceId", device.getDeviceId()); + deviceMap.put("deviceType", "2"); + deviceMap.put("deviceTypeName", "智能耳标"); + deviceMap.put("deviceVoltage", device.getDeviceVoltage()); + deviceMap.put("deviceTemp", device.getDeviceTemp()); + deviceMap.put("latitude", device.getLatitude()); + deviceMap.put("longitude", device.getLongitude()); + deviceMap.put("walkSteps", device.getWalkSteps()); + deviceMap.put("yWalkSteps", device.getYWalkSteps()); + deviceMap.put("isWare", "0"); // 耳标默认未佩戴 + + // 获取实际的图片数据 + DeliveryDevice deviceImages = deviceImageMap.get(device.getDeviceId()); + deviceMap.put("frontImg", deviceImages != null ? deviceImages.getFrontImg() : ""); + deviceMap.put("sideImg", deviceImages != null ? deviceImages.getSideImg() : ""); + deviceMap.put("hipImg", deviceImages != null ? deviceImages.getHipImg() : ""); + + allDevices.add(deviceMap); + } + } + + // 查询项圈设备 + AjaxResult collarResult = xqClientService.getDevicesByDeliveryId(deliveryId, 3); + if (collarResult.get("code").equals(200) && collarResult.get("data") != null) { + Object collarDataObj = collarResult.get("data"); + List collarDevices; + if (collarDataObj instanceof Map) { + Map collarData = (Map) collarDataObj; + collarDevices = (List) collarData.get("rows"); + } else { + collarDevices = (List) collarDataObj; // 兼容空列表直接返回的情况 + } + for (XqClient device : collarDevices) { + Map deviceMap = new HashMap<>(); + String deviceId = device.getSn() != null ? device.getSn().toString() : device.getDeviceId(); + deviceMap.put("deviceId", deviceId); + deviceMap.put("deviceType", "3"); + deviceMap.put("deviceTypeName", "智能项圈"); + deviceMap.put("battery", device.getBattery()); + deviceMap.put("temperature", device.getTemperature()); + deviceMap.put("latitude", device.getLatitude()); + deviceMap.put("longitude", device.getLongitude()); + deviceMap.put("steps", device.getSteps()); + deviceMap.put("ySteps", device.getYSteps()); + deviceMap.put("isWare", device.getIsWear() != null ? device.getIsWear().toString() : "0"); + + // 获取实际的图片数据 + DeliveryDevice deviceImages = deviceImageMap.get(deviceId); + deviceMap.put("frontImg", deviceImages != null ? deviceImages.getFrontImg() : ""); + deviceMap.put("sideImg", deviceImages != null ? deviceImages.getSideImg() : ""); + deviceMap.put("hipImg", deviceImages != null ? deviceImages.getHipImg() : ""); + + allDevices.add(deviceMap); + } + } + + return AjaxResult.success(allDevices); + } catch (Exception e) { + e.printStackTrace(); + return AjaxResult.error("查询设备列表失败:" + e.getMessage()); + } + } + +} diff --git a/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/controller/JbqClientController.java b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/controller/JbqClientController.java new file mode 100644 index 0000000..ba68f27 --- /dev/null +++ b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/controller/JbqClientController.java @@ -0,0 +1,147 @@ +package com.aiotagro.cattletrade.business.controller; + + +import com.aiotagro.cattletrade.business.dto.DeviceDto; +import com.aiotagro.cattletrade.business.entity.JbqClient; +import com.aiotagro.cattletrade.business.entity.DeliveryDevice; +import com.aiotagro.cattletrade.business.mapper.DeliveryDeviceMapper; +import com.aiotagro.cattletrade.business.service.IJbqClientService; +import com.aiotagro.cattletrade.business.service.IDeliveryDeviceService; +import com.aiotagro.common.core.web.domain.AjaxResult; +import com.aiotagro.common.core.web.domain.PageResultResponse; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.List; +import java.util.Map; +import java.util.HashMap; +import java.util.stream.Collectors; + +/** + *

+ * 智能耳标表 前端控制器 + *

+ * + * @author admin + * @since 2024-12-26 + */ +@RestController +@RequestMapping("/jbqClient") +public class JbqClientController { + @Autowired + private IJbqClientService jbqClientService; + + @Autowired + private IDeliveryDeviceService deliveryDeviceService; + + @Autowired + private DeliveryDeviceMapper deliveryDeviceMapper; + + /** + * 查询分页列表 + */ + @PostMapping(value = "/list") + public PageResultResponse jbqList(@RequestBody DeviceDto dto) { + return jbqClientService.jbqList(dto); + } + + /** + * 根据运送清单ID查询耳标列表 + */ + @PostMapping(value = "/pageJbqListByDeliveryId") + public AjaxResult pageJbqListByDeliveryId(@RequestBody Map params) { + Object deliveryIdObj = params.get("deliveryId"); + Integer deliveryId = null; + if (deliveryIdObj != null) { + if (deliveryIdObj instanceof Integer) { + deliveryId = (Integer) deliveryIdObj; + } else if (deliveryIdObj instanceof String) { + deliveryId = Integer.valueOf((String) deliveryIdObj); + } + } + return jbqClientService.getDevicesByDeliveryId(deliveryId, 2); // 2表示耳标类型 + } + + /** + * 测试查询运单设备关联 + */ + @PostMapping(value = "/testDeliveryDevices") + public AjaxResult testDeliveryDevices(@RequestBody Map params) { + Object deliveryIdObj = params.get("deliveryId"); + Integer deliveryId = null; + if (deliveryIdObj != null) { + if (deliveryIdObj instanceof Integer) { + deliveryId = (Integer) deliveryIdObj; + } else if (deliveryIdObj instanceof String) { + deliveryId = Integer.valueOf((String) deliveryIdObj); + } + } + System.out.println("=== 测试查询运单设备关联,deliveryId: " + deliveryId); + + if (deliveryId == null) { + return AjaxResult.error("运送清单ID不能为空"); + } + + // 查询所有设备类型 + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(DeliveryDevice::getDeliveryId, deliveryId); + List allDevices = deliveryDeviceMapper.selectList(wrapper); + + System.out.println("=== 运单 " + deliveryId + " 关联的所有设备记录数: " + allDevices.size()); + for (DeliveryDevice device : allDevices) { + System.out.println("=== 设备记录: " + device); + } + + // 按设备类型分组 + Map> devicesByType = allDevices.stream() + .collect(Collectors.groupingBy(DeliveryDevice::getDeviceType)); + + Map result = new HashMap<>(); + result.put("totalDevices", allDevices.size()); + result.put("devicesByType", devicesByType); + result.put("allDevices", allDevices); + + return AjaxResult.success(result); + } + + /** + * 测试查询未分配设备 + */ + @PostMapping(value = "/testUnassigned") + public AjaxResult testUnassigned(@RequestBody Map params) { + try { + System.out.println("测试查询未分配智能耳标,参数: " + params); + + // 直接查询jbq_client表 + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + List devices = jbqClientService.list(wrapper); + + System.out.println("智能耳标总数: " + devices.size()); + + // 查询已分配的设备ID + LambdaQueryWrapper deliveryWrapper = new LambdaQueryWrapper<>(); + deliveryWrapper.eq(DeliveryDevice::getDeviceType, 2); + List assignedDevices = deliveryDeviceService.list(deliveryWrapper); + + System.out.println("已分配智能耳标数量: " + assignedDevices.size()); + + // 筛选未分配的设备 + List assignedDeviceIds = assignedDevices.stream() + .map(DeliveryDevice::getDeviceId) + .collect(Collectors.toList()); + + List unassignedDevices = devices.stream() + .filter(device -> !assignedDeviceIds.contains(device.getDeviceId())) + .collect(Collectors.toList()); + + System.out.println("未分配智能耳标数量: " + unassignedDevices.size()); + + return AjaxResult.success("测试成功", unassignedDevices); + } catch (Exception e) { + e.printStackTrace(); + return AjaxResult.error("测试失败: " + e.getMessage()); + } + } + +} diff --git a/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/controller/JbqClientLogController.java b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/controller/JbqClientLogController.java new file mode 100644 index 0000000..e36509e --- /dev/null +++ b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/controller/JbqClientLogController.java @@ -0,0 +1,34 @@ +package com.aiotagro.cattletrade.business.controller; + + +import com.aiotagro.cattletrade.business.dto.JbqLogDto; +import com.aiotagro.cattletrade.business.service.IJbqClientLogService; +import com.aiotagro.common.core.web.domain.PageResultResponse; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +/** + *

+ * 智能耳标日志表 前端控制器 + *

+ * + * @author admin + * @since 2024-12-26 + */ +@RestController +@RequestMapping("/jbqClientLog") +public class JbqClientLogController { + + @Autowired + private IJbqClientLogService jbqClientLogService; + + /** + * 查询分页列表 + */ + @PostMapping(value = "/jbqLogList") + public PageResultResponse jbqList(@RequestBody JbqLogDto dto) { + return jbqClientLogService.jbqLogList(dto); + } + + +} diff --git a/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/controller/JbqServerController.java b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/controller/JbqServerController.java new file mode 100644 index 0000000..4f30537 --- /dev/null +++ b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/controller/JbqServerController.java @@ -0,0 +1,114 @@ +package com.aiotagro.cattletrade.business.controller; + +import cn.dev33.satoken.annotation.SaCheckPermission; +import com.aiotagro.cattletrade.business.service.IJbqServerService; +import com.aiotagro.common.core.web.domain.AjaxResult; +import com.aiotagro.common.core.web.domain.PageResultResponse; +import com.github.pagehelper.Page; +import com.github.pagehelper.PageHelper; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.Map; + +/** + * 主机设备控制器 + * + * @author System + * @date 2025-01-16 + */ +@RestController +@RequestMapping("/jbqServer") +public class JbqServerController { + + @Autowired + private IJbqServerService jbqServerService; + + /** + * 主机设备列表查询 + */ + @SaCheckPermission("delivery:view") + @PostMapping("/jbqCheckList") + public PageResultResponse> jbqCheckList(@RequestBody Map params) { + try { + Integer pageNum = params.get("pageNum") != null ? (Integer) params.get("pageNum") : 1; + Integer pageSize = params.get("pageSize") != null ? (Integer) params.get("pageSize") : 10; + String deviceId = (String) params.get("deviceId"); + + Page> result = PageHelper.startPage(pageNum, pageSize); + + // 查询主机设备列表 + // 这里可以根据实际需求实现主机设备查询逻辑 + // 暂时返回空列表,避免编译错误 + return new PageResultResponse<>(0L, java.util.Collections.emptyList()); + } catch (Exception e) { + e.printStackTrace(); + return new PageResultResponse<>(0L, java.util.Collections.emptyList()); + } + } + + @SaCheckPermission("delivery:view") + @PostMapping("/serverList") + public PageResultResponse> serverList(@RequestBody Map params) { + try { + Integer pageNum = params.get("pageNum") != null ? (Integer) params.get("pageNum") : 1; + Integer pageSize = params.get("pageSize") != null ? (Integer) params.get("pageSize") : 10; + String deviceId = (String) params.get("deviceId"); + + Page> result = PageHelper.startPage(pageNum, pageSize); + + // 调用服务层查询主机设备列表 + com.aiotagro.cattletrade.business.dto.DeviceDto dto = new com.aiotagro.cattletrade.business.dto.DeviceDto(); + dto.setDeviceId(deviceId); + + PageResultResponse response = jbqServerService.serverList(dto); + return response; + } catch (Exception e) { + e.printStackTrace(); + return new PageResultResponse<>(0L, java.util.Collections.emptyList()); + } + } + + /** + * 查询分页列表(通用) + */ + @PostMapping(value = "/pageQuery") + public PageResultResponse pageQuery(@RequestBody Map params) { + try { + Integer pageNum = params.get("pageNum") != null ? (Integer) params.get("pageNum") : 1; + Integer pageSize = params.get("pageSize") != null ? (Integer) params.get("pageSize") : 10; + String deviceId = (String) params.get("deviceId"); + + Page> result = PageHelper.startPage(pageNum, pageSize); + + // 调用服务层查询主机设备列表 + com.aiotagro.cattletrade.business.dto.DeviceDto dto = new com.aiotagro.cattletrade.business.dto.DeviceDto(); + dto.setDeviceId(deviceId); + dto.setPageNum(pageNum); + dto.setPageSize(pageSize); + + PageResultResponse response = jbqServerService.serverList(dto); + return response; + } catch (Exception e) { + e.printStackTrace(); + return new PageResultResponse<>(0L, java.util.Collections.emptyList()); + } + } + + /** + * 测试智能主机查询 + */ + @GetMapping("/testServerList") + public AjaxResult testServerList() { + try { + System.out.println("Testing server list query"); + com.aiotagro.cattletrade.business.dto.DeviceDto dto = new com.aiotagro.cattletrade.business.dto.DeviceDto(); + PageResultResponse response = jbqServerService.serverList(dto); + System.out.println("Server list query completed"); + return AjaxResult.success("测试结果", response); + } catch (Exception e) { + e.printStackTrace(); + return AjaxResult.error("测试失败: " + e.getMessage()); + } + } +} \ No newline at end of file diff --git a/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/controller/JbqServerLogController.java b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/controller/JbqServerLogController.java new file mode 100644 index 0000000..a78ca2d --- /dev/null +++ b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/controller/JbqServerLogController.java @@ -0,0 +1,20 @@ +package com.aiotagro.cattletrade.business.controller; + + +import org.springframework.web.bind.annotation.RequestMapping; + +import org.springframework.web.bind.annotation.RestController; + +/** + *

+ * 智能主机日志表 前端控制器 + *

+ * + * @author admin + * @since 2024-12-26 + */ +@RestController +@RequestMapping("/jbqServerLog") +public class JbqServerLogController { + +} diff --git a/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/controller/LoginControl.java b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/controller/LoginControl.java new file mode 100644 index 0000000..1eb81b2 --- /dev/null +++ b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/controller/LoginControl.java @@ -0,0 +1,65 @@ +package com.aiotagro.cattletrade.business.controller; + +import cn.dev33.satoken.annotation.SaIgnore; +import cn.dev33.satoken.stp.StpUtil; +import com.aiotagro.cattletrade.business.dto.LoginDto; +import com.aiotagro.cattletrade.business.service.LoginService; +import com.aiotagro.common.core.web.domain.AjaxResult; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; + +/** + * 登录 + */ +@RestController +public class LoginControl { + + @Resource + private LoginService loginService; + + /** + * 获取登录验证码 + */ + @SaIgnore + @GetMapping("/sendLoginSmsCode") + public AjaxResult sendLoginSmsCode(@RequestParam String mobile) { + return loginService.sendLoginSmsCode(mobile); + } + + /** + * 登录 + */ + @SaIgnore + @PostMapping("/login") + public AjaxResult login(@Validated @RequestBody LoginDto dto) { + return loginService.login(dto); + } + + /** + * 根据登录用户获取菜单列表 + **/ + @GetMapping("/getUserMenus") + public AjaxResult getUserMenus() { + return loginService.getUserMenus(); + } + + + /** + * 退出登录 + */ + @GetMapping("/logout") + public AjaxResult logout() { + StpUtil.logout(); + return AjaxResult.success("退出成功"); + } + + /** + * 小程序我的 + **/ + @GetMapping("/getUserInfo") + public AjaxResult getUserInfo() { + return loginService.getUserInfo(); + } +} diff --git a/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/controller/MemberController.java b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/controller/MemberController.java new file mode 100644 index 0000000..c747899 --- /dev/null +++ b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/controller/MemberController.java @@ -0,0 +1,655 @@ +package com.aiotagro.cattletrade.business.controller; + +import cn.dev33.satoken.annotation.SaCheckPermission; +import cn.hutool.crypto.digest.DigestUtil; +import com.aiotagro.cattletrade.business.entity.SysRole; +import com.aiotagro.cattletrade.business.entity.SysUser; +import com.aiotagro.cattletrade.business.mapper.MemberDriverMapper; +import com.aiotagro.cattletrade.business.mapper.MemberMapper; +import com.aiotagro.cattletrade.business.mapper.SysRoleMapper; +import com.aiotagro.cattletrade.business.mapper.SysUserMapper; +import com.aiotagro.common.core.constant.Constants; +import com.aiotagro.common.core.utils.SecurityUtil; +import com.aiotagro.common.core.web.domain.AjaxResult; +import com.aiotagro.common.core.web.domain.PageResultResponse; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.github.pagehelper.Page; +import com.github.pagehelper.PageHelper; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.net.URL; +import java.net.URLConnection; + +import javax.annotation.Resource; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.transaction.annotation.Transactional; + +/** + * 会员用户控制器 + * + * @author System + * @date 2025-01-16 + */ +@RestController +@RequestMapping("/member") +public class MemberController { + + @Resource + private SysUserMapper sysUserMapper; + + @Resource + private MemberDriverMapper memberDriverMapper; + + @Resource + private MemberMapper memberMapper; + + @Resource + private SysRoleMapper sysRoleMapper; + + /** + * 获取用户列表(用于权限分配) + */ + @SaCheckPermission("permission:menu:list") + @GetMapping("/userList") + public AjaxResult getUserList() { + // 查询所有用户(从sys_user表) + List userList = sysUserMapper.selectUserListForPermission(); + return AjaxResult.success(userList); + } + + /** + * 获取用户列表(分页,用于用户管理页面) + * 查询member表和member_user表关联数据 + */ + @PostMapping("/userList") + public PageResultResponse> getUserListWithPagination(@RequestBody Map params) { + try { + Integer pageNum = params.get("pageNum") != null ? (Integer) params.get("pageNum") : 1; + Integer pageSize = params.get("pageSize") != null ? (Integer) params.get("pageSize") : 10; + String username = (String) params.get("username"); + String mobile = (String) params.get("mobile"); + Integer type = params.get("type") != null ? (Integer) params.get("type") : null; + Boolean usernameExact = params.get("usernameExact") != null ? (Boolean) params.get("usernameExact") : false; + Boolean mobileExact = params.get("mobileExact") != null ? (Boolean) params.get("mobileExact") : false; + + // 计算偏移量 + Integer offset = (pageNum - 1) * pageSize; + + // 查询用户列表(从member表和member_user表) + List> userList = memberMapper.selectUserListForManagement(username, mobile, type, usernameExact, mobileExact, offset, pageSize); + + // 查询总数 + Long total = memberMapper.countUserListForManagement(username, mobile, type, usernameExact, mobileExact); + + return new PageResultResponse<>(total, userList); + } catch (Exception e) { + e.printStackTrace(); + return new PageResultResponse<>(0L, java.util.Collections.emptyList()); + } + } + + /** + * 新增司机 + */ + @PostMapping("/addDriver") + public AjaxResult addDriver(@RequestBody Map params) { + try { + // 获取参数值 + String username = (String) params.get("username"); + String mobile = (String) params.get("mobile"); + String carNumber = (String) params.get("carNumber"); + String driverLicense = (String) params.get("driverLicense"); + String drivingLicense = (String) params.get("drivingLicense"); + String carImg = (String) params.get("carImg"); + String recordCode = (String) params.get("recordCode"); + String idCard = (String) params.get("idCard"); + String remark = (String) params.get("remark"); + Integer status = params.get("status") != null ? (Integer) params.get("status") : 0; // 默认启用 + + // 参数验证 + if (mobile == null || mobile.trim().isEmpty()) { + return AjaxResult.error("手机号不能为空"); + } + if (username == null || username.trim().isEmpty()) { + return AjaxResult.error("司机姓名不能为空"); + } + + // 检查手机号是否已存在 + Integer existingMemberId = memberMapper.selectMemberIdByMobile(mobile); + if (existingMemberId != null) { + return AjaxResult.error("该手机号已存在"); + } + + // 先插入member表 + int memberResult = memberMapper.insertMember(mobile, 1, status); // type=1表示司机 + if (memberResult <= 0) { + return AjaxResult.error("司机基础信息插入失败"); + } + + // 获取刚插入的member记录的ID + Integer memberId = memberMapper.selectMemberIdByMobile(mobile); + if (memberId == null) { + return AjaxResult.error("获取新司机ID失败"); + } + + // 插入member_driver表 + int driverResult = memberDriverMapper.insertDriver(memberId, username, carNumber, + driverLicense, drivingLicense, carImg, recordCode, idCard, remark); + if (driverResult <= 0) { + return AjaxResult.error("司机详细信息插入失败"); + } + + return AjaxResult.success("司机新增成功"); + } catch (Exception e) { + e.printStackTrace(); + return AjaxResult.error("新增司机失败:" + e.getMessage()); + } + } + + /** + * 获取司机列表(分页) + */ + @PostMapping("/driverList") + public PageResultResponse> driverList(@RequestBody Map params) { + Integer pageNum = params.get("pageNum") != null ? (Integer) params.get("pageNum") : 1; + Integer pageSize = params.get("pageSize") != null ? (Integer) params.get("pageSize") : 10; + String username = (String) params.get("username"); + String mobile = (String) params.get("mobile"); + + // 添加调试日志 + System.out.println("DriverList API called with params:"); + System.out.println("username: " + username); + System.out.println("mobile: " + mobile); + + Page> result = PageHelper.startPage(pageNum, pageSize); + List> list; + + // 如果只查询手机号,使用简化的精确查询 + if (mobile != null && !mobile.trim().isEmpty() && (username == null || username.trim().isEmpty())) { + System.out.println("Using mobile-only search method"); + list = memberDriverMapper.selectDriverListByMobile(mobile); + } else if ((username != null && !username.trim().isEmpty()) || (mobile != null && !mobile.trim().isEmpty())) { + System.out.println("Using combined search method"); + list = memberDriverMapper.selectDriverListBySearch(username, mobile, true); + } else { + System.out.println("Using default list method"); + list = memberDriverMapper.selectDriverList(); + } + + System.out.println("Found " + list.size() + " drivers"); + return new PageResultResponse<>(result.getTotal(), list); + } + @PostMapping("/memberListByType") + public PageResultResponse> getMemberListByType(@RequestBody Map params) { + Integer pageNum = params.get("pageNum") != null ? (Integer) params.get("pageNum") : 1; + Integer pageSize = params.get("pageSize") != null ? (Integer) params.get("pageSize") : 10; + String username = (String) params.get("username"); + Integer type = params.get("type") != null ? (Integer) params.get("type") : null; + + // 计算偏移量 + Integer offset = (pageNum - 1) * pageSize; + + // 查询数据 + List> list = memberMapper.selectMemberListByType(type, username, offset, pageSize); + + // 查询总数 + Long total = memberMapper.countMemberListByType(type, username); + + return new PageResultResponse<>(total, list); + } + + /** + * 插入测试数据(仅用于开发测试) + */ + @PostMapping("/insertTestData") + public AjaxResult insertTestData() { + try { + // 这里可以添加插入测试数据的逻辑 + // 由于没有直接的SQL执行方法,我们返回成功信息 + return AjaxResult.success("测试数据插入功能已准备就绪,请手动执行SQL脚本"); + } catch (Exception e) { + return AjaxResult.error("插入测试数据失败: " + e.getMessage()); + } + } + + /** + * 调试接口:直接查询member表数据 + */ + @GetMapping("/debugMemberData") + public AjaxResult debugMemberData() { + try { + // 直接查询member表的所有数据 + List> allMembers = memberMapper.selectMemberUserList(); + return AjaxResult.success("member表数据", allMembers); + } catch (Exception e) { + return AjaxResult.error("查询失败: " + e.getMessage()); + } + } + + /** + * 更新司机信息 + */ + @SaCheckPermission("member:edit") + @PostMapping("/updateDriver") + public AjaxResult updateDriver(@RequestBody Map params) { + try { + Integer id = (Integer) params.get("id"); + if (id == null) { + return AjaxResult.error("司机ID不能为空"); + } + + // 获取参数值 + String username = (String) params.get("username"); + String carNumber = (String) params.get("carNumber"); + String driverLicense = (String) params.get("driverLicense"); + String drivingLicense = (String) params.get("drivingLicense"); + String carImg = (String) params.get("carImg"); + String recordCode = (String) params.get("recordCode"); + String idCard = (String) params.get("idCard"); + String remark = (String) params.get("remark"); + + // 执行更新 + int result = memberDriverMapper.updateDriver(id, username, carNumber, driverLicense, + drivingLicense, carImg, recordCode, idCard, remark); + + if (result > 0) { + return AjaxResult.success("司机信息更新成功"); + } else { + return AjaxResult.error("司机信息更新失败"); + } + } catch (Exception e) { + e.printStackTrace(); + return AjaxResult.error("更新司机信息失败:" + e.getMessage()); + } + } + + /** + * 更新用户信息 + */ + @SaCheckPermission("member:edit") + @PostMapping("/updateUser") + public AjaxResult updateUser(@RequestBody Map params) { + try { + Integer id = (Integer) params.get("id"); + if (id == null) { + return AjaxResult.error("用户ID不能为空"); + } + + // 获取参数值 + String mobile = (String) params.get("mobile"); + Integer type = params.get("type") != null ? (Integer) params.get("type") : null; + Integer status = params.get("status") != null ? (Integer) params.get("status") : null; + String username = (String) params.get("username"); + String cbkAccount = (String) params.get("cbkAccount"); + String remark = (String) params.get("remark"); + + // 执行更新 + int result = memberMapper.updateUserInfo(id, mobile, type, status, username, cbkAccount, remark); + + if (result > 0) { + return AjaxResult.success("用户信息更新成功"); + } else { + return AjaxResult.error("用户信息更新失败"); + } + } catch (Exception e) { + e.printStackTrace(); + return AjaxResult.error("更新用户信息失败:" + e.getMessage()); + } + } + + /** + * 新增用户 + */ + @PostMapping("/addUser") + @Transactional + public AjaxResult addUser(@RequestBody Map params) { + try { + // 获取参数值 + String mobile = (String) params.get("mobile"); + Integer type = params.get("type") != null ? (Integer) params.get("type") : null; + Integer status = params.get("status") != null ? (Integer) params.get("status") : 0; // 默认启用 + String username = (String) params.get("username"); + String cbkAccount = (String) params.get("cbkAccount"); + String remark = (String) params.get("remark"); + + // 参数验证 + if (mobile == null || mobile.trim().isEmpty()) { + return AjaxResult.error("手机号不能为空"); + } + if (username == null || username.trim().isEmpty()) { + return AjaxResult.error("用户姓名不能为空"); + } + if (type == null) { + return AjaxResult.error("用户类型不能为空"); + } + + // 检查手机号是否已存在(直接查询member表) + Integer existingMemberId = memberMapper.selectMemberIdByMobile(mobile); + if (existingMemberId != null) { + return AjaxResult.error("该手机号已存在"); + } + + // 先插入member表 + int memberResult = memberMapper.insertMember(mobile, type, status); + if (memberResult <= 0) { + return AjaxResult.error("用户基础信息插入失败"); + } + + // 获取刚插入的member记录的ID(通过查询最新记录) + Integer memberId = memberMapper.selectMemberIdByMobile(mobile); + if (memberId == null) { + return AjaxResult.error("获取新用户ID失败"); + } + + // 再插入member_user表 + int userResult = memberMapper.insertMemberUser(memberId, username, cbkAccount, remark); + if (userResult <= 0) { + return AjaxResult.error("用户详细信息插入失败"); + } + + // 自动创建员工记录 + try { + createEmployeeRecord(username, mobile, type, status); + } catch (Exception e) { + // 员工记录创建失败不影响用户创建,只记录日志 + System.err.println("创建员工记录失败: " + e.getMessage()); + } + + return AjaxResult.success("用户新增成功"); + } catch (Exception e) { + e.printStackTrace(); + return AjaxResult.error("新增用户失败:" + e.getMessage()); + } + } + + /** + * 根据ID获取司机详情 + */ + @SaCheckPermission("member:view") + @GetMapping("/driverDetail/{id}") + public AjaxResult getDriverDetail(@PathVariable("id") Integer id) { + try { + if (id == null) { + return AjaxResult.error("司机ID不能为空"); + } + + Map driverInfo = memberDriverMapper.selectDriverById(id); + if (driverInfo != null) { + return AjaxResult.success(driverInfo); + } else { + return AjaxResult.error("司机信息不存在"); + } + } catch (Exception e) { + e.printStackTrace(); + return AjaxResult.error("获取司机详情失败:" + e.getMessage()); + } + } + + /** + * 根据用户类型自动创建员工记录 + * + * @param username 用户姓名 + * @param mobile 手机号 + * @param userType 用户类型 (1:司机, 2:供应商, 3:资金方, 4:采购商) + * @param status 状态 + */ + private void createEmployeeRecord(String username, String mobile, Integer userType, Integer status) { + // 检查手机号是否已存在于员工表中 + List existingUsers = sysUserMapper.selectList( + new LambdaQueryWrapper() + .eq(SysUser::getMobile, mobile) + .eq(SysUser::getIsDelete, 0) + ); + + if (!existingUsers.isEmpty()) { + System.out.println("手机号 " + mobile + " 已存在于员工表中,跳过创建"); + return; + } + + // 根据用户类型获取或创建对应的岗位 + Integer roleId = getOrCreateRoleByUserType(userType); + if (roleId == null) { + throw new RuntimeException("无法获取或创建岗位"); + } + + // 创建员工记录 + SysUser sysUser = new SysUser(); + sysUser.setName(username); + sysUser.setMobile(mobile); + sysUser.setRoleId(roleId); + sysUser.setPassword(DigestUtil.md5Hex(Constants.USER_PASSWORD_PREFIX + "123456")); // 默认密码123456 + sysUser.setStatus(status != null ? status : 1); // 默认启用 + sysUser.setCreateTime(new Date()); + sysUser.setCreateBy(SecurityUtil.getCurrentUserId()); + sysUser.setIsDelete(0); + + int result = sysUserMapper.insert(sysUser); + if (result <= 0) { + throw new RuntimeException("员工记录插入失败"); + } + + System.out.println("成功为用户 " + username + " 创建员工记录,岗位ID: " + roleId); + } + + /** + * 根据用户类型获取或创建对应的岗位 + * + * @param userType 用户类型 + * @return 岗位ID + */ + private Integer getOrCreateRoleByUserType(Integer userType) { + String roleName; + String roleDescription; + + // 根据用户类型确定岗位名称和描述 + switch (userType) { + case 1: + roleName = "司机"; + roleDescription = "司机用户岗位"; + break; + case 2: + roleName = "供应商"; + roleDescription = "供应商用户岗位"; + break; + case 3: + roleName = "资金方"; + roleDescription = "资金方用户岗位"; + break; + case 4: + roleName = "采购商"; + roleDescription = "采购商用户岗位"; + break; + default: + roleName = "普通用户"; + roleDescription = "普通用户岗位"; + break; + } + + // 查询是否已存在该岗位 + List existingRoles = sysRoleMapper.selectList( + new LambdaQueryWrapper() + .eq(SysRole::getName, roleName) + .eq(SysRole::getIsDelete, 0) + ); + + if (!existingRoles.isEmpty()) { + return existingRoles.get(0).getId(); + } + + // 创建新岗位 + SysRole newRole = new SysRole(); + newRole.setName(roleName); + newRole.setDescription(roleDescription); + newRole.setCreateTime(new Date()); + newRole.setIsDelete(0); + + int result = sysRoleMapper.insert(newRole); + if (result <= 0) { + throw new RuntimeException("创建岗位失败"); + } + + System.out.println("成功创建岗位: " + roleName + ", ID: " + newRole.getId()); + return newRole.getId(); + } + + /** + * 图片代理接口 - 解决CORS跨域问题 + * 通过后端代理访问外部图片资源 + */ + @GetMapping("/proxy/image") + public ResponseEntity proxyImage(@RequestParam("url") String imageUrl) { + try { + // 验证URL格式 + if (imageUrl == null || imageUrl.trim().isEmpty()) { + return ResponseEntity.badRequest().build(); + } + + // 安全检查:只允许HTTPS协议 + if (!imageUrl.startsWith("https://")) { + return ResponseEntity.badRequest().build(); + } + + System.out.println("代理图片请求: " + imageUrl); + + // 创建URL连接 + URL url = new URL(imageUrl); + URLConnection connection = url.openConnection(); + + // 设置请求头,模拟浏览器请求 + connection.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"); + connection.setRequestProperty("Accept", "image/*"); + connection.setConnectTimeout(10000); // 10秒连接超时 + connection.setReadTimeout(30000); // 30秒读取超时 + + // 获取输入流 + InputStream inputStream = connection.getInputStream(); + + // 读取图片数据 + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + byte[] buffer = new byte[4096]; + int bytesRead; + while ((bytesRead = inputStream.read(buffer)) != -1) { + outputStream.write(buffer, 0, bytesRead); + } + + byte[] imageData = outputStream.toByteArray(); + + // 关闭流 + inputStream.close(); + outputStream.close(); + + // 设置响应头 + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.IMAGE_JPEG); // 默认设置为JPEG + headers.setContentLength(imageData.length); + + // 设置缓存头,减少重复请求 + headers.setCacheControl("public, max-age=3600"); // 缓存1小时 + + // 根据文件扩展名设置正确的Content-Type + String contentType = connection.getContentType(); + if (contentType != null && contentType.startsWith("image/")) { + headers.setContentType(MediaType.parseMediaType(contentType)); + } + + System.out.println("图片代理成功,大小: " + imageData.length + " bytes"); + + return new ResponseEntity<>(imageData, headers, HttpStatus.OK); + + } catch (Exception e) { + System.err.println("图片代理失败: " + e.getMessage()); + e.printStackTrace(); + return ResponseEntity.status(HttpStatus.NOT_FOUND).build(); + } +} + +/** + * 调试接口:查看司机图片数据详情 + */ +@GetMapping("/debug/driverImages") +public AjaxResult debugDriverImages() { + try { + // 查询所有司机的图片数据 + List> drivers = memberDriverMapper.selectDriverList(); + + System.out.println("=== 司机图片数据调试 ==="); + for (Map driver : drivers) { + String username = (String) driver.get("username"); + String carImg = (String) driver.get("car_img"); + String mobile = (String) driver.get("mobile"); + + System.out.println("司机: " + username + " (手机: " + mobile + ")"); + System.out.println("car_img字段: " + carImg); + + if (carImg != null && !carImg.trim().isEmpty()) { + String[] urls = carImg.split(","); + System.out.println("解析后的URL数量: " + urls.length); + for (int i = 0; i < urls.length; i++) { + String url = urls[i].trim(); + System.out.println(" URL " + (i + 1) + ": " + url); + System.out.println(" URL " + (i + 1) + " 长度: " + url.length()); + System.out.println(" URL " + (i + 1) + " 是否HTTPS: " + url.startsWith("https://")); + } + } else { + System.out.println("car_img字段为空"); + } + System.out.println("---"); + } + + return AjaxResult.success("司机图片数据调试完成,请查看控制台输出", drivers); + } catch (Exception e) { + e.printStackTrace(); + return AjaxResult.error("调试失败: " + e.getMessage()); + } +} + +@GetMapping("/debug/carImgSplit") +public AjaxResult debugCarImgSplit() { + try { + // 测试分割逻辑 + String testCarImg = "https://smart-1251449951.cos.ap-guangzhou.myqcloud.com/iotPlateform/2025/10/16/4c4e20251016142427.jpg,https://smart-1251449951.cos.ap-guangzhou.myqcloud.com/iotPlateform/2025/10/16/4c4e20251016142429.jpg"; + + System.out.println("=== 测试car_img分割逻辑 ==="); + System.out.println("原始car_img: " + testCarImg); + + String[] carImgUrls = testCarImg.split(","); + System.out.println("分割后数组长度: " + carImgUrls.length); + + if (carImgUrls.length >= 2) { + String carBehindPhoto = carImgUrls[0].trim(); + String carFrontPhoto = carImgUrls[1].trim(); + + System.out.println("车尾照片 (carBehindPhoto): " + carBehindPhoto); + System.out.println("车头照片 (carFrontPhoto): " + carFrontPhoto); + + Map result = new HashMap<>(); + result.put("originalCarImg", testCarImg); + result.put("carBehindPhoto", carBehindPhoto); + result.put("carFrontPhoto", carFrontPhoto); + result.put("splitCount", carImgUrls.length); + + return AjaxResult.success("分割测试完成", result); + } else { + return AjaxResult.error("分割失败:URL数量不足"); + } + } catch (Exception e) { + e.printStackTrace(); + return AjaxResult.error("分割测试失败: " + e.getMessage()); + } +} +} + diff --git a/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/controller/SysMenuController.java b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/controller/SysMenuController.java new file mode 100644 index 0000000..3a05c76 --- /dev/null +++ b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/controller/SysMenuController.java @@ -0,0 +1,209 @@ +package com.aiotagro.cattletrade.business.controller; + +import cn.dev33.satoken.annotation.SaCheckPermission; +import com.aiotagro.cattletrade.business.entity.SysMenu; +import com.aiotagro.cattletrade.business.entity.SysRoleMenu; +import com.aiotagro.cattletrade.business.mapper.SysMenuMapper; +import com.aiotagro.cattletrade.business.mapper.SysRoleMenuMapper; +import com.aiotagro.common.core.web.domain.AjaxResult; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; +import java.util.*; +import java.util.stream.Collectors; + +/** + * 菜单权限管理控制器 + * + * @author System + * @date 2025-10-11 + */ +@Slf4j +@RestController +@RequestMapping("/sysMenu") +public class SysMenuController { + + @Resource + private SysMenuMapper sysMenuMapper; + + @Resource + private SysRoleMenuMapper sysRoleMenuMapper; + + /** + * 获取菜单树(所有菜单) + */ + @SaCheckPermission("permission:menu:list") + @GetMapping("/tree") + public AjaxResult getMenuTree() { + List allMenus = sysMenuMapper.selectList( + new LambdaQueryWrapper() + .eq(SysMenu::getIsDelete, 0) + .orderByAsc(SysMenu::getSort) + ); + + List> tree = buildMenuTree(allMenus, 0); + return AjaxResult.success(tree); + } + + /** + * 获取菜单列表(扁平) + */ + @SaCheckPermission("permission:menu:list") + @GetMapping("/list") + public AjaxResult getMenuList() { + List menus = sysMenuMapper.selectList( + new LambdaQueryWrapper() + .eq(SysMenu::getIsDelete, 0) + .orderByAsc(SysMenu::getSort) + ); + return AjaxResult.success(menus); + } + + /** + * 根据角色ID获取菜单列表(用于角色权限分配) + */ + @SaCheckPermission("permission:menu:list") + @GetMapping("/listByRoleId") + public AjaxResult getMenuListByRoleId(@RequestParam Integer roleId) { + List menus = sysMenuMapper.queryListByRoleId(roleId); + return AjaxResult.success(menus); + } + + /** + * 获取角色已分配的菜单ID列表 + */ + @SaCheckPermission("permission:menu:list") + @GetMapping("/roleMenuIds") + public AjaxResult getRoleMenuIds(@RequestParam Integer roleId) { + log.info("=== 获取角色菜单权限 === roleId: {}", roleId); + + List roleMenus = sysRoleMenuMapper.selectList( + new LambdaQueryWrapper() + .eq(SysRoleMenu::getRoleId, roleId) + ); + + log.info("=== 查询到的角色菜单关联记录数: {}", roleMenus.size()); + + List menuIds = roleMenus.stream() + .map(SysRoleMenu::getMenuId) + .collect(Collectors.toList()); + + log.info("=== 返回的菜单ID列表: {}", menuIds); + + return AjaxResult.success(menuIds); + } + + /** + * 新增菜单 + */ + @SaCheckPermission("permission:menu:add") + @PostMapping("/add") + public AjaxResult addMenu(@RequestBody SysMenu menu) { + menu.setIsDelete(0); + menu.setCreateTime(new Date()); + int result = sysMenuMapper.insert(menu); + return result > 0 ? AjaxResult.success("添加成功") : AjaxResult.error("添加失败"); + } + + /** + * 更新菜单 + */ + @SaCheckPermission("permission:menu:edit") + @PostMapping("/update") + public AjaxResult updateMenu(@RequestBody SysMenu menu) { + menu.setUpdateTime(new Date()); + int result = sysMenuMapper.updateById(menu); + return result > 0 ? AjaxResult.success("更新成功") : AjaxResult.error("更新失败"); + } + + /** + * 删除菜单(软删除) + */ + @SaCheckPermission("permission:menu:delete") + @GetMapping("/delete") + public AjaxResult deleteMenu(@RequestParam Integer id) { + SysMenu menu = new SysMenu(); + menu.setId(id); + menu.setIsDelete(1); + menu.setUpdateTime(new Date()); + int result = sysMenuMapper.updateById(menu); + return result > 0 ? AjaxResult.success("删除成功") : AjaxResult.error("删除失败"); + } + + /** + * 为角色分配菜单权限 + */ + @SaCheckPermission("permission:menu:assign") + @PostMapping("/assignRoleMenus") + public AjaxResult assignRoleMenus(@RequestBody Map params) { + Integer roleId = (Integer) params.get("roleId"); + @SuppressWarnings("unchecked") + List menuIds = (List) params.get("menuIds"); + + log.info("=== 分配角色菜单权限 ==="); + log.info("roleId: {}", roleId); + log.info("menuIds: {}", menuIds); + + if (roleId == null) { + log.error("角色ID不能为空"); + return AjaxResult.error("角色ID不能为空"); + } + + // 删除原有权限 + int deletedCount = sysRoleMenuMapper.delete( + new LambdaQueryWrapper() + .eq(SysRoleMenu::getRoleId, roleId) + ); + log.info("=== 删除原有权限记录数: {}", deletedCount); + + // 添加新权限 + if (menuIds != null && !menuIds.isEmpty()) { + for (Integer menuId : menuIds) { + SysRoleMenu roleMenu = new SysRoleMenu(); + roleMenu.setRoleId(roleId); + roleMenu.setMenuId(menuId); + int insertResult = sysRoleMenuMapper.insert(roleMenu); + log.info("=== 插入权限记录 menuId: {}, 结果: {}", menuId, insertResult); + } + log.info("=== 成功分配 {} 个权限", menuIds.size()); + } else { + log.info("=== 没有要分配的权限,清空所有权限"); + } + + return AjaxResult.success("分配成功"); + } + + /** + * 构建菜单树 + */ + private List> buildMenuTree(List menus, Integer parentId) { + List> tree = new ArrayList<>(); + + for (SysMenu menu : menus) { + if (menu.getParentId() != null && menu.getParentId().equals(parentId)) { + Map node = new HashMap<>(); + node.put("id", menu.getId()); + node.put("label", menu.getName()); + node.put("parentId", menu.getParentId()); + node.put("type", menu.getType()); + node.put("icon", menu.getIcon()); + node.put("sort", menu.getSort()); + node.put("routeUrl", menu.getRouteUrl()); + node.put("pageUrl", menu.getPageUrl()); + node.put("authority", menu.getAuthority()); + + List> children = buildMenuTree(menus, menu.getId()); + if (!children.isEmpty()) { + node.put("children", children); + } + + tree.add(node); + } + } + + return tree; + } +} + diff --git a/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/controller/SysRoleController.java b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/controller/SysRoleController.java new file mode 100644 index 0000000..47f2c16 --- /dev/null +++ b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/controller/SysRoleController.java @@ -0,0 +1,90 @@ +package com.aiotagro.cattletrade.business.controller; + + +import cn.dev33.satoken.annotation.SaCheckPermission; +import com.aiotagro.cattletrade.business.dto.SysRoleDto; +import com.aiotagro.cattletrade.business.entity.SysRole; +import com.aiotagro.cattletrade.business.service.ISysRoleService; +import com.aiotagro.common.core.web.domain.AjaxResult; +import com.aiotagro.common.core.web.domain.PageResultResponse; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +/** + *

+ * 岗色管理表 前端控制器 + *

+ * + * @author admin + * @since 2024-12-26 + */ +@RestController +@RequestMapping("/sysRole") +public class SysRoleController { + + @Autowired + private ISysRoleService roleService; + + /** + * 查询分页列表 + */ + @SaCheckPermission("permission:operation:list") + @PostMapping(value = "/queryList") + public PageResultResponse queryList(@RequestBody SysRoleDto dto) { + return roleService.queryList(dto); + } + + /** + * 查询角色列表(权限管理页面使用) + */ + @SaCheckPermission("permission:operation:list") + @PostMapping(value = "/list") + public PageResultResponse list(@RequestBody SysRoleDto dto) { + return roleService.queryList(dto); + } + + /** + * 新增、编辑 + **/ + @SaCheckPermission("permission:operation:role") + @PostMapping("/save") + public AjaxResult saveRole(@RequestBody SysRole role) { + return roleService.saveRole(role); + } + + /** + * 新增角色 + **/ + @SaCheckPermission("permission:operation:role") + @PostMapping("/add") + public AjaxResult add(@RequestBody SysRole role) { + return roleService.saveRole(role); + } + + /** + * 更新角色 + **/ + @SaCheckPermission("permission:operation:role") + @PostMapping("/update") + public AjaxResult update(@RequestBody SysRole role) { + return roleService.saveRole(role); + } + + /** + * 删除 + **/ + @SaCheckPermission("permission:operation:role") + @GetMapping("/delete") + public AjaxResult delete(@RequestParam Integer id) { + return roleService.delete(id); + } + + /** + * 查询菜单列表 + **/ + @SaCheckPermission("permission:menu:list") + @GetMapping("/getMenus") + public AjaxResult getMenus(@RequestParam(required = false) Integer id) { + return roleService.getMenus(id); + } +} diff --git a/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/controller/SysTenantController.java b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/controller/SysTenantController.java new file mode 100644 index 0000000..35434a2 --- /dev/null +++ b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/controller/SysTenantController.java @@ -0,0 +1,113 @@ +package com.aiotagro.cattletrade.business.controller; + +import cn.dev33.satoken.annotation.SaCheckPermission; +import com.aiotagro.cattletrade.business.entity.SysTenant; +import com.aiotagro.cattletrade.business.mapper.SysTenantMapper; +import com.aiotagro.common.core.web.domain.AjaxResult; +import com.aiotagro.common.core.web.domain.PageResultResponse; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.github.pagehelper.Page; +import com.github.pagehelper.PageHelper; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; +import java.util.Date; +import java.util.List; +import java.util.Map; + +/** + * 租户管理控制器 + * + * @author System + * @date 2025-01-16 + */ +@RestController +@RequestMapping("/sysTenant") +public class SysTenantController { + + @Resource + private SysTenantMapper tenantMapper; + + /** + * 租户列表查询(分页) + */ + @SaCheckPermission("system:tenant:list") + @PostMapping("/queryList") + public PageResultResponse queryList(@RequestBody Map params) { + Integer pageNum = params.get("pageNum") != null ? (Integer) params.get("pageNum") : 1; + Integer pageSize = params.get("pageSize") != null ? (Integer) params.get("pageSize") : 10; + String name = (String) params.get("name"); + + Page result = PageHelper.startPage(pageNum, pageSize); + + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(SysTenant::getIsDelete, 0); + + if (name != null && !name.trim().isEmpty()) { + wrapper.like(SysTenant::getName, name); + } + + wrapper.orderByDesc(SysTenant::getId); + List list = tenantMapper.selectList(wrapper); + + return new PageResultResponse<>(result.getTotal(), list); + } + + /** + * 新增租户 + */ + @SaCheckPermission("system:tenant:add") + @PostMapping("/add") + public AjaxResult add(@RequestBody SysTenant tenant) { + tenant.setCreateTime(new Date()); + tenant.setIsDelete(0); + int rows = tenantMapper.insert(tenant); + return rows > 0 ? AjaxResult.success("新增成功") : AjaxResult.error("新增失败"); + } + + /** + * 更新租户 + */ + @SaCheckPermission("system:tenant:edit") + @PostMapping("/update") + public AjaxResult update(@RequestBody SysTenant tenant) { + int rows = tenantMapper.updateById(tenant); + return rows > 0 ? AjaxResult.success("更新成功") : AjaxResult.error("更新失败"); + } + + /** + * 删除租户(逻辑删除) + */ + @SaCheckPermission("system:tenant:delete") + @PostMapping("/delete") + public AjaxResult delete(@RequestParam Integer id) { + SysTenant tenant = new SysTenant(); + tenant.setId(id); + tenant.setIsDelete(1); + int rows = tenantMapper.updateById(tenant); + return rows > 0 ? AjaxResult.success("删除成功") : AjaxResult.error("删除失败"); + } + + /** + * 获取租户详情 + */ + @SaCheckPermission("system:tenant:view") + @GetMapping("/detail") + public AjaxResult detail(@RequestParam Integer id) { + SysTenant tenant = tenantMapper.selectById(id); + return AjaxResult.success(tenant); + } + + /** + * 获取租户分配列表(用于下拉框等) + */ + @GetMapping("/allotList") + public AjaxResult allotList() { + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(SysTenant::getIsDelete, 0); + wrapper.orderByDesc(SysTenant::getId); + List list = tenantMapper.selectList(wrapper); + return AjaxResult.success(list); + } +} + diff --git a/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/controller/SysUserController.java b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/controller/SysUserController.java new file mode 100644 index 0000000..ad3792b --- /dev/null +++ b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/controller/SysUserController.java @@ -0,0 +1,51 @@ +package com.aiotagro.cattletrade.business.controller; + + +import com.aiotagro.cattletrade.business.dto.SysUserDto; +import com.aiotagro.cattletrade.business.entity.SysUser; +import com.aiotagro.cattletrade.business.service.ISysUserService; +import com.aiotagro.common.core.web.domain.AjaxResult; +import com.aiotagro.common.core.web.domain.PageResultResponse; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +/** + *

+ * 员工管理表 前端控制器 + *

+ * + * @author admin + * @since 2024-12-26 + */ +@RestController +@RequestMapping("/sysUser") +public class SysUserController { + + @Autowired + private ISysUserService userService; + + /** + * 查询分页列表 + */ + @PostMapping(value = "/queryList") + public PageResultResponse queryList(@RequestBody SysUserDto dto) { + return userService.queryList(dto); + } + + /** + * 新增、编辑 + **/ + @PostMapping("/save") + public AjaxResult saveUser(@RequestBody SysUser user) { + return userService.saveUser(user); + } + + /** + * 删除 + **/ + @GetMapping("/delete") + public AjaxResult delete(@RequestParam Integer id) { + return userService.delete(id); + } + +} diff --git a/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/controller/WarningLogController.java b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/controller/WarningLogController.java new file mode 100644 index 0000000..60e9334 --- /dev/null +++ b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/controller/WarningLogController.java @@ -0,0 +1,82 @@ +package com.aiotagro.cattletrade.business.controller; + + +import com.aiotagro.cattletrade.business.dto.DeliveryQueryDto; +import com.aiotagro.cattletrade.business.dto.DeliverListDto; +import com.aiotagro.cattletrade.business.service.IDeliveryService; +import com.aiotagro.cattletrade.business.service.IWarningLogService; +import com.aiotagro.cattletrade.business.vo.DeliveryLogVo; +import com.aiotagro.common.core.web.domain.AjaxResult; +import com.aiotagro.common.core.web.domain.PageResultResponse; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +/** + * 预警记录表 + * + * @author admin + * @since 2024-12-26 + */ +@RestController +@RequestMapping("/warningLog") +public class WarningLogController { + + @Autowired + private IWarningLogService warningLogService; + @Autowired + private IDeliveryService deliveryService; + + /** + * 供远程调用-保存预警日志 + */ + @GetMapping("/savaWarn") + public AjaxResult savaWarn(@RequestParam String deviceId) { + return warningLogService.savaWarn(deviceId); + } + + /** + * 运单预警-预警统计 + */ + @RequestMapping("/warningCount") + public AjaxResult warningCount() { + return warningLogService.warningCount(); + } + + /** + * 运单预警-分页查询 + * + * @param dto + * @return + */ + @PostMapping(value = "/queryList") + public PageResultResponse queryList(@RequestBody @Validated DeliveryQueryDto dto) { + return warningLogService.queryList(dto); + } + + /** + * 预警记录详情 + */ + @RequestMapping("/warningDetail") + public AjaxResult warningDetail(@RequestParam Integer id) { + return warningLogService.warningDetail(id); + } + + + /** + * 后台系统,预警记录 + * + * @param dto + * @return + */ + @PostMapping(value = "/pageQuery") + public PageResultResponse pageQuery(@RequestBody DeliverListDto dto) { + return deliveryService.pageQueryList(dto); + } + +} diff --git a/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/controller/WechatDeliveryController.java b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/controller/WechatDeliveryController.java new file mode 100644 index 0000000..a8f19f4 --- /dev/null +++ b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/controller/WechatDeliveryController.java @@ -0,0 +1,199 @@ +package com.aiotagro.cattletrade.business.controller; + +import cn.dev33.satoken.annotation.SaCheckPermission; +import com.aiotagro.cattletrade.business.entity.Delivery; +import com.aiotagro.cattletrade.business.entity.DeliveryDevice; +import com.aiotagro.cattletrade.business.service.IDeliveryDeviceService; +import com.aiotagro.cattletrade.business.service.IDeliveryService; +import com.aiotagro.cattletrade.business.service.IJbqClientService; +import com.aiotagro.cattletrade.business.service.IJbqServerService; +import com.aiotagro.cattletrade.business.service.IXqClientService; +import com.aiotagro.common.core.web.domain.AjaxResult; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * 微信端运送清单控制器 + * + * @author System + * @date 2025-01-16 + */ +@RestController +@RequestMapping("/wechatDelivery") +public class WechatDeliveryController { + + @Autowired + private IDeliveryDeviceService deliveryDeviceService; + + @Autowired + private IDeliveryService deliveryService; + + @Autowired + private IXqClientService xqClientService; + + @Autowired + private IJbqClientService jbqClientService; + + @Autowired + private IJbqServerService jbqServerService; + + /** + * 获取装车信息 + */ + @SaCheckPermission("delivery:view") + @PostMapping("/carLoadInfo") + public AjaxResult carLoadInfo(@RequestBody Map params) { + try { + Integer deliveryId = (Integer) params.get("deliveryId"); + if (deliveryId == null) { + return AjaxResult.error("运送清单ID不能为空"); + } + + Map result = new HashMap<>(); + + // 查询耳标设备 + AjaxResult earResult = jbqClientService.getDevicesByDeliveryId(deliveryId, 2); + if (earResult.get("code").equals(200)) { + result.put("deliveryDevices", earResult.get("data")); + } else { + result.put("deliveryDevices", java.util.Collections.emptyList()); + } + + // 查询项圈设备 + AjaxResult collarResult = xqClientService.getDevicesByDeliveryId(deliveryId, 3); + if (collarResult.get("code").equals(200)) { + result.put("xqDevices", collarResult.get("data")); + } else { + result.put("xqDevices", java.util.Collections.emptyList()); + } + + return AjaxResult.success(result); + } catch (Exception e) { + e.printStackTrace(); + return AjaxResult.error("查询装车信息失败:" + e.getMessage()); + } + } + + /** + * 测试数据库连接 + */ + @GetMapping("/testConnection") + public AjaxResult testConnection() { + try { + // 测试基本查询 + List deliveries = deliveryService.list(); + Map result = new HashMap<>(); + result.put("deliveryCount", deliveries.size()); + result.put("status", "数据库连接正常"); + return AjaxResult.success("测试成功", result); + } catch (Exception e) { + e.printStackTrace(); + return AjaxResult.error("测试失败: " + e.getMessage()); + } + } + + /** + * 更新装车信息 + */ + @SaCheckPermission("delivery:edit") + @PostMapping("/updateLoadInfo") + public AjaxResult updateLoadInfo(@RequestBody Map params) { + try { + Integer deliveryId = (Integer) params.get("deliveryId"); + if (deliveryId == null) { + return AjaxResult.error("运送清单ID不能为空"); + } + + // 更新运送清单基本信息 + LambdaUpdateWrapper updateWrapper = new LambdaUpdateWrapper<>(); + updateWrapper.eq(Delivery::getId, deliveryId); + + // 更新预计送达时间 + if (params.get("estimatedDeliveryTime") != null) { + updateWrapper.set(Delivery::getEstimatedDeliveryTime, params.get("estimatedDeliveryTime")); + } + + // 更新空车过磅重量 + if (params.get("emptyWeight") != null) { + updateWrapper.set(Delivery::getEmptyWeight, params.get("emptyWeight")); + } + + // 更新装车过磅重量 + if (params.get("entruckWeight") != null) { + updateWrapper.set(Delivery::getEntruckWeight, params.get("entruckWeight")); + } + + // 更新检疫票 + if (params.get("quarantineTickeyUrl") != null) { + updateWrapper.set(Delivery::getQuarantineTickeyUrl, params.get("quarantineTickeyUrl")); + } + + // 更新传纸质磅单(双章) + if (params.get("poundListImg") != null) { + updateWrapper.set(Delivery::getPoundListImg, params.get("poundListImg")); + } + + // 更新装车过磅视频 + if (params.get("entruckWeightVideo") != null) { + updateWrapper.set(Delivery::getEntruckWeightVideo, params.get("entruckWeightVideo")); + } + + // 更新空车过磅视频 + if (params.get("emptyWeightVideo") != null) { + updateWrapper.set(Delivery::getEmptyWeightVideo, params.get("emptyWeightVideo")); + } + + // 更新上传装牛视频 + if (params.get("entruckVideo") != null) { + updateWrapper.set(Delivery::getEntruckVideo, params.get("entruckVideo")); + } + + // 更新控槽视频 + if (params.get("controlSlotVideo") != null) { + updateWrapper.set(Delivery::getControlSlotVideo, params.get("controlSlotVideo")); + } + + // 更新车辆空磅上磅车头照片 + if (params.get("emptyVehicleFrontPhoto") != null) { + updateWrapper.set(Delivery::getEmptyVehicleFrontPhoto, params.get("emptyVehicleFrontPhoto")); + } + + // 更新装完牛绕车一圈视频 + if (params.get("cattleLoadingCircleVideo") != null) { + updateWrapper.set(Delivery::getCattleLoadingCircleVideo, params.get("cattleLoadingCircleVideo")); + } + + // 更新车辆过重磅车头照片 + if (params.get("loadedVehicleFrontPhoto") != null) { + updateWrapper.set(Delivery::getLoadedVehicleFrontPhoto, params.get("loadedVehicleFrontPhoto")); + } + + // 更新车辆重磅照片 + if (params.get("loadedVehicleWeightPhoto") != null) { + updateWrapper.set(Delivery::getLoadedVehicleWeightPhoto, params.get("loadedVehicleWeightPhoto")); + } + + // 更新驾驶员手持身份证站车头照片 + if (params.get("driverIdCardPhoto") != null) { + updateWrapper.set(Delivery::getDriverIdCardPhoto, params.get("driverIdCardPhoto")); + } + + // 执行更新 + boolean updateResult = deliveryService.update(updateWrapper); + + if (updateResult) { + return AjaxResult.success("装车信息更新成功"); + } else { + return AjaxResult.error("装车信息更新失败"); + } + } catch (Exception e) { + e.printStackTrace(); + return AjaxResult.error("更新装车信息失败:" + e.getMessage()); + } + } +} diff --git a/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/controller/XqClientController.java b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/controller/XqClientController.java new file mode 100644 index 0000000..4b45672 --- /dev/null +++ b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/controller/XqClientController.java @@ -0,0 +1,107 @@ +package com.aiotagro.cattletrade.business.controller; + +import com.aiotagro.cattletrade.business.service.IXqClientService; +import com.aiotagro.common.core.web.domain.AjaxResult; +import com.aiotagro.common.core.web.domain.PageResultResponse; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; +import java.util.Map; + +/** + * 项圈客户端控制器 + * + * @author System + * @date 2025-01-16 + */ +@RestController +@RequestMapping("/xqClient") +public class XqClientController { + + @Resource + private IXqClientService xqClientService; + + /** + * 项圈列表(分页查询) + */ + @PostMapping(value = "/list") + public PageResultResponse list(@RequestBody Map params) { + return xqClientService.xqList(params); + } + + /** + * 根据运送清单ID查询项圈列表 + */ + @PostMapping(value = "/pageXqListByDeliveryId") + public AjaxResult pageXqListByDeliveryId(@RequestBody Map params) { + Object deliveryIdObj = params.get("deliveryId"); + Integer deliveryId = null; + if (deliveryIdObj != null) { + if (deliveryIdObj instanceof Integer) { + deliveryId = (Integer) deliveryIdObj; + } else if (deliveryIdObj instanceof String) { + deliveryId = Integer.valueOf((String) deliveryIdObj); + } + } + return xqClientService.getDevicesByDeliveryId(deliveryId, 3); // 3表示项圈 + } + + /** + * 查询分页列表(通用) + */ + @PostMapping(value = "/pageQuery") + public PageResultResponse pageQuery(@RequestBody Map params) { + return xqClientService.xqList(params); + } + + /** + * 项圈轨迹 + */ + @PostMapping(value = "/xqTrack") + public AjaxResult xqTrack(@RequestBody Map params) { + String deliveryId = params.get("deliveryId") == null ? null : String.valueOf(params.get("deliveryId")); + String xqDeviceId = params.get("xqDeviceId") == null ? null : String.valueOf(params.get("xqDeviceId")); + String trackTime = params.get("trackTime") == null ? null : String.valueOf(params.get("trackTime")); + String trackEndTime = params.get("trackEndTime") == null ? null : String.valueOf(params.get("trackEndTime")); + return xqClientService.getCollarTrack(deliveryId, xqDeviceId, trackTime, trackEndTime); + } + + /** + * 项圈位置 + */ + @PostMapping(value = "/xqLocation") + public AjaxResult xqLocation(@RequestBody Map params) { + String deliveryId = params.get("deliveryId") == null ? null : String.valueOf(params.get("deliveryId")); + String xqDeviceId = params.get("xqDeviceId") == null ? null : String.valueOf(params.get("xqDeviceId")); + return xqClientService.getCollarLocation(deliveryId, xqDeviceId); + } + + /** + * 测试项圈定位接口 + */ + @GetMapping("/testLocation/{deviceId}") + public AjaxResult testLocation(@PathVariable("deviceId") String deviceId) { + try { + System.out.println("Testing collar location for device: " + deviceId); + AjaxResult result = xqClientService.getCollarLocation(null, deviceId); + System.out.println("Location test completed"); + return result; + } catch (Exception e) { + e.printStackTrace(); + return AjaxResult.error("测试失败: " + e.getMessage()); + } + } + + /** + * 设备轨迹 + */ + @PostMapping(value = "/xqDeviceTrack") + public AjaxResult xqDeviceTrack(@RequestBody Map params) { + String deliveryId = params.get("deliveryId") == null ? null : String.valueOf(params.get("deliveryId")); + String xqDeviceId = params.get("xqDeviceId") == null ? null : String.valueOf(params.get("xqDeviceId")); + String trackTime = params.get("trackTime") == null ? null : String.valueOf(params.get("trackTime")); + String trackEndTime = params.get("trackEndTime") == null ? null : String.valueOf(params.get("trackEndTime")); + return xqClientService.getCollarDeviceTrack(deliveryId, xqDeviceId, trackTime, trackEndTime); + } +} + diff --git a/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/dto/BaseDto.java b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/dto/BaseDto.java new file mode 100644 index 0000000..06421d7 --- /dev/null +++ b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/dto/BaseDto.java @@ -0,0 +1,18 @@ +package com.aiotagro.cattletrade.business.dto; + +import lombok.Data; + +/** + * @author Carson + * @package_name com.aiotagro.payinfo.domain.dto + * @date 2024/2/18 16:38 + */ +@Data +public class BaseDto { + + private Integer pageNum = 1; + + private Integer pageSize = 10; + + +} diff --git a/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/dto/DeliverListDto.java b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/dto/DeliverListDto.java new file mode 100644 index 0000000..6acf263 --- /dev/null +++ b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/dto/DeliverListDto.java @@ -0,0 +1,44 @@ +package com.aiotagro.cattletrade.business.dto; + +import lombok.Data; + + +@Data +public class DeliverListDto extends BaseDto{ + /** + * 运单号 + */ + private String deliveryNumber; + /** + * 车牌号 + */ + private String licensePlate; + /** + * 创建时间-开始 + */ + private String startTime; + /** + * 创建时间-结束 + */ + private String endTime; + /** + * 预警类型 + */ + private Integer warningType; + /** + * 当前用户id + */ + private Integer currentUserId; + /** + * 核验状态 + */ + private Integer status; + /** + * 订单标题 + */ + private String deliveryTitle; + /** + * 目的地 + */ + private String endLocation; +} diff --git a/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/dto/DeliveryAddDto.java b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/dto/DeliveryAddDto.java new file mode 100644 index 0000000..7814cff --- /dev/null +++ b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/dto/DeliveryAddDto.java @@ -0,0 +1,84 @@ +package com.aiotagro.cattletrade.business.dto; + +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; + +import java.io.Serializable; +import java.util.Date; +import java.util.List; + +/** + * 我要装车对象 + */ +@Data +public class DeliveryAddDto implements Serializable { + /** + * 起始地 + */ + private String startLocation; + /** + * 起始纬度 + */ + private String startLat; + /** + * 起始经度 + */ + private String startLon; + /** + * 送达目的地 + */ + private String endLocation; + /** + * 目的地纬度 + */ + private String endLat; + /** + * 目的地经度 + */ + private String endLon; + /** + * 预计送达时间 + */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + private Date estimatedDeliveryTime; + /** + * 司机姓名 + */ + private String driverName; + /** + * 司机手机号 + */ + private String driverMobile; + /** + * 车牌号 + */ + private String licensePlate; + /** + * 车头照片 + */ + private String carFrontPhoto; + /** + * 车尾照片 + */ + private String carBehindPhoto; + /** + * 车视频 + */ + private String carVideo; + /** + * 登记智能耳标数 + */ + private Integer registeredJbqCount; + /** + * 主机编号 + */ + private String serverDeviceSn; + /** + * 耳标编号集合 + */ + private List jbqDeviceSn; + /** + * 核验人 + */ + private String checkBy; +} diff --git a/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/dto/DeliveryCreateDto.java b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/dto/DeliveryCreateDto.java new file mode 100644 index 0000000..8756182 --- /dev/null +++ b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/dto/DeliveryCreateDto.java @@ -0,0 +1,114 @@ +package com.aiotagro.cattletrade.business.dto; + +import lombok.Data; +import javax.validation.constraints.*; +import java.util.Date; +import java.util.List; + +/** + * 运送清单创建DTO + * + * @author System + * @date 2025-10-11 + */ +@Data +public class DeliveryCreateDto { + + /** + * 发货方 + */ + @NotBlank(message = "发货方不能为空") + private String shipper; + + /** + * 采购方 + */ + @NotBlank(message = "采购方不能为空") + private String buyer; + + /** + * 车牌号 + */ + @NotBlank(message = "车牌号不能为空") + @Pattern(regexp = "^[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领][A-Z][A-Z0-9]{5}[A-Z0-9挂学警港澳]$", + message = "车牌号格式不正确") + private String plateNumber; + + /** + * 司机姓名 + */ + @NotBlank(message = "司机姓名不能为空") + private String driverName; + + /** + * 司机电话 + */ + @NotBlank(message = "司机电话不能为空") + @Pattern(regexp = "^1[3-9]\\d{9}$", message = "手机号格式不正确") + private String driverPhone; + + /** + * 主机设备ID + */ + private Integer serverId; + + /** + * 耳标设备ID列表 + */ + private List eartagIds; + + /** + * 项圈设备ID列表 + */ + private List collarIds; + + /** + * 预计出发时间 + */ + @NotNull(message = "预计出发时间不能为空") + private Date estimatedDepartureTime; + + /** + * 预计到达时间 + */ + @NotNull(message = "预计到达时间不能为空") + private Date estimatedArrivalTime; + + /** + * 起点地址 + */ + @NotBlank(message = "起点地址不能为空") + private String startLocation; + + /** + * 目的地地址 + */ + @NotBlank(message = "目的地地址不能为空") + private String endLocation; + + /** + * 牛只数量 + */ + @NotNull(message = "牛只数量不能为空") + @Min(value = 1, message = "牛只数量至少为1") + private Integer cattleCount; + + /** + * 预估重量(公斤) + */ + @NotNull(message = "预估重量不能为空") + @DecimalMin(value = "0.01", message = "预估重量必须大于0") + private Double estimatedWeight; + + /** + * 检疫证号 + */ + private String quarantineCertNo; + + /** + * 备注 + */ + @Size(max = 500, message = "备注不能超过500字") + private String remark; +} + diff --git a/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/dto/DeliveryEditDto.java b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/dto/DeliveryEditDto.java new file mode 100644 index 0000000..592497f --- /dev/null +++ b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/dto/DeliveryEditDto.java @@ -0,0 +1,48 @@ +package com.aiotagro.cattletrade.business.dto; + +import lombok.Data; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; + +/** + * 运送清单编辑DTO + */ +@Data +public class DeliveryEditDto { + + @NotNull(message = "运送清单ID不能为空") + private Integer deliveryId; + + private String deliveryTitle; + + private Integer ratedQuantity; + + private String supplierId; + + private Integer fundId; + + private Integer driverId; + + private String driverMobile; + + private Integer buyerId; + + private Double buyerPrice; + + private Double salePrice; + + private Double firmPrice; + + private String startLocation; + + private String startLat; + + private String startLon; + + private String endLocation; + + private String endLat; + + private String endLon; +} diff --git a/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/dto/DeliveryQueryDto.java b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/dto/DeliveryQueryDto.java new file mode 100644 index 0000000..86b7f8e --- /dev/null +++ b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/dto/DeliveryQueryDto.java @@ -0,0 +1,23 @@ +package com.aiotagro.cattletrade.business.dto; + +import com.aiotagro.cattletrade.business.dto.BaseDto; +import lombok.Data; + +import javax.validation.constraints.NotNull; + +@Data +public class DeliveryQueryDto extends BaseDto { + + /** + * 运单号 + */ + private String deliveryNumber; + /** + * 预警类型 + */ + private Integer warningType; + /** + * 当前登陆人 + */ + private Integer userId; +} diff --git a/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/dto/DeviceAssignDto.java b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/dto/DeviceAssignDto.java new file mode 100644 index 0000000..b527e16 --- /dev/null +++ b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/dto/DeviceAssignDto.java @@ -0,0 +1,38 @@ +package com.aiotagro.cattletrade.business.dto; + +import lombok.Data; +import java.util.List; + +/** + * 设备分配DTO + * + * @author admin + * @since 2024-12-26 + */ +@Data +public class DeviceAssignDto { + + /** + * 运单ID + */ + private Integer deliveryId; + + /** + * 设备信息列表 + */ + private List deviceIds; + + /** + * 车牌号 + */ + private String licensePlate; + + /** + * 设备信息内部类 + */ + @Data + public static class DeviceInfo { + private String deviceId; + private Integer deviceTypeId; + } +} diff --git a/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/dto/DeviceDto.java b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/dto/DeviceDto.java new file mode 100644 index 0000000..818dd05 --- /dev/null +++ b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/dto/DeviceDto.java @@ -0,0 +1,32 @@ +package com.aiotagro.cattletrade.business.dto; + +import lombok.Data; + +@Data +public class DeviceDto extends BaseDto{ + /** + * 耳标编号 + */ + private String deviceId; + + /** + * 开始号段 + */ + private String startNo; + + /** + * 结束号段 + */ + private String endNo; + + /** + * 设备类型:1=智能主机,2=智能耳标,3=智能项圈 + */ + private Integer deviceType; + + /** + * 是否只查询未分配的设备 + */ + private Boolean unassignedOnly; + +} diff --git a/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/dto/FileDto.java b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/dto/FileDto.java new file mode 100644 index 0000000..b5d932c --- /dev/null +++ b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/dto/FileDto.java @@ -0,0 +1,10 @@ +package com.aiotagro.cattletrade.business.dto; + +import lombok.Data; + +@Data +public class FileDto { + + private String src; + +} diff --git a/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/dto/JbqLogDto.java b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/dto/JbqLogDto.java new file mode 100644 index 0000000..4e7347e --- /dev/null +++ b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/dto/JbqLogDto.java @@ -0,0 +1,8 @@ +package com.aiotagro.cattletrade.business.dto; + +import lombok.Data; + +@Data +public class JbqLogDto extends BaseDto{ + private Integer deliveryId; +} diff --git a/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/dto/LoginDto.java b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/dto/LoginDto.java new file mode 100644 index 0000000..fc4464c --- /dev/null +++ b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/dto/LoginDto.java @@ -0,0 +1,25 @@ +package com.aiotagro.cattletrade.business.dto; + +import lombok.Data; + +import javax.validation.constraints.NotBlank; + +@Data +public class LoginDto { + + /** + * 手机号 + */ + @NotBlank(message = "手机号不能为空") + private String mobile; + + /** + * 用户密码 + */ + private String password; + + /** + * 登录类型:0-密码 1-验证码 + */ + private Integer loginType; +} diff --git a/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/dto/ServerLocationDto.java b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/dto/ServerLocationDto.java new file mode 100644 index 0000000..84a964a --- /dev/null +++ b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/dto/ServerLocationDto.java @@ -0,0 +1,15 @@ +package com.aiotagro.cattletrade.business.dto; + +import lombok.Data; + +@Data +public class ServerLocationDto { + /** + * 运单id + */ + private Integer deliveryId; + /** + * 主机id + */ + private String serverDeviceId; +} diff --git a/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/dto/SysRoleDto.java b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/dto/SysRoleDto.java new file mode 100644 index 0000000..56940cb --- /dev/null +++ b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/dto/SysRoleDto.java @@ -0,0 +1,16 @@ +package com.aiotagro.cattletrade.business.dto; + +import lombok.Data; + +/** + * @author xiefan + */ +@Data +public class SysRoleDto extends BaseDto { + + /** + * 岗位名称/角色名称 + */ + private String name; + +} diff --git a/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/dto/SysUserDto.java b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/dto/SysUserDto.java new file mode 100644 index 0000000..586c1bc --- /dev/null +++ b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/dto/SysUserDto.java @@ -0,0 +1,16 @@ +package com.aiotagro.cattletrade.business.dto; + +import lombok.Data; + +/** + * @author xiefan + */ +@Data +public class SysUserDto extends BaseDto { + + /** + * 员工姓名 + */ + private String name; + +} diff --git a/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/dto/WarningDetailDto.java b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/dto/WarningDetailDto.java new file mode 100644 index 0000000..e56fa13 --- /dev/null +++ b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/dto/WarningDetailDto.java @@ -0,0 +1,110 @@ +package com.aiotagro.cattletrade.business.dto; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; + +import java.io.Serializable; +import java.util.Date; +import java.util.List; + +@Data +public class WarningDetailDto implements Serializable { + /** + * 预警原因 + */ + private String warningReason; + /** + * 预警时间 + */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + private Date warningTime; + /** + * 应行驶距离 + */ + private String expectedDistance; + + /** + * 实际行驶距离 + */ + private String actualDistance; + /** + * 起始地 + */ + private String startLocation; + /** + * 起始纬度 + */ + private String startLat; + /** + * 起始经度 + */ + private String startLon; + /** + * 送达目的地 + */ + private String endLocation; + /** + * 目的地纬度 + */ + private String endLat; + /** + * 目的地经度 + */ + private String endLon; + /** + * 预计送达时间 + */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + private Date estimatedDeliveryTime; + /** + * 司机姓名 + */ + private String driverName; + /** + * 司机手机号 + */ + private String driverMobile; + /** + * 车牌号 + */ + private String licensePlate; + /** + * 车头照片 + */ + private String carFrontPhoto; + /** + * 车尾照片 + */ + private String carBehindPhoto; + /** + * 车视频 + */ + private String carVideo; + /** + * 登记智能耳标数 + */ + private Integer registeredJbqCount; + /** + * 主机编号 + */ + private String serverDeviceSn; + /** + * 耳标编号集合 + */ + private List jbqDeviceSn; + /** + * 核验人 + */ + private Integer checkBy; + + /** + * 预警类型,2:数量盘点预警,3:距离盘点预警 + */ + private String warningType; + + /** + * 车内盘点数量 + */ + private Integer inventoryJbqCount; +} diff --git a/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/entity/Delivery.java b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/entity/Delivery.java new file mode 100644 index 0000000..ecbf974 --- /dev/null +++ b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/entity/Delivery.java @@ -0,0 +1,412 @@ +package com.aiotagro.cattletrade.business.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import java.io.Serializable; +import java.util.Date; +import java.util.List; + +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Getter; +import lombok.Setter; + +/** + *

+ * 运送清单表 + *

+ * + * @author admin + * @since 2024-12-26 + */ +@Getter +@Setter +@TableName("delivery") +public class Delivery implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * 自增ID + */ + @TableId(value = "id", type = IdType.AUTO) + private Integer id; + + /** + * 运单号 + */ + @TableField("delivery_number") + private String deliveryNumber; + + /** + * 订单标题 + */ + @TableField("delivery_title") + private String deliveryTitle; + + /** + * 装车数量 + */ + @TableField("rated_quantity") + private Integer ratedQuantity; + + /** + * 供应商ID + */ + @TableField("supplier_id") + private String supplierId; + + /** + * 资金方ID + */ + @TableField("fund_id") + private Integer fundId; + + /** + * 司机ID + */ + @TableField("driver_id") + private Integer driverId; + + /** + * 采购商ID + */ + @TableField("buyer_id") + private Integer buyerId; + + /** + * 采购价格 + */ + @TableField("buyer_price") + private Double buyerPrice; + + /** + * 销售价格 + */ + @TableField("sale_price") + private Double salePrice; + + /** + * 约定单价 + */ + @TableField("firm_price") + private Double firmPrice; + + /** + * 状态,1-待核验,2-已核验 + */ + @TableField("status") + private Integer status; + + /** + * 车牌号 + */ + @TableField("license_plate") + private String licensePlate; + + /** + * 车头照片 + */ + @TableField("car_front_photo") + private String carFrontPhoto; + + /** + * 车尾照片 + */ + @TableField("car_behind_photo") + private String carBehindPhoto; + + /** + * 车视频 + */ + @TableField("car_video") + private String carVideo; + + /** + * 起始地 + */ + @TableField("start_location") + private String startLocation; + + /** + * 起始经度 + */ + @TableField("start_lat") + private String startLat; + + /** + * 起始纬度 + */ + @TableField("start_lon") + private String startLon; + + /** + * 送达目的地 + */ + @TableField("end_location") + private String endLocation; + + /** + * 目的地经度 + */ + @TableField("end_lat") + private String endLat; + + /** + * 目的地纬度 + */ + @TableField("end_lon") + private String endLon; + + /** + * 预计送达时间 + */ + @TableField("estimated_delivery_time") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + private Date estimatedDeliveryTime; + + /** + * 登记智能耳标数 + */ + @TableField("registered_jbq_count") + private Integer registeredJbqCount; + + /** + * 已分配设备数量 + */ + @TableField(exist = false) + private Integer bindJbqCount; + + /** + * 已佩戴设备数量 + */ + @TableField(exist = false) + private Integer wareCount; + + /** + * 司机姓名 + */ + @TableField("driver_name") + private String driverName; + + /** + * 司机手机号 + */ + @TableField("driver_mobile") + private String driverMobile; + + /** + * 创建时间 + */ + @TableField("create_time") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + private Date createTime; + + /** + * 创建人 + */ + @TableField("created_by") + private Integer createdBy; + + /** + * 核验人 + */ + @TableField("check_by") + private Integer checkBy; + + /** + * 核验时间 + */ + @TableField("check_time") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + private Date checkTime; + + /** + * 核验视频 + */ + @TableField("check_video") + private String checkVideo; + + /** + * 空车过磅重量 + */ + @TableField("empty_weight") + private String emptyWeight; + + /** + * 装车过磅重量 + */ + @TableField("entruck_weight") + private String entruckWeight; + + /** + * 检疫票 + */ + @TableField("quarantine_tickey_url") + private String quarantineTickeyUrl; + + /** + * 传纸质磅单(双章) + */ + @TableField("pound_list_img") + private String poundListImg; + + /** + * 装车过磅视频 + */ + @TableField("entruck_weight_video") + private String entruckWeightVideo; + + /** + * 空车过磅视频 + */ + @TableField("empty_weight_video") + private String emptyWeightVideo; + + /** + * 上传装牛视频 + */ + @TableField("entruck_video") + private String entruckVideo; + + /** + * 控槽视频 + */ + @TableField("control_slot_video") + private String controlSlotVideo; + + /** + * 车辆空磅上磅车头照片 + */ + @TableField("empty_vehicle_front_photo") + private String emptyVehicleFrontPhoto; + + /** + * 装完牛绕车一圈视频 + */ + @TableField("cattle_loading_circle_video") + private String cattleLoadingCircleVideo; + + /** + * 车辆过重磅车头照片 + */ + @TableField("loaded_vehicle_front_photo") + private String loadedVehicleFrontPhoto; + + /** + * 车辆重磅照片 + */ + @TableField("loaded_vehicle_weight_photo") + private String loadedVehicleWeightPhoto; + + /** + * 驾驶员手持身份证站车头照片 + */ + @TableField("driver_id_card_photo") + private String driverIdCardPhoto; + + /** + * 主机设备编号 + */ + @TableField(exist = false) + private String serverDeviceId; + + /** + * 耳标设备编号 + */ + @TableField(exist = false) + private List deviceIds; + + /** + * 是否需要核验,1:需要核验,2:不需要核验 + */ + @TableField(exist = false) + private Integer ifCheck; + + /** + * 预警类型 + */ + @TableField(exist = false) + private String warningType; + /** + * 预警时间 + */ + @TableField(exist = false) + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + private Date warningTime; + /** + * 车内盘点数量 + */ + @TableField(exist = false) + private Integer inventoryJbqCount; + + /** + * 核验人姓名 + */ + @TableField(exist = false) + private String checkByName; + + /** + * 预警类型描述, 1-正常 2-盘点预警 3-距离预警 + */ + @TableField(exist = false) + private String warningTypeDesc; + + @TableField(exist = false) + private List warningTypeList; + + /** + * 创建人姓名 + */ + @TableField(exist = false) + private String createByName; + + /** + * 供应商名称 + */ + @TableField(exist = false) + private String supplierName; + + /** + * 资金方名称 + */ + @TableField(exist = false) + private String fundName; + + /** + * 采购商名称 + */ + @TableField(exist = false) + private String buyerName; + + /** + * 核验状态描述 + */ + @TableField(exist = false) + private String statusDesc; + + /** + * 耳标设备数量 + */ + @TableField(exist = false) + private Integer earTagCount; + + /** + * 供应商手机号(逗号分隔) + */ + @TableField(exist = false) + private String supplierMobile; + + /** + * 资金方手机号 + */ + @TableField(exist = false) + private String fundMobile; + + /** + * 采购商手机号 + */ + @TableField(exist = false) + private String buyerMobile; +} diff --git a/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/entity/DeliveryDevice.java b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/entity/DeliveryDevice.java new file mode 100644 index 0000000..22f8548 --- /dev/null +++ b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/entity/DeliveryDevice.java @@ -0,0 +1,112 @@ +package com.aiotagro.cattletrade.business.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Date; +import lombok.Getter; +import lombok.Setter; + +/** + *

+ * 运单设备表 + *

+ * + * @author admin + * @since 2024-12-26 + */ +@Getter +@Setter +@TableName("delivery_device") +public class DeliveryDevice implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * 主键id + */ + @TableId(value = "id", type = IdType.AUTO) + private Integer id; + + /** + * 运单ID + */ + @TableField("delivery_id") + private Integer deliveryId; + + /** + * 设备编号 + */ + @TableField("device_id") + private String deviceId; + + /** + * 设备类型: 1 主机 2 耳标 3 项圈 + */ + @TableField("device_type") + private Integer deviceType; + + /** + * 设备用户 + */ + @TableField("device_user") + private String deviceUser; + + /** + * 是否佩戴 + */ + @TableField("is_ware") + private Integer isWare; + + /** + * 正面图片 + */ + @TableField("front_img") + private String frontImg; + + /** + * 侧面图片 + */ + @TableField("side_img") + private String sideImg; + + /** + * 臀部图片 + */ + @TableField("hip_img") + private String hipImg; + + /** + * 绑定时间 + */ + @TableField("bind_time") + private Date bindTime; + + /** + * 创建时间 + */ + @TableField("create_time") + private Date createTime; + + /** + * 绑定重量 + */ + @TableField("bind_weight") + private BigDecimal bindWeight; + + /** + * 删除标志 + */ + @TableField("deleted") + private Integer deleted; + + /** + * 租户ID + */ + @TableField("tenant_id") + private Integer tenantId; + +} diff --git a/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/entity/JbqClient.java b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/entity/JbqClient.java new file mode 100644 index 0000000..0c68783 --- /dev/null +++ b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/entity/JbqClient.java @@ -0,0 +1,123 @@ +package com.aiotagro.cattletrade.business.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import java.io.Serializable; +import java.util.Date; +import lombok.Getter; +import lombok.Setter; + +/** + *

+ * 智能耳标表 + *

+ * + * @author admin + * @since 2024-12-26 + */ +@Getter +@Setter +@TableName("jbq_client") +public class JbqClient implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * 数据主键 + */ + @TableId(value = "id", type = IdType.AUTO) + private Integer id; + + /** + * 耳标编号 + */ + @TableField("device_id") + private String deviceId; + + /** + * 设备电量 + */ + @TableField("device_voltage") + private String deviceVoltage; + + /** + * 设备温度 + */ + @TableField("device_temp") + private String deviceTemp; + + /** + * 主机编号 + */ + @TableField("server_device_id") + private String serverDeviceId; + + /** + * 纬度 + */ + @TableField("latitude") + private String latitude; + + /** + * 经度 + */ + @TableField("longitude") + private String longitude; + + /** + * 总步数 + */ + @TableField("walk_steps") + private Long walkSteps; + + /** + * 昨日总步数 + */ + @TableField("y_walk_steps") + private Long yWalkSteps; + + /** + * 创建时间 + */ + @TableField("create_time") + private Date createTime; + + /** + * 创建人 + */ + @TableField("create_by") + private String createBy; + + /** + * 更新时间 + */ + @TableField("update_time") + private Date updateTime; + + /** + * 更新人 + */ + @TableField("update_by") + private String updateBy; + + /** + * 物联网平台同步标识 + */ + @TableField("source_id") + private Long sourceId; + + /** + * 运单号 + */ + @TableField("delivery_number") + private String deliveryNumber; + + /** + * 车牌号 + */ + @TableField("license_plate") + private String licensePlate; + +} diff --git a/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/entity/JbqClientLog.java b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/entity/JbqClientLog.java new file mode 100644 index 0000000..30b6eb9 --- /dev/null +++ b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/entity/JbqClientLog.java @@ -0,0 +1,110 @@ +package com.aiotagro.cattletrade.business.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import java.io.Serializable; +import java.util.Date; + +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Getter; +import lombok.Setter; + +/** + *

+ * 智能耳标日志表 + *

+ * + * @author admin + * @since 2024-12-26 + */ +@Getter +@Setter +@TableName("jbq_client_log") +public class JbqClientLog implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * 数据主键 + */ + @TableId(value = "id", type = IdType.AUTO) + private Integer id; + + /** + * 耳标编号 + */ + @TableField("device_id") + private String deviceId; + + /** + * 设备电量 + */ + @TableField("device_voltage") + private String deviceVoltage; + + /** + * 设备温度 + */ + @TableField("device_temp") + private String deviceTemp; + + /** + * 主机编号 + */ + @TableField("server_device_id") + private String serverDeviceId; + + /** + * 纬度 + */ + @TableField("latitude") + private String latitude; + + /** + * 经度 + */ + @TableField("longitude") + private String longitude; + + /** + * 总步数 + */ + @TableField("walk_steps") + private Long walkSteps; + + /** + * 昨日总步数 + */ + @TableField("y_walk_steps") + private Long yWalkSteps; + + /** + * 创建时间 + */ + @TableField("create_time") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + private Date createTime; + + /** + * 创建人 + */ + @TableField("create_by") + private String createBy; + + /** + * 更新时间 + */ + @TableField("update_time") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + private Date updateTime; + + /** + * 更新人 + */ + @TableField("update_by") + private String updateBy; + + +} diff --git a/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/entity/JbqServer.java b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/entity/JbqServer.java new file mode 100644 index 0000000..4bacf99 --- /dev/null +++ b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/entity/JbqServer.java @@ -0,0 +1,106 @@ +package com.aiotagro.cattletrade.business.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import java.io.Serializable; +import java.util.Date; +import lombok.Getter; +import lombok.Setter; + +/** + *

+ * 智能主机表 + *

+ * + * @author admin + * @since 2024-12-26 + */ +@Getter +@Setter +@TableName("jbq_server") +public class JbqServer implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * 数据主键 + */ + @TableId(value = "id", type = IdType.AUTO) + private Integer id; + + /** + * 主机编号 + */ + @TableField("device_id") + private String deviceId; + + /** + * 设备电量 + */ + @TableField("device_voltage") + private String deviceVoltage; + + /** + * 设备温度 + */ + @TableField("device_temp") + private String deviceTemp; + + /** + * 纬度 + */ + @TableField("latitude") + private String latitude; + + /** + * 经度 + */ + @TableField("longitude") + private String longitude; + + /** + * 总步数 + */ + @TableField("walk_steps") + private Long walkSteps; + + /** + * 昨日总步数 + */ + @TableField("y_walk_steps") + private Long yWalkSteps; + + /** + * 创建时间 + */ + @TableField("create_time") + private Date createTime; + + /** + * 创建人 + */ + @TableField("create_by") + private String createBy; + + /** + * 更新时间 + */ + @TableField("update_time") + private Date updateTime; + + /** + * 更新人 + */ + @TableField("update_by") + private String updateBy; + + /** + * 物联网平台同步标识 + */ + @TableField("source_id") + private Long sourceId; + + +} diff --git a/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/entity/JbqServerLog.java b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/entity/JbqServerLog.java new file mode 100644 index 0000000..88d734d --- /dev/null +++ b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/entity/JbqServerLog.java @@ -0,0 +1,100 @@ +package com.aiotagro.cattletrade.business.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import java.io.Serializable; +import java.util.Date; +import lombok.Getter; +import lombok.Setter; + +/** + *

+ * 智能主机日志表 + *

+ * + * @author admin + * @since 2024-12-26 + */ +@Getter +@Setter +@TableName("jbq_server_log") +public class JbqServerLog implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * 数据主键 + */ + @TableId(value = "id", type = IdType.AUTO) + private Integer id; + + /** + * 主机编号 + */ + @TableField("device_id") + private String deviceId; + + /** + * 设备电量 + */ + @TableField("device_voltage") + private String deviceVoltage; + + /** + * 设备温度 + */ + @TableField("device_temp") + private String deviceTemp; + + /** + * 纬度 + */ + @TableField("latitude") + private String latitude; + + /** + * 经度 + */ + @TableField("longitude") + private String longitude; + + /** + * 总步数 + */ + @TableField("walk_steps") + private Long walkSteps; + + /** + * 昨日总步数 + */ + @TableField("y_walk_steps") + private Long yWalkSteps; + + /** + * 创建时间 + */ + @TableField("create_time") + private Date createTime; + + /** + * 创建人 + */ + @TableField("create_by") + private String createBy; + + /** + * 更新时间 + */ + @TableField("update_time") + private Date updateTime; + + /** + * 更新人 + */ + @TableField("update_by") + private String updateBy; + + +} diff --git a/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/entity/Member.java b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/entity/Member.java new file mode 100644 index 0000000..20499ae --- /dev/null +++ b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/entity/Member.java @@ -0,0 +1,39 @@ +package com.aiotagro.cattletrade.business.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +import java.util.Date; + +/** + * Member实体 + * + * @author System + * @date 2025-01-16 + */ +@Data +@TableName("member") +public class Member { + + @TableId(type = IdType.AUTO) + private Integer id; + + private String mobile; + + private Integer type; + + private Boolean status; + + private Date lastLoginTime; + + private Integer createBy; + + private Date createTime; + + private Date updateTime; + + private Integer tenantId; +} + diff --git a/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/entity/SysMenu.java b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/entity/SysMenu.java new file mode 100644 index 0000000..e537e58 --- /dev/null +++ b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/entity/SysMenu.java @@ -0,0 +1,104 @@ +package com.aiotagro.cattletrade.business.entity; + +import com.baomidou.mybatisplus.annotation.*; +import lombok.Getter; +import lombok.Setter; + +import java.io.Serializable; +import java.util.Date; + +/** + *

+ * 系统菜单表 + *

+ * + * @author admin + * @since 2024-12-07 + */ +@Getter +@Setter +@TableName("sys_menu") +public class SysMenu implements Serializable { + + private static final long serialVersionUID = 1L; + + @TableId(value = "id", type = IdType.AUTO) + private Integer id; + + /** + * 上级id + */ + @TableField("parent_id") + private Integer parentId; + + /** + * 菜单类型 0-目录 1-菜单 2-按钮 + */ + @TableField("type") + private Integer type; + + /** + * 菜单图标 + */ + @TableField("icon") + private String icon; + + /** + * 菜单名称 + */ + @TableField("name") + private String name; + + /** + * 排序 数字越小越靠前 + */ + @TableField("sort") + private Integer sort; + + /** + * 后端路由地址 + */ + @TableField("route_url") + private String routeUrl; + + /** + * 前端路由地址 + */ + @TableField("page_url") + private String pageUrl; + + /** + * 所属机构 1-海关端 2-企业端 3海关企业共用 + */ + @TableField("org_type") + private Integer orgType; + + /** + * 权限字符串 + */ + @TableField("authority") + private String authority; + + /** + * 创建时间 + */ + @TableField("create_time") + private Date createTime; + + /** + * 修改时间 + */ + @TableField("update_time") + private Date updateTime; + + /** + * 是否删除 0-未删除 1-已删除 + */ + @TableLogic("is_delete") + private Integer isDelete; + + /** + * 是否被选中 0-未选中 1-选中 + */ + private Integer isSelected; +} diff --git a/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/entity/SysRole.java b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/entity/SysRole.java new file mode 100644 index 0000000..fb9b574 --- /dev/null +++ b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/entity/SysRole.java @@ -0,0 +1,73 @@ +package com.aiotagro.cattletrade.business.entity; + +import com.baomidou.mybatisplus.annotation.*; +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Getter; +import lombok.Setter; + +import java.io.Serializable; +import java.util.Date; +import java.util.List; + +/** + *

+ * 岗位管理表 + *

+ * + * @author admin + * @since 2024-12-07 + */ +@Getter +@Setter +@TableName("sys_role") +public class SysRole implements Serializable { + + private static final long serialVersionUID = 1L; + + @TableId(value = "id", type = IdType.AUTO) + private Integer id; + + /** + * 岗位名称 + */ + @TableField("name") + private String name; + + /** + * 岗位描述 + */ + @TableField("description") + private String description; + + /** + * 创建时间 + */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + @TableField("create_time") + private Date createTime; + + /** + * 更新时间 + */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + @TableField("update_time") + private Date updateTime; + + /** + * 是否删除 0-未删除 1-已删除 + */ + @TableLogic("is_delete") + private Integer isDelete; + + /** + * 菜单id + */ + @TableField(exist = false) + private List menuIds; + + /** + * 菜单 + */ + @TableField(exist = false) + private List menuList; +} diff --git a/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/entity/SysRoleMenu.java b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/entity/SysRoleMenu.java new file mode 100644 index 0000000..d7e7dd1 --- /dev/null +++ b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/entity/SysRoleMenu.java @@ -0,0 +1,43 @@ +package com.aiotagro.cattletrade.business.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Getter; +import lombok.Setter; + +import java.io.Serializable; + +/** + *

+ * 岗位菜单表 + *

+ * + * @author admin + * @since 2024-12-07 + */ +@Getter +@Setter +@TableName("sys_role_menu") +public class SysRoleMenu implements Serializable { + + private static final long serialVersionUID = 1L; + + @TableId(value = "id", type = IdType.AUTO) + private Integer id; + + /** + * 岗位id + */ + @TableField("role_id") + private Integer roleId; + + /** + * 菜单id + */ + @TableField("menu_id") + private Integer menuId; + + +} diff --git a/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/entity/SysTenant.java b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/entity/SysTenant.java new file mode 100644 index 0000000..157b687 --- /dev/null +++ b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/entity/SysTenant.java @@ -0,0 +1,31 @@ +package com.aiotagro.cattletrade.business.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +import java.util.Date; + +/** + * 租户表实体 + * + * @author System + * @date 2025-01-16 + */ +@Data +@TableName("sys_tenant") +public class SysTenant { + + @TableId(type = IdType.AUTO) + private Integer id; + + private String name; + + private String mobile; + + private Date createTime; + + private Integer isDelete; +} + diff --git a/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/entity/SysUser.java b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/entity/SysUser.java new file mode 100644 index 0000000..b7a695a --- /dev/null +++ b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/entity/SysUser.java @@ -0,0 +1,109 @@ +package com.aiotagro.cattletrade.business.entity; + +import com.baomidou.mybatisplus.annotation.*; +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Getter; +import lombok.Setter; + +import java.io.Serializable; +import java.util.Date; + +/** + *

+ * 员工管理表 + *

+ * + * @author admin + * @since 2024-12-26 + */ +@Getter +@Setter +@TableName("sys_user") +public class SysUser implements Serializable { + + private static final long serialVersionUID = 1L; + + @TableId(value = "id", type = IdType.AUTO) + private Integer id; + + /** + * 员工姓名 + */ + @TableField("name") + private String name; + + /** + * 手机号 + */ + @TableField("mobile") + private String mobile; + + /** + * 岗位id + */ + @TableField("role_id") + private Integer roleId; + + /** + * 密码 + */ + @TableField("password") + private String password; + + /** + * 状态 1-正常 0-禁用 + */ + @TableField("status") + private Integer status; + + /** + * 创建时间 + */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + @TableField("create_time") + private Date createTime; + + /** + * 创建人id + */ + @TableField("create_by") + private Integer createBy; + + /** + * 更新时间 + */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + @TableField("update_time") + private Date updateTime; + + /** + * 更新人id + */ + @TableField("update_by") + private Integer updateBy; + + /** + * 最后登录时间 + */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + @TableField("last_login_time") + private Date lastLoginTime; + + /** + * 删除状态 0-正常 1-已删除 + */ + @TableLogic("is_delete") + private Integer isDelete; + + /** + * 创建人 + */ + @TableField(exist = false) + private String createName; + + /** + * 岗位/角色 + */ + @TableField(exist = false) + private String roleName; +} diff --git a/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/entity/WarningLog.java b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/entity/WarningLog.java new file mode 100644 index 0000000..01adc04 --- /dev/null +++ b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/entity/WarningLog.java @@ -0,0 +1,79 @@ +package com.aiotagro.cattletrade.business.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import java.io.Serializable; +import java.util.Date; + +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Getter; +import lombok.Setter; + +/** + *

+ * 预警记录表 + *

+ * + * @author admin + * @since 2024-12-26 + */ +@Getter +@Setter +@TableName("warning_log") +public class WarningLog implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * 自增ID + */ + @TableId(value = "id", type = IdType.AUTO) + private Integer id; + + /** + * 运单ID + */ + @TableField("delivery_id") + private Integer deliveryId; + + /** + * 预警类型, 1-正常 2-盘点预警 3-距离预警 + */ + @TableField("warning_type") + private String warningType; + + /** + * 车内盘点耳标数量 + */ + @TableField("inventory_jbq_count") + private Integer inventoryJbqCount; + + /** + * 应行驶距离 + */ + @TableField("expected_distance") + private String expectedDistance; + + /** + * 实际行驶距离 + */ + @TableField("actual_distance") + private String actualDistance; + + /** + * 预警时间 + */ + @TableField("warning_time") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + private Date warningTime; + + /** + * 预警描述 + */ + @TableField(exist = false) + private String warningTypeDesc; + + +} diff --git a/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/entity/XqClient.java b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/entity/XqClient.java new file mode 100644 index 0000000..9e8702e --- /dev/null +++ b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/entity/XqClient.java @@ -0,0 +1,118 @@ +package com.aiotagro.cattletrade.business.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +import java.util.Date; + +/** + * 项圈客户端实体 + * + * @author System + * @date 2025-01-16 + */ +@Data +@TableName(value = "xq_client", autoResultMap = true) +public class XqClient { + + @TableId(type = IdType.AUTO) + private Integer id; + + @TableField("org_id") + private Integer orgId; + + private Integer uid; + + @TableField("deviceId") + private String deviceId; + + private Long sn; + + private Integer state; + + private String longitude; + + private String latitude; + + private String altitude; + + @TableField("gps_state") + private String gpsState; + + private Integer nsat; + + private String rsrp; + + private String battery; + + private String temperature; + + private Long steps; + + @TableField("acc_x") + private String accX; + + @TableField("acc_y") + private String accY; + + @TableField("acc_z") + private String accZ; + + @TableField("bandge_status") + private Integer bandgeStatus; + + private String ver; + + private Date time; + + private Date uptime; + + @TableField("y_steps") + private Long ySteps; + + @TableField("is_wear") + private Integer isWear; + + @TableField("is_temperature") + private Integer isTemperature; + + @TableField("source_id") + private Long sourceId; + + private String loctime; + + @TableField("subType") + private Integer subType; + + private Integer frequency; + + @TableField("vehicle_id") + private Integer vehicleId; + + @TableField("evening_frequency") + private Integer eveningFrequency; + + @TableField("tenant_id") + private Integer tenantId; + + /** + * 运单ID + */ + @TableField("delivery_id") + private Integer deliveryId; + + /** + * 运单号 + */ + @TableField("delivery_number") + private String deliveryNumber; + + /** + * 车牌号 + */ + @TableField("license_plate") + private String licensePlate; +} diff --git a/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/mapper/DeliveryDeviceMapper.java b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/mapper/DeliveryDeviceMapper.java new file mode 100644 index 0000000..33a9101 --- /dev/null +++ b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/mapper/DeliveryDeviceMapper.java @@ -0,0 +1,18 @@ +package com.aiotagro.cattletrade.business.mapper; + +import com.aiotagro.cattletrade.business.entity.DeliveryDevice; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Mapper; + +/** + *

+ * 运单设备表 Mapper 接口 + *

+ * + * @author admin + * @since 2024-12-26 + */ +@Mapper +public interface DeliveryDeviceMapper extends BaseMapper { + +} diff --git a/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/mapper/DeliveryMapper.java b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/mapper/DeliveryMapper.java new file mode 100644 index 0000000..5057857 --- /dev/null +++ b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/mapper/DeliveryMapper.java @@ -0,0 +1,25 @@ +package com.aiotagro.cattletrade.business.mapper; + +import com.aiotagro.cattletrade.business.dto.DeliverListDto; +import com.aiotagro.cattletrade.business.entity.Delivery; +import com.aiotagro.cattletrade.business.vo.DeliveryLogVo; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + *

+ * 运送清单表 Mapper 接口 + *

+ * + * @author admin + * @since 2024-12-26 + */ +@Mapper +public interface DeliveryMapper extends BaseMapper { + + + List getPageWarningLog(@Param("dto") DeliverListDto dto); +} diff --git a/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/mapper/JbqClientLogMapper.java b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/mapper/JbqClientLogMapper.java new file mode 100644 index 0000000..d7321cf --- /dev/null +++ b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/mapper/JbqClientLogMapper.java @@ -0,0 +1,23 @@ +package com.aiotagro.cattletrade.business.mapper; + +import com.aiotagro.cattletrade.business.entity.JbqClientLog; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.Date; +import java.util.List; + +/** + *

+ * 智能耳标日志表 Mapper 接口 + *

+ * + * @author admin + * @since 2024-12-26 + */ +@Mapper +public interface JbqClientLogMapper extends BaseMapper { + + List jbqLogList(@Param("deliveryId") Integer deliveryId,@Param("createTime") Date createTime,@Param("checkTime") Date checkTime); +} diff --git a/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/mapper/JbqClientMapper.java b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/mapper/JbqClientMapper.java new file mode 100644 index 0000000..b0be4b7 --- /dev/null +++ b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/mapper/JbqClientMapper.java @@ -0,0 +1,29 @@ +package com.aiotagro.cattletrade.business.mapper; + +import com.aiotagro.cattletrade.business.dto.DeviceDto; +import com.aiotagro.cattletrade.business.entity.JbqClient; +import com.aiotagro.cattletrade.business.vo.JbqClientVo; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + *

+ * 智能耳标表 Mapper 接口 + *

+ * + * @author admin + * @since 2024-12-26 + */ +@Mapper +public interface JbqClientMapper extends BaseMapper { + + List JbqList(@Param("item") DeviceDto dto); + + /** + * 查询未分配的智能耳标列表 + */ + List selectUnassignedJbqList(@Param("item") DeviceDto dto); +} diff --git a/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/mapper/JbqServerLogMapper.java b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/mapper/JbqServerLogMapper.java new file mode 100644 index 0000000..00bb266 --- /dev/null +++ b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/mapper/JbqServerLogMapper.java @@ -0,0 +1,24 @@ +package com.aiotagro.cattletrade.business.mapper; + +import com.aiotagro.cattletrade.business.entity.JbqServerLog; +import com.aiotagro.cattletrade.business.vo.ServerLocationVo; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.Date; +import java.util.List; + +/** + *

+ * 智能主机日志表 Mapper 接口 + *

+ * + * @author admin + * @since 2024-12-26 + */ +@Mapper +public interface JbqServerLogMapper extends BaseMapper { + + List selectServerLocationByTime(@Param("deviceId") String serverDeviceId,@Param("deliverTime") Date deliverTime,@Param("checkTime") Date checkTime); +} diff --git a/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/mapper/JbqServerMapper.java b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/mapper/JbqServerMapper.java new file mode 100644 index 0000000..58c4d3f --- /dev/null +++ b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/mapper/JbqServerMapper.java @@ -0,0 +1,24 @@ +package com.aiotagro.cattletrade.business.mapper; + +import com.aiotagro.cattletrade.business.dto.DeviceDto; +import com.aiotagro.cattletrade.business.entity.JbqServer; +import com.aiotagro.cattletrade.business.vo.ServerClientVo; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + *

+ * 智能主机表 Mapper 接口 + *

+ * + * @author admin + * @since 2024-12-26 + */ +@Mapper +public interface JbqServerMapper extends BaseMapper { + + List serverList(@Param("item") DeviceDto dto); +} diff --git a/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/mapper/MemberDriverMapper.java b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/mapper/MemberDriverMapper.java new file mode 100644 index 0000000..1d3e634 --- /dev/null +++ b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/mapper/MemberDriverMapper.java @@ -0,0 +1,138 @@ +package com.aiotagro.cattletrade.business.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.Select; + +import java.util.List; +import java.util.Map; + +/** + * Member Driver Mapper + * + * @author System + * @date 2025-01-16 + */ +@Mapper +public interface MemberDriverMapper extends BaseMapper> { + + /** + * 查询司机列表(关联member表获取手机号) + */ + @Select("SELECT md.id, md.member_id, md.username, md.car_number, " + + "md.driving_license, md.driver_license, md.record_code, " + + "md.car_img, md.id_card, md.remark, md.create_time, m.mobile, m.status " + + "FROM member_driver md " + + "LEFT JOIN member m ON md.member_id = m.id " + + "ORDER BY md.id DESC") + List> selectDriverList(); + + /** + * 根据用户名搜索司机列表(关联member表获取手机号) + */ + @Select("") + List> selectDriverListByUsername(@Param("username") String username); + + /** + * 根据用户名和手机号搜索司机列表(支持精确查询) + */ + @Select("SELECT md.id, md.member_id, md.username, md.car_number, " + + "md.driving_license, md.driver_license, md.record_code, " + + "md.car_img, md.id_card, md.remark, md.create_time, m.mobile, m.status " + + "FROM member_driver md " + + "LEFT JOIN member m ON md.member_id = m.id " + + "WHERE m.mobile = #{mobile} " + + "ORDER BY md.id DESC") + List> selectDriverListByMobile(@Param("mobile") String mobile); + + /** + * 根据用户名和手机号搜索司机列表(支持精确查询) + */ + @Select("") + List> selectDriverListBySearch(@Param("username") String username, + @Param("mobile") String mobile, + @Param("mobileExact") Boolean mobileExact); + + /** + * 根据司机ID查询司机信息(关联member表获取手机号) + */ + @Select("SELECT md.id, md.member_id, md.username, md.car_number, " + + "md.driving_license, md.driver_license, md.record_code, " + + "md.car_img, md.id_card, md.remark, md.create_time, m.mobile, m.status " + + "FROM member_driver md " + + "LEFT JOIN member m ON md.member_id = m.id " + + "WHERE md.id = #{driverId}") + Map selectDriverById(@Param("driverId") Integer driverId); + + /** + * 根据车牌号查询司机信息 + */ + @Select("SELECT md.id, md.member_id, md.username, md.car_number, " + + "md.driving_license, md.driver_license, md.record_code, " + + "md.car_img, md.id_card, md.remark, md.create_time, m.mobile, m.status " + + "FROM member_driver md " + + "LEFT JOIN member m ON md.member_id = m.id " + + "WHERE md.car_number = #{licensePlate} " + + "ORDER BY md.create_time DESC LIMIT 1") + Map selectDriverByPlate(@Param("licensePlate") String licensePlate); + + /** + * 新增司机信息 + */ + @org.apache.ibatis.annotations.Insert("INSERT INTO member_driver (member_id, username, car_number, " + + "driver_license, driving_license, car_img, record_code, id_card, remark, create_time) " + + "VALUES (#{memberId}, #{username}, #{carNumber}, #{driverLicense}, #{drivingLicense}, " + + "#{carImg}, #{recordCode}, #{idCard}, #{remark}, NOW())") + int insertDriver(@Param("memberId") Integer memberId, @Param("username") String username, + @Param("carNumber") String carNumber, @Param("driverLicense") String driverLicense, + @Param("drivingLicense") String drivingLicense, @Param("carImg") String carImg, + @Param("recordCode") String recordCode, @Param("idCard") String idCard, @Param("remark") String remark); + + /** + * 更新司机信息 + */ + @org.apache.ibatis.annotations.Update("UPDATE member_driver SET username = #{username}, car_number = #{carNumber}, " + + "driver_license = #{driverLicense}, driving_license = #{drivingLicense}, car_img = #{carImg}, " + + "record_code = #{recordCode}, id_card = #{idCard}, remark = #{remark} WHERE id = #{id}") + int updateDriver(@Param("id") Integer id, @Param("username") String username, @Param("carNumber") String carNumber, + @Param("driverLicense") String driverLicense, @Param("drivingLicense") String drivingLicense, + @Param("carImg") String carImg, @Param("recordCode") String recordCode, @Param("idCard") String idCard, @Param("remark") String remark); + + /** + * 根据司机姓名和车牌号查询司机信息(包含车身照片) + */ + @Select("SELECT md.id, md.member_id, md.username, md.car_number, " + + "md.driving_license, md.driver_license, md.record_code, " + + "md.car_img, md.id_card, md.remark, md.create_time, m.mobile " + + "FROM member_driver md " + + "LEFT JOIN member m ON md.member_id = m.id " + + "WHERE md.username = #{driverName} AND md.car_number = #{licensePlate}") + Map selectDriverByNameAndPlate(@Param("driverName") String driverName, + @Param("licensePlate") String licensePlate); +} + diff --git a/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/mapper/MemberMapper.java b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/mapper/MemberMapper.java new file mode 100644 index 0000000..8e3ec7c --- /dev/null +++ b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/mapper/MemberMapper.java @@ -0,0 +1,209 @@ +package com.aiotagro.cattletrade.business.mapper; + +import com.aiotagro.cattletrade.business.entity.Member; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.Select; +import org.apache.ibatis.annotations.Insert; +import org.apache.ibatis.annotations.Options; + +import java.util.List; +import java.util.Map; + +/** + * Member Mapper + * + * @author System + * @date 2025-01-16 + */ +@Mapper +public interface MemberMapper extends BaseMapper { + + /** + * 查询用户列表(关联member_user表获取用户名) + */ + @Select("SELECT m.id, m.mobile, COALESCE(mu.username, m.mobile) as name, m.type " + + "FROM member m " + + "LEFT JOIN member_user mu ON m.id = mu.member_id " + + "WHERE CAST(m.status AS UNSIGNED) = 1 " + + "ORDER BY m.id ASC") + List> selectMemberUserList(); + + /** + * 根据类型查询会员列表(分页,用于装车订单下拉框) + */ + @Select("") + List> selectMemberListByType(@Param("type") Integer type, + @Param("username") String username, + @Param("offset") Integer offset, + @Param("pageSize") Integer pageSize); + + /** + * 根据类型查询会员总数 + */ + @Select("") + Long countMemberListByType(@Param("type") Integer type, @Param("username") String username); + + /** + * 查询用户列表(分页,用于用户管理页面) + * 只查询member_user表中存在的用户 + */ + @Select("") + List> selectUserListForManagement(@Param("username") String username, + @Param("mobile") String mobile, + @Param("type") Integer type, + @Param("usernameExact") Boolean usernameExact, + @Param("mobileExact") Boolean mobileExact, + @Param("offset") Integer offset, + @Param("pageSize") Integer pageSize); + + /** + * 查询用户总数(用于用户管理页面) + * 只计算member_user表中存在的用户 + */ + @Select("") + Long countUserListForManagement(@Param("username") String username, + @Param("mobile") String mobile, + @Param("type") Integer type, + @Param("usernameExact") Boolean usernameExact, + @Param("mobileExact") Boolean mobileExact); + + /** + * 更新用户信息 + * 同时更新member表和member_user表 + */ + @org.apache.ibatis.annotations.Update("") + int updateUserInfo(@Param("id") Integer id, + @Param("mobile") String mobile, + @Param("type") Integer type, + @Param("status") Integer status, + @Param("username") String username, + @Param("cbkAccount") String cbkAccount, + @Param("remark") String remark); + + /** + * 根据手机号查询member记录(用于获取新插入的member ID) + */ + @Select("SELECT id FROM member WHERE mobile = #{mobile} ORDER BY id DESC LIMIT 1") + Integer selectMemberIdByMobile(@Param("mobile") String mobile); + + /** + * 新增用户基础信息(插入member表) + */ + @Insert("INSERT INTO member (mobile, type, status, create_time, create_by) " + + "VALUES (#{mobile}, #{type}, #{status}, NOW(), 1)") + int insertMember(@Param("mobile") String mobile, + @Param("type") Integer type, + @Param("status") Integer status); + + /** + * 新增用户详细信息(插入member_user表) + */ + @Insert("INSERT INTO member_user (member_id, username, cbk_account, remark, create_time) " + + "VALUES (#{memberId}, #{username}, #{cbkAccount}, #{remark}, NOW())") + int insertMemberUser(@Param("memberId") Integer memberId, + @Param("username") String username, + @Param("cbkAccount") String cbkAccount, + @Param("remark") String remark); +} diff --git a/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/mapper/SysMenuMapper.java b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/mapper/SysMenuMapper.java new file mode 100644 index 0000000..880c725 --- /dev/null +++ b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/mapper/SysMenuMapper.java @@ -0,0 +1,45 @@ +package com.aiotagro.cattletrade.business.mapper; + +import com.aiotagro.cattletrade.business.entity.SysMenu; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + *

+ * 系统菜单表 Mapper 接口 + *

+ * + * @author admin + * @since 2024-12-26 + */ +@Mapper +public interface SysMenuMapper extends BaseMapper { + + /** + * 查询用户菜单 + * + * @param userId + * @return + */ + List queryMenusByUserId(@Param("userId") Integer userId); + + /** + * 根据岗位/角色查询菜单 + * + * @param id + * @return + */ + List queryListByRoleId(@Param("roleId") Integer id); + + /** + * 根据角色ID查询菜单列表(包含权限信息) + * + * @param roleId 角色ID + * @return 菜单列表 + */ + List selectMenusByRoleId(@Param("roleId") Integer roleId); + +} diff --git a/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/mapper/SysRoleMapper.java b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/mapper/SysRoleMapper.java new file mode 100644 index 0000000..911d619 --- /dev/null +++ b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/mapper/SysRoleMapper.java @@ -0,0 +1,18 @@ +package com.aiotagro.cattletrade.business.mapper; + +import com.aiotagro.cattletrade.business.entity.SysRole; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Mapper; + +/** + *

+ * 岗位管理表 Mapper 接口 + *

+ * + * @author admin + * @since 2024-12-07 + */ +@Mapper +public interface SysRoleMapper extends BaseMapper { + +} diff --git a/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/mapper/SysRoleMenuMapper.java b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/mapper/SysRoleMenuMapper.java new file mode 100644 index 0000000..2d024a7 --- /dev/null +++ b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/mapper/SysRoleMenuMapper.java @@ -0,0 +1,18 @@ +package com.aiotagro.cattletrade.business.mapper; + +import com.aiotagro.cattletrade.business.entity.SysRoleMenu; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Mapper; + +/** + *

+ * 岗位菜单表 Mapper 接口 + *

+ * + * @author admin + * @since 2024-12-26 + */ +@Mapper +public interface SysRoleMenuMapper extends BaseMapper { + +} diff --git a/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/mapper/SysTenantMapper.java b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/mapper/SysTenantMapper.java new file mode 100644 index 0000000..795cd0e --- /dev/null +++ b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/mapper/SysTenantMapper.java @@ -0,0 +1,16 @@ +package com.aiotagro.cattletrade.business.mapper; + +import com.aiotagro.cattletrade.business.entity.SysTenant; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Mapper; + +/** + * 租户Mapper接口 + * + * @author System + * @date 2025-01-16 + */ +@Mapper +public interface SysTenantMapper extends BaseMapper { +} + diff --git a/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/mapper/SysUserMapper.java b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/mapper/SysUserMapper.java new file mode 100644 index 0000000..b05054b --- /dev/null +++ b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/mapper/SysUserMapper.java @@ -0,0 +1,44 @@ +package com.aiotagro.cattletrade.business.mapper; + +import com.aiotagro.cattletrade.business.dto.SysUserDto; +import com.aiotagro.cattletrade.business.entity.SysUser; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + *

+ * 员工管理表 Mapper 接口 + *

+ * + * @author admin + * @since 2024-12-26 + */ +@Mapper +public interface SysUserMapper extends BaseMapper { + + /** + * 查询用户 + * + * @param dto + * @return + */ + List queryList(@Param("item") SysUserDto dto); + + /** + * 获取用户信息 + * + * @param currentUserId + * @return + */ + SysUser getUserInfoById(@Param("id") Integer currentUserId); + + /** + * 查询所有用户列表(用于权限分配) + * + * @return 用户列表 + */ + List selectUserListForPermission(); +} diff --git a/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/mapper/WarningLogMapper.java b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/mapper/WarningLogMapper.java new file mode 100644 index 0000000..8fe7c03 --- /dev/null +++ b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/mapper/WarningLogMapper.java @@ -0,0 +1,24 @@ +package com.aiotagro.cattletrade.business.mapper; + +import com.aiotagro.cattletrade.business.dto.DeliveryQueryDto; +import com.aiotagro.cattletrade.business.entity.WarningLog; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + *

+ * 预警记录表 Mapper 接口 + *

+ * + * @author admin + * @since 2024-12-26 + */ +@Mapper +public interface WarningLogMapper extends BaseMapper { + + public List getNewWarningLog(@Param("dto") DeliveryQueryDto dto); + +} diff --git a/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/mapper/XqClientMapper.java b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/mapper/XqClientMapper.java new file mode 100644 index 0000000..1106b5d --- /dev/null +++ b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/mapper/XqClientMapper.java @@ -0,0 +1,74 @@ +package com.aiotagro.cattletrade.business.mapper; + +import com.aiotagro.cattletrade.business.entity.XqClient; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.Select; + +import java.util.List; +import java.util.Map; + +/** + * 项圈客户端Mapper接口 + * + * @author System + * @date 2025-01-16 + */ +@Mapper +public interface XqClientMapper extends BaseMapper { + + /** + * 查询项圈列表(带关联信息) + */ + @Select("SELECT xc.id, xc.org_id, xc.uid, xc.sn AS deviceId, xc.sn, xc.state, " + + " xc.longitude, xc.latitude, xc.altitude, xc.gps_state, " + + " xc.nsat, xc.rsrp, xc.battery, xc.temperature, xc.steps, " + + " xc.acc_x, xc.acc_y, xc.acc_z, xc.bandge_status, xc.ver, " + + " xc.time, xc.uptime, xc.y_steps, xc.is_wear, xc.is_temperature, " + + " xc.source_id, xc.loctime, xc.subType, xc.frequency, " + + " xc.vehicle_id, xc.evening_frequency, xc.tenant_id, " + + " COALESCE(dd.delivery_id, 0) AS delivery_id, " + + " COALESCE(d.delivery_number, '未分配') AS delivery_number, " + + " COALESCE(d.license_plate, md.car_number, '未分配') AS license_plate " + + "FROM xq_client xc " + + "LEFT JOIN ( " + + " SELECT device_id, delivery_id, ROW_NUMBER() OVER (PARTITION BY device_id ORDER BY id DESC) AS rn " + + " FROM delivery_device WHERE device_type = 3 " + + ") dd ON CONCAT(xc.sn, '') = dd.device_id AND dd.rn = 1 " + + "LEFT JOIN delivery d ON dd.delivery_id = d.id " + + "LEFT JOIN member_driver md ON xc.vehicle_id = md.id " + + "WHERE (#{sn} IS NULL OR #{sn} = '' OR xc.sn LIKE CONCAT('%', #{sn}, '%')) " + + " AND (#{startNo} IS NULL OR #{startNo} = '' OR xc.sn >= #{startNo}) " + + " AND (#{endNo} IS NULL OR #{endNo} = '' OR xc.sn <= #{endNo}) " + + "ORDER BY xc.id DESC") + List> selectXqClientListWithRelations(@Param("sn") String sn, + @Param("startNo") String startNo, + @Param("endNo") String endNo); + + /** + * 查询未分配的智能项圈列表 + */ + @Select("SELECT xc.id, xc.org_id, xc.uid, xc.sn AS deviceId, xc.sn, xc.state, " + + " xc.longitude, xc.latitude, xc.altitude, xc.gps_state, " + + " xc.nsat, xc.rsrp, xc.battery, xc.temperature, xc.steps, " + + " xc.acc_x, xc.acc_y, xc.acc_z, xc.bandge_status, xc.ver, " + + " xc.time, xc.uptime, xc.y_steps, xc.is_wear, xc.is_temperature, " + + " xc.source_id, xc.loctime, xc.subType, xc.frequency, " + + " xc.vehicle_id, xc.evening_frequency, xc.tenant_id, " + + " 0 AS delivery_id, '未分配' AS delivery_number, '未分配' AS license_plate " + + "FROM xq_client xc " + + "WHERE xc.deviceId NOT IN (" + + " SELECT DISTINCT device_id " + + " FROM delivery_device " + + " WHERE device_type = 3" + + ") " + + "AND (#{sn} IS NULL OR #{sn} = '' OR xc.sn LIKE CONCAT('%', #{sn}, '%')) " + + "AND (#{startNo} IS NULL OR #{startNo} = '' OR xc.sn >= #{startNo}) " + + "AND (#{endNo} IS NULL OR #{endNo} = '' OR xc.sn <= #{endNo}) " + + "ORDER BY xc.id DESC") + List> selectUnassignedXqClientList(@Param("sn") String sn, + @Param("startNo") String startNo, + @Param("endNo") String endNo); +} + diff --git a/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/service/IDeliveryDeviceService.java b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/service/IDeliveryDeviceService.java new file mode 100644 index 0000000..ecd31cf --- /dev/null +++ b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/service/IDeliveryDeviceService.java @@ -0,0 +1,16 @@ +package com.aiotagro.cattletrade.business.service; + +import com.aiotagro.cattletrade.business.entity.DeliveryDevice; +import com.baomidou.mybatisplus.extension.service.IService; + +/** + *

+ * 运单设备表 服务类 + *

+ * + * @author admin + * @since 2024-12-26 + */ +public interface IDeliveryDeviceService extends IService { + +} diff --git a/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/service/IDeliveryService.java b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/service/IDeliveryService.java new file mode 100644 index 0000000..525229e --- /dev/null +++ b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/service/IDeliveryService.java @@ -0,0 +1,51 @@ +package com.aiotagro.cattletrade.business.service; + +import com.aiotagro.cattletrade.business.dto.DeliverListDto; +import com.aiotagro.cattletrade.business.dto.DeliveryAddDto; +import com.aiotagro.cattletrade.business.dto.DeliveryCreateDto; +import com.aiotagro.cattletrade.business.dto.DeliveryEditDto; +import com.aiotagro.cattletrade.business.dto.DeliveryQueryDto; +import com.aiotagro.cattletrade.business.entity.Delivery; +import com.aiotagro.cattletrade.business.vo.DeliveryLogVo; +import com.aiotagro.common.core.web.domain.AjaxResult; +import com.aiotagro.common.core.web.domain.PageResultResponse; +import com.baomidou.mybatisplus.extension.service.IService; + +/** + *

+ * 运送清单表 服务类 + *

+ * + * @author admin + * @since 2024-12-26 + */ +public interface IDeliveryService extends IService { + + AjaxResult addDelivery(DeliveryAddDto dto); + + /** + * 创建运送清单(PC端) + * + * @param dto 运送清单创建DTO + * @return AjaxResult + */ + AjaxResult createDelivery(DeliveryCreateDto dto); + + /** + * 编辑运送清单 + * + * @param dto 运送清单编辑DTO + * @return AjaxResult + */ + AjaxResult updateDeliveryInfo(DeliveryEditDto dto); + + PageResultResponse pageQuery(DeliveryQueryDto dto); + + AjaxResult viewDelivery(Integer id); + + PageResultResponse pageQueryList(DeliverListDto dto); + + AjaxResult detail(Integer id); + + PageResultResponse pageQueryListLog(DeliverListDto dto); +} diff --git a/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/service/IJbqClientLogService.java b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/service/IJbqClientLogService.java new file mode 100644 index 0000000..9d1e76c --- /dev/null +++ b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/service/IJbqClientLogService.java @@ -0,0 +1,19 @@ +package com.aiotagro.cattletrade.business.service; + +import com.aiotagro.cattletrade.business.dto.JbqLogDto; +import com.aiotagro.cattletrade.business.entity.JbqClientLog; +import com.aiotagro.common.core.web.domain.PageResultResponse; +import com.baomidou.mybatisplus.extension.service.IService; + +/** + *

+ * 智能耳标日志表 服务类 + *

+ * + * @author admin + * @since 2024-12-26 + */ +public interface IJbqClientLogService extends IService { + + PageResultResponse jbqLogList(JbqLogDto dto); +} diff --git a/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/service/IJbqClientService.java b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/service/IJbqClientService.java new file mode 100644 index 0000000..88b0def --- /dev/null +++ b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/service/IJbqClientService.java @@ -0,0 +1,29 @@ +package com.aiotagro.cattletrade.business.service; + +import com.aiotagro.cattletrade.business.dto.DeviceDto; +import com.aiotagro.cattletrade.business.entity.JbqClient; +import com.aiotagro.common.core.web.domain.AjaxResult; +import com.aiotagro.common.core.web.domain.PageResultResponse; +import com.baomidou.mybatisplus.extension.service.IService; + +/** + *

+ * 智能耳标表 服务类 + *

+ * + * @author admin + * @since 2024-12-26 + */ +public interface IJbqClientService extends IService { + + PageResultResponse jbqList(DeviceDto dto); + + /** + * 根据运送清单ID查询设备列表 + * + * @param deliveryId 运送清单ID + * @param deviceType 设备类型 + * @return AjaxResult + */ + AjaxResult getDevicesByDeliveryId(Integer deliveryId, Integer deviceType); +} diff --git a/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/service/IJbqServerLogService.java b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/service/IJbqServerLogService.java new file mode 100644 index 0000000..36e9414 --- /dev/null +++ b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/service/IJbqServerLogService.java @@ -0,0 +1,16 @@ +package com.aiotagro.cattletrade.business.service; + +import com.aiotagro.cattletrade.business.entity.JbqServerLog; +import com.baomidou.mybatisplus.extension.service.IService; + +/** + *

+ * 智能主机日志表 服务类 + *

+ * + * @author admin + * @since 2024-12-26 + */ +public interface IJbqServerLogService extends IService { + +} diff --git a/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/service/IJbqServerService.java b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/service/IJbqServerService.java new file mode 100644 index 0000000..22a6bd3 --- /dev/null +++ b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/service/IJbqServerService.java @@ -0,0 +1,25 @@ +package com.aiotagro.cattletrade.business.service; + +import com.aiotagro.cattletrade.business.dto.DeviceDto; +import com.aiotagro.cattletrade.business.dto.ServerLocationDto; +import com.aiotagro.cattletrade.business.entity.JbqServer; +import com.aiotagro.common.core.web.domain.AjaxResult; +import com.aiotagro.common.core.web.domain.PageResultResponse; +import com.baomidou.mybatisplus.extension.service.IService; + +/** + *

+ * 智能主机表 服务类 + *

+ * + * @author admin + * @since 2024-12-26 + */ +public interface IJbqServerService extends IService { + + PageResultResponse serverList(DeviceDto dto); + + AjaxResult serverLocation(ServerLocationDto dto); + + AjaxResult serverTrack(ServerLocationDto dto); +} diff --git a/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/service/ISysRoleService.java b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/service/ISysRoleService.java new file mode 100644 index 0000000..4c03609 --- /dev/null +++ b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/service/ISysRoleService.java @@ -0,0 +1,50 @@ +package com.aiotagro.cattletrade.business.service; + +import com.aiotagro.cattletrade.business.dto.SysRoleDto; +import com.aiotagro.cattletrade.business.entity.SysRole; +import com.aiotagro.common.core.web.domain.AjaxResult; +import com.aiotagro.common.core.web.domain.PageResultResponse; +import com.baomidou.mybatisplus.extension.service.IService; + +/** + *

+ * 岗位管理表 服务类 + *

+ * + * @author admin + * @since 2024-12-26 + */ +public interface ISysRoleService extends IService { + + /** + * 查询分页列表 + * + * @param dto + * @return + */ + PageResultResponse queryList(SysRoleDto dto); + + /** + * 新增、编辑 + * + * @param role + * @return + */ + AjaxResult saveRole(SysRole role); + + /** + * 删除 + * + * @param id + * @return + */ + AjaxResult delete(Integer id); + + /** + * 查询菜单 + * + * @return + */ + AjaxResult getMenus(Integer id); + +} diff --git a/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/service/ISysUserService.java b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/service/ISysUserService.java new file mode 100644 index 0000000..8af0177 --- /dev/null +++ b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/service/ISysUserService.java @@ -0,0 +1,43 @@ +package com.aiotagro.cattletrade.business.service; + +import com.aiotagro.cattletrade.business.dto.SysUserDto; +import com.aiotagro.cattletrade.business.entity.SysUser; +import com.aiotagro.common.core.web.domain.AjaxResult; +import com.aiotagro.common.core.web.domain.PageResultResponse; +import com.baomidou.mybatisplus.extension.service.IService; + +/** + *

+ * 员工管理表 服务类 + *

+ * + * @author admin + * @since 2024-12-26 + */ +public interface ISysUserService extends IService { + + /** + * 查询分页列表 + * + * @param dto + * @return + */ + PageResultResponse queryList(SysUserDto dto); + + /** + * 新增、编辑 + * + * @param user + * @return + */ + AjaxResult saveUser(SysUser user); + + /** + * 删除 + * + * @param id + * @return + */ + AjaxResult delete(Integer id); + +} diff --git a/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/service/IWarningLogService.java b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/service/IWarningLogService.java new file mode 100644 index 0000000..9754723 --- /dev/null +++ b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/service/IWarningLogService.java @@ -0,0 +1,40 @@ +package com.aiotagro.cattletrade.business.service; + +import com.aiotagro.cattletrade.business.dto.DeliveryQueryDto; +import com.aiotagro.cattletrade.business.dto.DeliverListDto; +import com.aiotagro.cattletrade.business.entity.WarningLog; +import com.aiotagro.common.core.web.domain.AjaxResult; +import com.aiotagro.cattletrade.business.vo.DeliveryLogVo; +import com.aiotagro.common.core.web.domain.PageResultResponse; +import com.baomidou.mybatisplus.extension.service.IService; + +/** + *

+ * 预警记录表 服务类 + *

+ * + * @author admin + * @since 2024-12-26 + */ +public interface IWarningLogService extends IService { + + /** + * 供远程调用-保存预警日志 + * + * @param deviceId + * @return + */ + AjaxResult savaWarn(String deviceId); + + AjaxResult warningCount(); + + PageResultResponse queryList(DeliveryQueryDto dto); + + /** + * 预警详情查看 + * @param id + * @return + */ + AjaxResult warningDetail(Integer id); + +} diff --git a/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/service/IXqClientService.java b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/service/IXqClientService.java new file mode 100644 index 0000000..a5dca45 --- /dev/null +++ b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/service/IXqClientService.java @@ -0,0 +1,43 @@ +package com.aiotagro.cattletrade.business.service; + +import com.aiotagro.cattletrade.business.entity.XqClient; +import com.aiotagro.common.core.web.domain.AjaxResult; +import com.aiotagro.common.core.web.domain.PageResultResponse; +import com.baomidou.mybatisplus.extension.service.IService; + +import java.util.Map; + +/** + * 项圈客户端服务接口 + * + * @author System + * @date 2025-01-16 + */ +public interface IXqClientService extends IService { + + /** + * 分页查询项圈列表 + */ + PageResultResponse xqList(Map params); + + /** + * 根据运送清单ID查询设备列表 + */ + AjaxResult getDevicesByDeliveryId(Integer deliveryId, Integer deviceType); + + /** + * 查询项圈位置信息 + */ + AjaxResult getCollarLocation(String deliveryId, String xqDeviceId); + + /** + * 查询项圈轨迹 + */ + AjaxResult getCollarTrack(String deliveryId, String xqDeviceId, String trackTime, String trackEndTime); + + /** + * 查询设备轨迹 + */ + AjaxResult getCollarDeviceTrack(String deliveryId, String xqDeviceId, String trackTime, String trackEndTime); +} + diff --git a/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/service/LoginService.java b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/service/LoginService.java new file mode 100644 index 0000000..6563d4c --- /dev/null +++ b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/service/LoginService.java @@ -0,0 +1,37 @@ +package com.aiotagro.cattletrade.business.service; + +import com.aiotagro.cattletrade.business.dto.LoginDto; +import com.aiotagro.common.core.web.domain.AjaxResult; + +public interface LoginService { + + /** + * 发送验证码 + * + * @param mobile + * @return + */ + AjaxResult sendLoginSmsCode(String mobile); + + /** + * 用户登录 + * + * @param dto + * @return + */ + AjaxResult login(LoginDto dto); + + /** + * 查询菜单 + * + * @return + */ + AjaxResult getUserMenus(); + + /** + * rfid获取用户信息 + * + * @return + */ + AjaxResult getUserInfo(); +} diff --git a/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/service/TencentSmsCodeService.java b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/service/TencentSmsCodeService.java new file mode 100644 index 0000000..fc44de1 --- /dev/null +++ b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/service/TencentSmsCodeService.java @@ -0,0 +1,6 @@ +package com.aiotagro.cattletrade.business.service; + +public interface TencentSmsCodeService { + + String sendSmsCode(String mobile, String code); +} diff --git a/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/service/impl/DeliveryDeviceServiceImpl.java b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/service/impl/DeliveryDeviceServiceImpl.java new file mode 100644 index 0000000..634e7f6 --- /dev/null +++ b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/service/impl/DeliveryDeviceServiceImpl.java @@ -0,0 +1,20 @@ +package com.aiotagro.cattletrade.business.service.impl; + +import com.aiotagro.cattletrade.business.entity.DeliveryDevice; +import com.aiotagro.cattletrade.business.mapper.DeliveryDeviceMapper; +import com.aiotagro.cattletrade.business.service.IDeliveryDeviceService; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import org.springframework.stereotype.Service; + +/** + *

+ * 运单设备表 服务实现类 + *

+ * + * @author admin + * @since 2024-12-26 + */ +@Service +public class DeliveryDeviceServiceImpl extends ServiceImpl implements IDeliveryDeviceService { + +} diff --git a/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/service/impl/DeliveryServiceImpl.java b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/service/impl/DeliveryServiceImpl.java new file mode 100644 index 0000000..945c2d5 --- /dev/null +++ b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/service/impl/DeliveryServiceImpl.java @@ -0,0 +1,1284 @@ +package com.aiotagro.cattletrade.business.service.impl; + +import com.aiotagro.cattletrade.business.dto.DeliverListDto; +import com.aiotagro.cattletrade.business.dto.DeliveryAddDto; +import com.aiotagro.cattletrade.business.dto.DeliveryCreateDto; +import com.aiotagro.cattletrade.business.dto.DeliveryEditDto; +import com.aiotagro.cattletrade.business.dto.DeliveryQueryDto; +import com.aiotagro.cattletrade.business.entity.*; +import com.aiotagro.cattletrade.business.mapper.*; +import com.aiotagro.cattletrade.business.service.IDeliveryService; +import com.aiotagro.cattletrade.business.service.IDeliveryDeviceService; +import com.aiotagro.cattletrade.business.service.IXqClientService; +import com.aiotagro.cattletrade.business.vo.DeliveryLogVo; +import com.aiotagro.cattletrade.constant.WarningStatusAdminEnum; +import com.aiotagro.common.core.utils.DateUtils; +import com.aiotagro.common.core.utils.EnumUtil; +import com.aiotagro.common.core.utils.SecurityUtil; +import com.aiotagro.common.core.utils.StringUtils; +import com.aiotagro.common.core.constant.RoleConstants; +import com.aiotagro.common.core.web.domain.AjaxResult; +import com.aiotagro.common.core.web.domain.PageResultResponse; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.github.pagehelper.Page; +import com.github.pagehelper.PageHelper; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.logging.log4j.util.Strings; +import org.springframework.beans.BeanUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.*; +import java.util.stream.Collectors; + +/** + * + * 运送清单表 服务实现类 + * + * + * @author admin + * @since 2024-12-26 + */ +@Service +public class DeliveryServiceImpl extends ServiceImpl implements IDeliveryService { + + @Autowired + private DeliveryDeviceMapper deliveryDeviceMapper; + @Autowired + private WarningLogMapper warningLogMapper; + @Autowired + private SysUserMapper sysUserMapper; + @Autowired + private JbqClientMapper jbqClientMapper; + @Autowired + private JbqServerMapper jbqServerMapper; + @Autowired + private MemberMapper memberMapper; + @Autowired + private MemberDriverMapper memberDriverMapper; + @Autowired + private IDeliveryDeviceService deliveryDeviceService; + @Autowired + private IXqClientService xqClientService; + + + /** + * 列表查询 + * @param dto + * @return + */ + @Override + public PageResultResponse pageQuery(DeliveryQueryDto dto) { + //获取当前登录人id和手机号 + Integer userId = SecurityUtil.getCurrentUserId(); + String currentUserMobile = SecurityUtil.getUserMobile(); + System.out.println("=== 运送清单列表查询 - 当前登录用户手机号: " + currentUserMobile); + + Page result = PageHelper.startPage(dto.getPageNum(), dto.getPageSize()); + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + + // 运输单号模糊查询 + if(StringUtils.isNotEmpty(dto.getDeliveryNumber())){ + wrapper.like(Delivery::getDeliveryNumber, dto.getDeliveryNumber()); + } + + wrapper.orderByDesc(Delivery::getId); + List list = this.list(wrapper); + + if(CollectionUtils.isNotEmpty(list)){ + list.forEach(delivery -> { + if(userId.equals(delivery.getCheckBy())){ + //判断是否需要核验,1:需要核验,2:不需要核验 + delivery.setIfCheck(1); + } + + // 查询四个角色的手机号(供应商、资金方、采购商、司机) + try { + // 1. 查询供应商手机号(supplierId是逗号分隔的字符串) + if (StringUtils.isNotEmpty(delivery.getSupplierId())) { + String[] supplierIds = delivery.getSupplierId().split(","); + List supplierMobiles = new ArrayList<>(); + for (String supplierId : supplierIds) { + if (StringUtils.isNotEmpty(supplierId.trim())) { + try { + Integer sid = Integer.parseInt(supplierId.trim()); + Member supplier = memberMapper.selectById(sid); + if (supplier != null && StringUtils.isNotEmpty(supplier.getMobile())) { + supplierMobiles.add(supplier.getMobile()); + } + } catch (NumberFormatException e) { + System.out.println("供应商ID格式错误: " + supplierId); + } + } + } + if (!supplierMobiles.isEmpty()) { + delivery.setSupplierMobile(String.join(",", supplierMobiles)); + } + } + + // 2. 查询资金方手机号 + if (delivery.getFundId() != null) { + Member fund = memberMapper.selectById(delivery.getFundId()); + if (fund != null && StringUtils.isNotEmpty(fund.getMobile())) { + delivery.setFundMobile(fund.getMobile()); + } + } + + // 3. 查询采购商手机号 + if (delivery.getBuyerId() != null) { + Member buyer = memberMapper.selectById(delivery.getBuyerId()); + if (buyer != null && StringUtils.isNotEmpty(buyer.getMobile())) { + delivery.setBuyerMobile(buyer.getMobile()); + } + } + + // 4. 查询司机手机号(如果有司机ID) + if (delivery.getDriverId() != null) { + try { + Map driverInfo = memberDriverMapper.selectDriverById(delivery.getDriverId()); + if (driverInfo != null) { + String driverName = (String) driverInfo.get("username"); + String driverMobile = (String) driverInfo.get("mobile"); + String carImg = (String) driverInfo.get("car_img"); + + if (StringUtils.isNotEmpty(driverMobile)) { + delivery.setDriverMobile(driverMobile); + } + if (StringUtils.isNotEmpty(driverName)) { + delivery.setDriverName(driverName); + } + + // 处理车身照片分割 + if (carImg != null && !carImg.isEmpty()) { + // 按逗号分割car_img字段,分别映射到车头和车尾照片 + String[] carImgUrls = carImg.split(","); + + if (carImgUrls.length >= 2) { + // 逗号前面的URL作为车尾照片 + String carBehindPhoto = carImgUrls[0].trim(); + // 逗号后面的URL作为车头照片 + String carFrontPhoto = carImgUrls[1].trim(); + + delivery.setCarBehindPhoto(carBehindPhoto); + delivery.setCarFrontPhoto(carFrontPhoto); + + System.out.println("运送清单-分割车身照片:"); + System.out.println(" 车尾照片: " + carBehindPhoto); + System.out.println(" 车头照片: " + carFrontPhoto); + } else if (carImgUrls.length == 1) { + // 只有一个URL时,同时设置为车头和车尾照片 + String singlePhoto = carImgUrls[0].trim(); + delivery.setCarFrontPhoto(singlePhoto); + delivery.setCarBehindPhoto(singlePhoto); + + System.out.println("运送清单-单张车身照片: " + singlePhoto); + } else { + // 没有有效URL + delivery.setCarFrontPhoto(""); + delivery.setCarBehindPhoto(""); + System.out.println("运送清单-无有效车身照片URL"); + } + } else { + // 如果司机信息中没有照片,清空delivery表中的照片 + delivery.setCarFrontPhoto(""); + delivery.setCarBehindPhoto(""); + System.out.println("运送清单-司机信息中无照片,清空车身照片"); + } + } + } catch (Exception e) { + System.out.println("查询司机信息异常: " + e.getMessage()); + } + } + } catch (Exception e) { + System.out.println("查询角色手机号异常: " + e.getMessage()); + e.printStackTrace(); + } + + // 统计登记设备数量(耳标+项圈) + Integer currentDeliveryId = delivery.getId(); + if (currentDeliveryId != null) { + try { + // 统计耳标设备数量 + LambdaQueryWrapper earTagWrapper = new LambdaQueryWrapper<>(); + earTagWrapper.eq(DeliveryDevice::getDeliveryId, currentDeliveryId) + .eq(DeliveryDevice::getDeviceType, 2); + long earTagCount = deliveryDeviceService.count(earTagWrapper); + + // 统计项圈设备数量 + LambdaQueryWrapper collarWrapper = new LambdaQueryWrapper<>(); + collarWrapper.eq(DeliveryDevice::getDeliveryId, currentDeliveryId) + .eq(DeliveryDevice::getDeviceType, 3); + long collarCount = deliveryDeviceService.count(collarWrapper); + + // 设置总设备数量和耳标数量 + int totalDeviceCount = (int) (earTagCount + collarCount); + delivery.setRegisteredJbqCount(totalDeviceCount); + delivery.setEarTagCount((int) earTagCount); + + // 设置已分配设备数量,与登记设备数量保持一致 + delivery.setBindJbqCount(totalDeviceCount); + + // 统计已佩戴设备数量(bandge_status = 1) + int wornDeviceCount = 0; + try { + // 统计已佩戴的耳标设备数量(通过delivery_device表关联) + LambdaQueryWrapper wornEarTagWrapper = new LambdaQueryWrapper<>(); + wornEarTagWrapper.eq(DeliveryDevice::getDeliveryId, currentDeliveryId) + .eq(DeliveryDevice::getDeviceType, 2) + .eq(DeliveryDevice::getIsWare, 1); // 1表示已佩戴 + long wornEarTagCount = deliveryDeviceService.count(wornEarTagWrapper); + + // 统计已佩戴的项圈设备数量(通过delivery_device表关联xq_client表) + LambdaQueryWrapper wornCollarWrapper = new LambdaQueryWrapper<>(); + wornCollarWrapper.eq(DeliveryDevice::getDeliveryId, currentDeliveryId) + .eq(DeliveryDevice::getDeviceType, 3); + List collarDevices = deliveryDeviceService.list(wornCollarWrapper); + + int wornCollarCount = 0; + for (DeliveryDevice device : collarDevices) { + // 查询xq_client表中的bandge_status + LambdaQueryWrapper xqWrapper = new LambdaQueryWrapper<>(); + xqWrapper.eq(XqClient::getSn, device.getDeviceId()); + XqClient xqClient = xqClientService.getOne(xqWrapper); + if (xqClient != null && xqClient.getBandgeStatus() != null && xqClient.getBandgeStatus() == 1) { + wornCollarCount++; + } + } + + wornDeviceCount = (int) (wornEarTagCount + wornCollarCount); + delivery.setWareCount(wornDeviceCount); + + System.out.println("运送清单-运单ID " + currentDeliveryId + " 已佩戴设备统计: 耳标=" + wornEarTagCount + ", 项圈=" + wornCollarCount + ", 总计=" + wornDeviceCount); + } catch (Exception e) { + System.out.println("运送清单-统计已佩戴设备数量失败: " + e.getMessage()); + delivery.setWareCount(0); + } + + System.out.println("运送清单-运单ID " + currentDeliveryId + " 设备统计: 耳标=" + earTagCount + ", 项圈=" + collarCount + ", 总计=" + totalDeviceCount + ", 已佩戴=" + wornDeviceCount); + } catch (Exception e) { + System.out.println("运送清单-统计设备数量失败: " + e.getMessage()); + delivery.setRegisteredJbqCount(0); + delivery.setBindJbqCount(0); + delivery.setWareCount(0); + } + } + }); + + // 数据权限过滤:非超级管理员只能看到与自己手机号相关的订单 + if (!SecurityUtil.isSuperAdmin() && StringUtils.isNotEmpty(currentUserMobile)) { + System.out.println("=== 非超级管理员,执行数据权限过滤 ==="); + System.out.println("当前用户手机号: " + currentUserMobile); + + list = list.stream().filter(delivery -> { + boolean hasPermission = false; + + // 检查是否是司机 + if (StringUtils.isNotEmpty(delivery.getDriverMobile()) && + currentUserMobile.equals(delivery.getDriverMobile())) { + hasPermission = true; + System.out.println("运单 " + delivery.getDeliveryNumber() + " - 匹配司机手机号"); + } + + // 检查是否是供应商(可能有多个供应商) + if (!hasPermission && StringUtils.isNotEmpty(delivery.getSupplierMobile())) { + String[] supplierMobiles = delivery.getSupplierMobile().split(","); + for (String mobile : supplierMobiles) { + if (currentUserMobile.equals(mobile.trim())) { + hasPermission = true; + System.out.println("运单 " + delivery.getDeliveryNumber() + " - 匹配供应商手机号"); + break; + } + } + } + + // 检查是否是资金方 + if (!hasPermission && StringUtils.isNotEmpty(delivery.getFundMobile()) && + currentUserMobile.equals(delivery.getFundMobile())) { + hasPermission = true; + System.out.println("运单 " + delivery.getDeliveryNumber() + " - 匹配资金方手机号"); + } + + // 检查是否是采购商 + if (!hasPermission && StringUtils.isNotEmpty(delivery.getBuyerMobile()) && + currentUserMobile.equals(delivery.getBuyerMobile())) { + hasPermission = true; + System.out.println("运单 " + delivery.getDeliveryNumber() + " - 匹配采购商手机号"); + } + + if (!hasPermission) { + System.out.println("运单 " + delivery.getDeliveryNumber() + " - 无权限,过滤掉"); + } + + return hasPermission; + }).collect(Collectors.toList()); + + System.out.println("过滤后的运单数量: " + list.size()); + } else if (SecurityUtil.isSuperAdmin()) { + System.out.println("=== 超级管理员,不执行数据权限过滤 ==="); + } else { + System.out.println("=== 非超级管理员,但未获取到当前用户手机号,跳过数据过滤 ==="); + } + } + + // 更新分页信息 + long filteredTotal = list.size(); + return new PageResultResponse(filteredTotal, list); + } + + /** + * 我要装车 + * @param dto + * @return + */ + @Transactional + @Override + public AjaxResult addDelivery(DeliveryAddDto dto) { + //获取当前登录信息 + Integer userId = SecurityUtil.getCurrentUserId(); + LocalDateTime now = LocalDateTime.now(); + String deliverNo = now.format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss")); + Delivery delivery = new Delivery(); + BeanUtils.copyProperties(dto, delivery); + delivery.setStatus(1); + delivery.setDeliveryNumber(deliverNo); + delivery.setCreatedBy(userId); + delivery.setCreateTime(new Date()); + List jbqDeviceSn = dto.getJbqDeviceSn(); + if(CollectionUtils.isNotEmpty(jbqDeviceSn)){ + //校验设备是否已注册 + List deliveryDevices = jbqClientMapper.selectList(new LambdaQueryWrapper().in(JbqClient::getDeviceId, jbqDeviceSn)); + if(CollectionUtils.isNotEmpty(deliveryDevices)){ + List collect = deliveryDevices.stream().map(JbqClient::getDeviceId).collect(Collectors.toList()); + jbqDeviceSn = collect; + }else{ + return AjaxResult.error("设备数据有误!"); + } + delivery.setRegisteredJbqCount(jbqDeviceSn.size()); + }else{ + delivery.setRegisteredJbqCount(0); + } + boolean save = this.save(delivery); + if(save){ + Integer deliveryId = delivery.getId(); + //新增设备 + if(CollectionUtils.isNotEmpty(jbqDeviceSn)){ + jbqDeviceSn.forEach(deviceSn -> { + DeliveryDevice deliveryDevice = new DeliveryDevice(); + deliveryDevice.setDeliveryId(deliveryId); + deliveryDevice.setDeviceType(2); + deliveryDevice.setDeviceId(deviceSn); + deliveryDeviceMapper.insert(deliveryDevice); + }); + } + //校验主机是否在系统中存在 + Long serverCount = jbqServerMapper.selectCount(new LambdaQueryWrapper().eq(JbqServer::getDeviceId, dto.getServerDeviceSn())); + if(serverCount.intValue() == 1){ + DeliveryDevice deliveryDevice = new DeliveryDevice(); + deliveryDevice.setDeliveryId(deliveryId); + deliveryDevice.setDeviceType(1); + deliveryDevice.setDeviceId(dto.getServerDeviceSn()); + deliveryDeviceMapper.insert(deliveryDevice); + }else{ + throw new IllegalArgumentException("主机未注册!"); + } + } + return AjaxResult.success("装车成功!"); + } + + /** + * 创建运送清单(PC端) + * + * @param dto 运送清单创建DTO + * @return AjaxResult + */ + @Transactional + @Override + public AjaxResult createDelivery(DeliveryCreateDto dto) { + // 校验时间逻辑 + if (dto.getEstimatedArrivalTime().before(dto.getEstimatedDepartureTime())) { + return AjaxResult.error("预计到达时间必须晚于出发时间"); + } + + // 获取当前登录用户 + Integer userId = SecurityUtil.getCurrentUserId(); + String userName = SecurityUtil.getUserName(); + + // 生成运输单号 + LocalDateTime now = LocalDateTime.now(); + String deliveryNumber = "YS" + now.format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss")); + + // 创建运送清单 + Delivery delivery = new Delivery(); + delivery.setDeliveryNumber(deliveryNumber); + delivery.setStartLocation(dto.getStartLocation()); + delivery.setEndLocation(dto.getEndLocation()); + delivery.setStatus(1); // 待装车 + delivery.setCreatedBy(userId); + delivery.setCreateByName(userName); + delivery.setCreateTime(new Date()); + + // 保存运送清单 + boolean saved = this.save(delivery); + if (!saved) { + return AjaxResult.error("创建失败"); + } + + // 如果选择了设备,保存设备关联 + Integer deliveryId = delivery.getId(); + + // 保存主机设备 + if (dto.getServerId() != null) { + Long serverCount = jbqServerMapper.selectCount( + new LambdaQueryWrapper().eq(JbqServer::getId, dto.getServerId()) + ); + if (serverCount > 0) { + DeliveryDevice serverDevice = new DeliveryDevice(); + serverDevice.setDeliveryId(deliveryId); + serverDevice.setDeviceType(1); + serverDevice.setDeviceId(dto.getServerId().toString()); + deliveryDeviceMapper.insert(serverDevice); + } + } + + // 保存耳标设备 + if (CollectionUtils.isNotEmpty(dto.getEartagIds())) { + for (Integer eartagId : dto.getEartagIds()) { + DeliveryDevice eartagDevice = new DeliveryDevice(); + eartagDevice.setDeliveryId(deliveryId); + eartagDevice.setDeviceType(2); + eartagDevice.setDeviceId(eartagId.toString()); + deliveryDeviceMapper.insert(eartagDevice); + } + } + + // 保存项圈设备 + if (CollectionUtils.isNotEmpty(dto.getCollarIds())) { + for (Integer collarId : dto.getCollarIds()) { + DeliveryDevice collarDevice = new DeliveryDevice(); + collarDevice.setDeliveryId(deliveryId); + collarDevice.setDeviceType(3); + collarDevice.setDeviceId(collarId.toString()); + deliveryDeviceMapper.insert(collarDevice); + } + } + + return AjaxResult.success("创建成功", delivery); + } + + @Override + public AjaxResult viewDelivery(Integer id) { + //获取当前登录人id + Integer userId = SecurityUtil.getCurrentUserId(); + Delivery delivery = this.getById(id); + if(delivery != null){ + List deliveryDevices = deliveryDeviceMapper.selectList(new LambdaQueryWrapper().eq(DeliveryDevice::getDeliveryId, id)); + //耳标设备集合 + List deviceIds = new ArrayList<>(); + if(CollectionUtils.isNotEmpty(deliveryDevices)){ + deliveryDevices.forEach(deliveryDevice -> { + if(deliveryDevice.getDeviceType() == 1){ + delivery.setServerDeviceId(deliveryDevice.getDeviceId()); + }else if(deliveryDevice.getDeviceType() == 2){ + deviceIds.add(deliveryDevice.getDeviceId()); + } + }); + } + delivery.setDeviceIds(deviceIds); + if(userId == delivery.getCheckBy()){ + //判断是否需要核验,1:需要核验,2:不需要核验 + delivery.setIfCheck(1); + } + } + return AjaxResult.success(delivery); + } + + /** + * 编辑运送清单 + * + * @param dto 运送清单编辑DTO + * @return AjaxResult + */ + @Transactional + @Override + public AjaxResult updateDeliveryInfo(DeliveryEditDto dto) { + // 获取当前登录用户 + Integer userId = SecurityUtil.getCurrentUserId(); + + // 查询运送清单是否存在 + Delivery delivery = this.getById(dto.getDeliveryId()); + if (delivery == null) { + return AjaxResult.error("运送清单不存在"); + } + + // 检查权限:只有创建者或超级管理员可以编辑 + if (!SecurityUtil.isSuperAdmin() && !userId.equals(delivery.getCreatedBy())) { + return AjaxResult.error("无权限编辑此运送清单"); + } + + // 更新运送清单信息 + if (dto.getDeliveryTitle() != null) { + delivery.setDeliveryTitle(dto.getDeliveryTitle()); + } + if (dto.getRatedQuantity() != null) { + delivery.setRatedQuantity(dto.getRatedQuantity()); + } + if (dto.getStartLocation() != null) { + delivery.setStartLocation(dto.getStartLocation()); + } + if (dto.getStartLat() != null) { + delivery.setStartLat(dto.getStartLat()); + } + if (dto.getStartLon() != null) { + delivery.setStartLon(dto.getStartLon()); + } + if (dto.getEndLocation() != null) { + delivery.setEndLocation(dto.getEndLocation()); + } + if (dto.getEndLat() != null) { + delivery.setEndLat(dto.getEndLat()); + } + if (dto.getEndLon() != null) { + delivery.setEndLon(dto.getEndLon()); + } + + // 更新其他业务字段 + if (dto.getSupplierId() != null) { + delivery.setSupplierId(dto.getSupplierId()); + } + if (dto.getFundId() != null) { + delivery.setFundId(dto.getFundId()); + } + if (dto.getDriverId() != null) { + delivery.setDriverId(dto.getDriverId()); + } + if (dto.getDriverMobile() != null) { + delivery.setDriverMobile(dto.getDriverMobile()); + } + if (dto.getBuyerId() != null) { + delivery.setBuyerId(dto.getBuyerId()); + } + if (dto.getBuyerPrice() != null) { + delivery.setBuyerPrice(dto.getBuyerPrice()); + } + if (dto.getSalePrice() != null) { + delivery.setSalePrice(dto.getSalePrice()); + } + if (dto.getFirmPrice() != null) { + delivery.setFirmPrice(dto.getFirmPrice()); + } + + // 保存更新 + boolean updated = this.updateById(delivery); + if (updated) { + return AjaxResult.success("编辑成功"); + } else { + return AjaxResult.error("编辑失败"); + } + } + + + /** + * 转换前端时间格式为标准格式 + * 将 "Oct 01, 2025" 转换为 "2025-10-01" + */ + private String convertDateFormat(String dateStr) { + if (StringUtils.isEmpty(dateStr)) { + return null; + } + + try { + // 处理 "Oct 01, 2025" 格式 + if (dateStr.contains(",")) { + String[] parts = dateStr.split(","); + if (parts.length == 2) { + String monthDay = parts[0].trim(); // "Oct 01" + String year = parts[1].trim(); // "2025" + + String[] monthDayParts = monthDay.split(" "); + if (monthDayParts.length == 2) { + String month = monthDayParts[0]; // "Oct" + String day = monthDayParts[1]; // "01" + + // 月份名称映射 + String[] monthNames = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; + int monthNum = 1; + for (int i = 0; i < monthNames.length; i++) { + if (monthNames[i].equals(month)) { + monthNum = i + 1; + break; + } + } + + return year + "-" + String.format("%02d", monthNum) + "-" + day; + } + } + } + + // 如果已经是标准格式,直接返回 + return dateStr; + } catch (Exception e) { + System.out.println("时间格式转换异常: " + e.getMessage()); + return dateStr; // 转换失败时返回原字符串 + } + } + + @Override + public PageResultResponse pageQueryListLog(DeliverListDto dto) { + //获取当前登录人的信息 + String currentUserMobile = SecurityUtil.getUserMobile(); + System.out.println("=== 装车订单列表查询 - 当前登录用户手机号: " + currentUserMobile); + + // 调试:打印接收到的所有参数 + System.out.println("=== 后端接收到的搜索参数 ==="); + System.out.println("deliveryNumber: " + dto.getDeliveryNumber()); + System.out.println("licensePlate: " + dto.getLicensePlate()); + System.out.println("deliveryTitle: " + dto.getDeliveryTitle()); + System.out.println("endLocation: " + dto.getEndLocation()); + System.out.println("status: " + dto.getStatus()); + System.out.println("startTime: " + dto.getStartTime()); + System.out.println("endTime: " + dto.getEndTime()); + + // 调试:查询数据库中的所有车牌号 + if(StringUtils.isNotEmpty(dto.getLicensePlate())) { + System.out.println("=== 查询数据库中的车牌号 ==="); + List allDeliveries = this.list(); + System.out.println("数据库中的车牌号列表:"); + allDeliveries.forEach(delivery -> { + System.out.println("ID: " + delivery.getId() + ", 车牌号: " + delivery.getLicensePlate()); + }); + } + + Page result = PageHelper.startPage(dto.getPageNum(), dto.getPageSize()); + LambdaQueryWrapper deliveryLambdaQueryWrapper = new LambdaQueryWrapper<>(); + + // 运单号精确匹配 + if(StringUtils.isNotEmpty(dto.getDeliveryNumber())){ + System.out.println("=== 添加运单号搜索条件(精确匹配): " + dto.getDeliveryNumber() + " ==="); + deliveryLambdaQueryWrapper.eq(Delivery::getDeliveryNumber, dto.getDeliveryNumber()); + } + // 车牌号搜索:暂时不在后端过滤,改为前端精确搜索 + if(StringUtils.isNotEmpty(dto.getLicensePlate())){ + System.out.println("=== 车牌号搜索参数: " + dto.getLicensePlate() + "(前端精确搜索)==="); + // 暂时不添加后端搜索条件,让前端进行精确搜索 + } + // 创建时间范围查询 + if(StringUtils.isNotEmpty(dto.getStartTime())){ + System.out.println("=== 添加创建时间开始条件 ==="); + System.out.println("原始startTime参数: " + dto.getStartTime()); + try { + // 将前端发送的时间格式转换为标准格式 + String startTimeStr = convertDateFormat(dto.getStartTime()) + " 00:00:00"; + System.out.println("转换后的startTime: " + startTimeStr); + Date startDate = DateUtils.parseDate(startTimeStr); + System.out.println("解析后的startDate: " + startDate); + deliveryLambdaQueryWrapper.ge(Delivery::getCreateTime, startDate); + } catch (Exception e) { + System.out.println("时间解析异常: " + e.getMessage()); + e.printStackTrace(); + } + } + if(StringUtils.isNotEmpty(dto.getEndTime())){ + System.out.println("=== 添加创建时间结束条件 ==="); + System.out.println("原始endTime参数: " + dto.getEndTime()); + try { + // 将前端发送的时间格式转换为标准格式 + String endTimeStr = convertDateFormat(dto.getEndTime()) + " 23:59:59"; + System.out.println("转换后的endTime: " + endTimeStr); + Date endDate = DateUtils.parseDate(endTimeStr); + System.out.println("解析后的endDate: " + endDate); + deliveryLambdaQueryWrapper.le(Delivery::getCreateTime, endDate); + } catch (Exception e) { + System.out.println("时间解析异常: " + e.getMessage()); + e.printStackTrace(); + } + } + // 核验状态精确匹配 + if(null != dto.getStatus()){ + System.out.println("=== 添加核验状态搜索条件(精确匹配): " + dto.getStatus() + " ==="); + deliveryLambdaQueryWrapper.eq(Delivery::getStatus, dto.getStatus()); + } + // 添加订单标题搜索条件(精确匹配) + if(StringUtils.isNotEmpty(dto.getDeliveryTitle())){ + System.out.println("=== 添加订单标题搜索条件(精确匹配): " + dto.getDeliveryTitle() + " ==="); + deliveryLambdaQueryWrapper.eq(Delivery::getDeliveryTitle, dto.getDeliveryTitle()); + } + // 添加目的地搜索条件(精确匹配) + if(StringUtils.isNotEmpty(dto.getEndLocation())){ + System.out.println("=== 添加目的地搜索条件(精确匹配): " + dto.getEndLocation() + " ==="); + deliveryLambdaQueryWrapper.eq(Delivery::getEndLocation, dto.getEndLocation()); + } + deliveryLambdaQueryWrapper.orderByDesc(Delivery::getCreateTime); + List resList = list(deliveryLambdaQueryWrapper); + System.out.println("=== 数据库查询结果 ==="); + System.out.println("查询到的记录数: " + (resList != null ? resList.size() : 0)); + if(CollectionUtils.isNotEmpty(resList)){ + System.out.println("第一条记录的车牌号: " + resList.get(0).getLicensePlate()); + System.out.println("第一条记录的运单号: " + resList.get(0).getDeliveryNumber()); + //获取当前的运单Id + List deliveryIds = resList.stream().map(delivery -> delivery.getId()).collect(Collectors.toList()); + //根据当前运单查询最新的一次数量预警记录 + List warningCountLogs = warningLogMapper.selectList(new LambdaQueryWrapper().in(WarningLog::getDeliveryId, deliveryIds).last(" and id in(select max(id) from warning_log where warning_type in ('0','2') group by delivery_id)")); + List warningRangeLogs = warningLogMapper.selectList(new LambdaQueryWrapper().in(WarningLog::getDeliveryId, deliveryIds).last(" and id in(select max(id) from warning_log where warning_type in ('1','3') group by delivery_id)")); + //获取当前登录人的Id + List createByIds = resList.stream().map(delivery -> delivery.getCreatedBy()).collect(Collectors.toList()); + List sysUsers = sysUserMapper.selectBatchIds(createByIds); + Map userMap = sysUsers.stream().collect(Collectors.toMap(SysUser::getId, SysUser::getName)); + resList.forEach(deliveryLogVo -> { + Integer deliveryId = deliveryLogVo.getId(); + List warningTypes = new ArrayList<>(); + //数量预警解析 + warningCountLogs.forEach(warningLog -> { + Integer deliveryLogId = warningLog.getDeliveryId(); + String warningType = warningLog.getWarningType(); + if(deliveryId == deliveryLogId){ + warningTypes.add(EnumUtil.getEnumConstant(WarningStatusAdminEnum.class , Integer.parseInt(warningType)).getDescription()); + deliveryLogVo.setInventoryJbqCount(warningLog.getInventoryJbqCount()); + } + }); + //距离预警解析 + warningRangeLogs.forEach(warningLog -> { + Integer deliveryLogId = warningLog.getDeliveryId(); + String warningType = warningLog.getWarningType(); + if(deliveryId == deliveryLogId){ + warningTypes.add(EnumUtil.getEnumConstant(WarningStatusAdminEnum.class , Integer.parseInt(warningType)).getDescription()); + } + }); + deliveryLogVo.setWarningTypeList(warningTypes); + deliveryLogVo.setCreateByName(userMap.get(deliveryLogVo.getCreatedBy())); + + // 添加司机信息关联查询逻辑 + if (deliveryLogVo.getDriverId() != null) { + try { + System.out.println("=== 列表查询-开始查询司机信息 ==="); + System.out.println("driverId: " + deliveryLogVo.getDriverId()); + System.out.println("运单号: " + deliveryLogVo.getDeliveryNumber()); + System.out.println("车牌号: " + deliveryLogVo.getLicensePlate()); + + Map driverInfo = memberDriverMapper.selectDriverById(deliveryLogVo.getDriverId()); + System.out.println("司机信息查询结果: " + driverInfo); + + // 额外调试:检查是否有多个司机记录 + if (driverInfo != null) { + String driverName = (String) driverInfo.get("username"); + String licensePlate = (String) driverInfo.get("car_number"); + System.out.println("查询到的司机姓名: " + driverName); + System.out.println("查询到的车牌号: " + licensePlate); + System.out.println("运单中的车牌号: " + deliveryLogVo.getLicensePlate()); + + // 如果车牌号不匹配,尝试根据车牌号查询 + if (licensePlate != null && !licensePlate.equals(deliveryLogVo.getLicensePlate())) { + System.out.println("*** 车牌号不匹配,尝试根据车牌号重新查询 ***"); + Map correctDriver = memberDriverMapper.selectDriverByPlate(deliveryLogVo.getLicensePlate()); + if (correctDriver != null) { + System.out.println("根据车牌号查询到的司机信息: " + correctDriver); + driverInfo = correctDriver; // 使用正确的司机信息 + } + } + } + + if (driverInfo != null) { + String driverName = (String) driverInfo.get("username"); + String licensePlate = (String) driverInfo.get("car_number"); + String carImg = (String) driverInfo.get("car_img"); + + System.out.println("司机姓名: " + driverName + ", 车牌号: " + licensePlate); + System.out.println("原始car_img字段: " + carImg); + + // 设置司机信息 + deliveryLogVo.setDriverName(driverName); + deliveryLogVo.setLicensePlate(licensePlate); + + // 强制从司机信息中获取最新的车身照片,确保数据一致性 + if (carImg != null && !carImg.isEmpty()) { + // 按逗号分割car_img字段,分别映射到车头和车尾照片 + String[] carImgUrls = carImg.split(","); + + if (carImgUrls.length >= 2) { + // 逗号前面的URL作为车尾照片 + String carBehindPhoto = carImgUrls[0].trim(); + // 逗号后面的URL作为车头照片 + String carFrontPhoto = carImgUrls[1].trim(); + + deliveryLogVo.setCarBehindPhoto(carBehindPhoto); + deliveryLogVo.setCarFrontPhoto(carFrontPhoto); + + System.out.println("列表查询-分割车身照片:"); + System.out.println(" 车尾照片: " + carBehindPhoto); + System.out.println(" 车头照片: " + carFrontPhoto); + } else if (carImgUrls.length == 1) { + // 只有一个URL时,同时设置为车头和车尾照片 + String singlePhoto = carImgUrls[0].trim(); + deliveryLogVo.setCarFrontPhoto(singlePhoto); + deliveryLogVo.setCarBehindPhoto(singlePhoto); + + System.out.println("列表查询-单张车身照片: " + singlePhoto); + } else { + // 没有有效URL + deliveryLogVo.setCarFrontPhoto(""); + deliveryLogVo.setCarBehindPhoto(""); + System.out.println("列表查询-无有效车身照片URL"); + } + } else { + // 如果司机信息中没有照片,清空delivery表中的照片 + deliveryLogVo.setCarFrontPhoto(""); + deliveryLogVo.setCarBehindPhoto(""); + System.out.println("列表查询-司机信息中无照片,清空车身照片"); + } + + // 如果车身照片仍然为空,尝试根据司机姓名和车牌号查询其他相关记录 + if ((deliveryLogVo.getCarFrontPhoto() == null || deliveryLogVo.getCarFrontPhoto().isEmpty()) && + (deliveryLogVo.getCarBehindPhoto() == null || deliveryLogVo.getCarBehindPhoto().isEmpty()) && + driverName != null && licensePlate != null) { + try { + System.out.println("列表查询-尝试根据司机姓名和车牌号查询相关记录"); + Map relatedDriver = memberDriverMapper.selectDriverByNameAndPlate( + driverName, licensePlate); + if (relatedDriver != null) { + String relatedCarImg = (String) relatedDriver.get("car_img"); + if (relatedCarImg != null && !relatedCarImg.isEmpty()) { + deliveryLogVo.setCarFrontPhoto(relatedCarImg); + deliveryLogVo.setCarBehindPhoto(relatedCarImg); + System.out.println("列表查询-从相关记录设置车身照片: " + relatedCarImg); + } + } + } catch (Exception e) { + System.out.println("列表查询-查询相关司机记录失败: " + e.getMessage()); + } + } + } else { + System.out.println("列表查询-未找到司机信息,driverId: " + deliveryLogVo.getDriverId()); + } + } catch (Exception e) { + System.out.println("列表查询-查询司机信息异常: " + e.getMessage()); + e.printStackTrace(); + } + } else { + System.out.println("列表查询-driverId为空,跳过司机信息查询"); + } + + // 添加核验状态中文映射 + Integer status = deliveryLogVo.getStatus(); + System.out.println("列表查询-处理状态映射,status: " + status); + if (status != null) { + String statusDesc = getStatusDescription(status); + System.out.println("列表查询-状态描述: " + statusDesc); + deliveryLogVo.setStatusDesc(statusDesc); + System.out.println("列表查询-设置后的statusDesc: " + deliveryLogVo.getStatusDesc()); + } else { + System.out.println("列表查询-status为空,跳过状态映射"); + } + + // 统计登记设备数量(耳标+项圈) + Integer currentDeliveryId = deliveryLogVo.getId(); + if (currentDeliveryId != null) { + try { + // 统计耳标设备数量 + LambdaQueryWrapper earTagWrapper = new LambdaQueryWrapper<>(); + earTagWrapper.eq(DeliveryDevice::getDeliveryId, currentDeliveryId) + .eq(DeliveryDevice::getDeviceType, 2); + long earTagCount = deliveryDeviceService.count(earTagWrapper); + + // 统计项圈设备数量 + LambdaQueryWrapper collarWrapper = new LambdaQueryWrapper<>(); + collarWrapper.eq(DeliveryDevice::getDeliveryId, currentDeliveryId) + .eq(DeliveryDevice::getDeviceType, 3); + long collarCount = deliveryDeviceService.count(collarWrapper); + + // 设置总设备数量和耳标数量 + int totalDeviceCount = (int) (earTagCount + collarCount); + deliveryLogVo.setRegisteredJbqCount(totalDeviceCount); + deliveryLogVo.setEarTagCount((int) earTagCount); + + // 设置已分配设备数量,与登记设备数量保持一致 + deliveryLogVo.setBindJbqCount(totalDeviceCount); + + // 统计已佩戴设备数量(bandge_status = 1) + int wornDeviceCount = 0; + try { + // 统计已佩戴的耳标设备数量(通过delivery_device表关联) + LambdaQueryWrapper wornEarTagWrapper = new LambdaQueryWrapper<>(); + wornEarTagWrapper.eq(DeliveryDevice::getDeliveryId, currentDeliveryId) + .eq(DeliveryDevice::getDeviceType, 2) + .eq(DeliveryDevice::getIsWare, 1); // 1表示已佩戴 + long wornEarTagCount = deliveryDeviceService.count(wornEarTagWrapper); + + // 统计已佩戴的项圈设备数量(通过delivery_device表关联xq_client表) + LambdaQueryWrapper wornCollarWrapper = new LambdaQueryWrapper<>(); + wornCollarWrapper.eq(DeliveryDevice::getDeliveryId, currentDeliveryId) + .eq(DeliveryDevice::getDeviceType, 3); + List collarDevices = deliveryDeviceService.list(wornCollarWrapper); + + int wornCollarCount = 0; + for (DeliveryDevice device : collarDevices) { + // 查询xq_client表中的bandge_status + LambdaQueryWrapper xqWrapper = new LambdaQueryWrapper<>(); + xqWrapper.eq(XqClient::getSn, device.getDeviceId()); + XqClient xqClient = xqClientService.getOne(xqWrapper); + if (xqClient != null && xqClient.getBandgeStatus() != null && xqClient.getBandgeStatus() == 1) { + wornCollarCount++; + } + } + + wornDeviceCount = (int) (wornEarTagCount + wornCollarCount); + deliveryLogVo.setWareCount(wornDeviceCount); + + System.out.println("运单ID " + currentDeliveryId + " 已佩戴设备统计: 耳标=" + wornEarTagCount + ", 项圈=" + wornCollarCount + ", 总计=" + wornDeviceCount); + } catch (Exception e) { + System.out.println("统计已佩戴设备数量失败: " + e.getMessage()); + deliveryLogVo.setWareCount(0); + } + + System.out.println("运单ID " + currentDeliveryId + " 设备统计: 耳标=" + earTagCount + ", 项圈=" + collarCount + ", 总计=" + totalDeviceCount + ", 已佩戴=" + wornDeviceCount); + } catch (Exception e) { + System.out.println("统计设备数量失败: " + e.getMessage()); + deliveryLogVo.setRegisteredJbqCount(0); + deliveryLogVo.setBindJbqCount(0); + deliveryLogVo.setWareCount(0); + } + } + + // 查询四个角色的手机号(供应商、资金方、采购商) + try { + // 1. 查询供应商手机号(supplierId是逗号分隔的字符串) + if (StringUtils.isNotEmpty(deliveryLogVo.getSupplierId())) { + String[] supplierIds = deliveryLogVo.getSupplierId().split(","); + List supplierMobiles = new ArrayList<>(); + for (String supplierId : supplierIds) { + if (StringUtils.isNotEmpty(supplierId.trim())) { + try { + Integer sid = Integer.parseInt(supplierId.trim()); + Member supplier = memberMapper.selectById(sid); + if (supplier != null && StringUtils.isNotEmpty(supplier.getMobile())) { + supplierMobiles.add(supplier.getMobile()); + } + } catch (NumberFormatException e) { + System.out.println("供应商ID格式错误: " + supplierId); + } + } + } + if (!supplierMobiles.isEmpty()) { + deliveryLogVo.setSupplierMobile(String.join(",", supplierMobiles)); + } + } + + // 2. 查询资金方手机号 + if (deliveryLogVo.getFundId() != null) { + Member fund = memberMapper.selectById(deliveryLogVo.getFundId()); + if (fund != null && StringUtils.isNotEmpty(fund.getMobile())) { + deliveryLogVo.setFundMobile(fund.getMobile()); + } + } + + // 3. 查询采购商手机号 + if (deliveryLogVo.getBuyerId() != null) { + Member buyer = memberMapper.selectById(deliveryLogVo.getBuyerId()); + if (buyer != null && StringUtils.isNotEmpty(buyer.getMobile())) { + deliveryLogVo.setBuyerMobile(buyer.getMobile()); + } + } + } catch (Exception e) { + System.out.println("查询角色手机号异常: " + e.getMessage()); + e.printStackTrace(); + } + }); + + // 数据权限过滤:非超级管理员只能看到与自己手机号相关的订单 + System.out.println("=== 超级管理员判断调试 ==="); + System.out.println("SecurityUtil.isSuperAdmin(): " + SecurityUtil.isSuperAdmin()); + System.out.println("当前用户角色ID: " + SecurityUtil.getRoleId()); + System.out.println("超级管理员角色ID常量: " + RoleConstants.SUPER_ADMIN_ROLE_ID); + + if (!SecurityUtil.isSuperAdmin() && StringUtils.isNotEmpty(currentUserMobile)) { + System.out.println("=== 非超级管理员,执行数据权限过滤 ==="); + System.out.println("当前用户手机号: " + currentUserMobile); + System.out.println("过滤前的订单数量: " + resList.size()); + + resList = resList.stream().filter(delivery -> { + boolean hasPermission = false; + + System.out.println("=== 检查订单权限: " + delivery.getDeliveryNumber() + " ==="); + System.out.println("司机手机号: " + delivery.getDriverMobile()); + System.out.println("供应商手机号: " + delivery.getSupplierMobile()); + System.out.println("资金方手机号: " + delivery.getFundMobile()); + System.out.println("采购商手机号: " + delivery.getBuyerMobile()); + + // 检查是否是司机 + if (StringUtils.isNotEmpty(delivery.getDriverMobile()) && + currentUserMobile.equals(delivery.getDriverMobile())) { + hasPermission = true; + System.out.println("订单 " + delivery.getDeliveryNumber() + " - 匹配司机手机号"); + } + + // 检查是否是供应商(可能有多个供应商) + if (!hasPermission && StringUtils.isNotEmpty(delivery.getSupplierMobile())) { + String[] supplierMobiles = delivery.getSupplierMobile().split(","); + for (String mobile : supplierMobiles) { + if (currentUserMobile.equals(mobile.trim())) { + hasPermission = true; + System.out.println("订单 " + delivery.getDeliveryNumber() + " - 匹配供应商手机号"); + break; + } + } + } + + // 检查是否是资金方 + if (!hasPermission && StringUtils.isNotEmpty(delivery.getFundMobile()) && + currentUserMobile.equals(delivery.getFundMobile())) { + hasPermission = true; + System.out.println("订单 " + delivery.getDeliveryNumber() + " - 匹配资金方手机号"); + } + + // 检查是否是采购商 + if (!hasPermission && StringUtils.isNotEmpty(delivery.getBuyerMobile()) && + currentUserMobile.equals(delivery.getBuyerMobile())) { + hasPermission = true; + System.out.println("订单 " + delivery.getDeliveryNumber() + " - 匹配采购商手机号"); + } + + if (!hasPermission) { + System.out.println("订单 " + delivery.getDeliveryNumber() + " - 无权限,过滤掉"); + } + + return hasPermission; + }).collect(Collectors.toList()); + + System.out.println("过滤后的订单数量: " + resList.size()); + } else if (SecurityUtil.isSuperAdmin()) { + System.out.println("=== 超级管理员,不执行数据权限过滤 ==="); + } else { + System.out.println("=== 非超级管理员,但未获取到当前用户手机号,跳过数据过滤 ==="); + } + } + + // 更新分页信息 + long filteredTotal = resList.size(); + return new PageResultResponse(filteredTotal, resList); + } + + @Override + public PageResultResponse pageQueryList(DeliverListDto dto) { + //获取当前登录人的信息 +// Integer currentUserId = SecurityUtil.getCurrentUserId(); + Page result = PageHelper.startPage(dto.getPageNum(), dto.getPageSize()); +// dto.setCurrentUserId(currentUserId); + if(StringUtils.isNotEmpty(dto.getStartTime())){ + String startTime = dto.getStartTime() + " 00:00:00"; + dto.setStartTime(startTime); + } + if(StringUtils.isNotEmpty(dto.getEndTime())){ + String endTime = dto.getEndTime() + " 23:59:59"; + dto.setEndTime(endTime); + } + List resList = this.baseMapper.getPageWarningLog(dto); + resList.forEach(deliveryLogVo -> { + String warningType = deliveryLogVo.getWarningType(); + if(StringUtils.isNotEmpty(warningType)){ + deliveryLogVo.setWarningTypeDesc(EnumUtil.getEnumConstant(WarningStatusAdminEnum.class , Integer.parseInt(warningType)).getDescription()); + } + }); + return new PageResultResponse(result.getTotal(), resList); + } + + @Override + public AjaxResult detail(Integer id) { + Map resMap = new HashMap<>(); + //查询运单信息 + Delivery delivery = this.getById(id); + + // 查询供应商名称 + if (delivery.getSupplierId() != null && !delivery.getSupplierId().isEmpty()) { + try { + Member supplier = memberMapper.selectById(delivery.getSupplierId()); + if (supplier != null) { + delivery.setSupplierName(supplier.getMobile()); // 使用手机号作为显示名称 + } + } catch (Exception e) { + // 忽略查询错误,继续处理 + } + } + + // 查询资金方名称 + if (delivery.getFundId() != null) { + try { + Member funder = memberMapper.selectById(delivery.getFundId()); + if (funder != null) { + delivery.setFundName(funder.getMobile()); // 使用手机号作为显示名称 + } + } catch (Exception e) { + // 忽略查询错误,继续处理 + } + } + + // 查询采购商名称 + if (delivery.getBuyerId() != null) { + try { + Member buyer = memberMapper.selectById(delivery.getBuyerId()); + if (buyer != null) { + delivery.setBuyerName(buyer.getMobile()); // 使用手机号作为显示名称 + } + } catch (Exception e) { + // 忽略查询错误,继续处理 + } + } + + // 查询司机信息 + if (delivery.getDriverId() != null) { + try { + System.out.println("开始查询司机信息,driverId: " + delivery.getDriverId()); + // 根据司机ID直接查询司机信息 + Map driverInfo = memberDriverMapper.selectDriverById(delivery.getDriverId()); + System.out.println("司机信息查询结果: " + driverInfo); + + if (driverInfo != null) { + String driverName = (String) driverInfo.get("username"); + String licensePlate = (String) driverInfo.get("car_number"); + String carImg = (String) driverInfo.get("car_img"); + String driverMobile = (String) driverInfo.get("mobile"); + + System.out.println("司机姓名: " + driverName + ", 车牌号: " + licensePlate + ", 车辆照片: " + carImg + ", 司机手机: " + driverMobile); + + // 设置司机信息 + delivery.setDriverName(driverName); + delivery.setLicensePlate(licensePlate); + delivery.setDriverMobile(driverMobile); + + // 强制从司机信息中获取最新的车身照片,确保数据一致性 + if (carImg != null && !carImg.isEmpty()) { + // 按逗号分割car_img字段,分别映射到车头和车尾照片 + String[] carImgUrls = carImg.split(","); + + if (carImgUrls.length >= 2) { + // 逗号前面的URL作为车尾照片 + String carBehindPhoto = carImgUrls[0].trim(); + // 逗号后面的URL作为车头照片 + String carFrontPhoto = carImgUrls[1].trim(); + + delivery.setCarBehindPhoto(carBehindPhoto); + delivery.setCarFrontPhoto(carFrontPhoto); + + System.out.println("详情查询-分割车身照片:"); + System.out.println(" 车尾照片: " + carBehindPhoto); + System.out.println(" 车头照片: " + carFrontPhoto); + } else if (carImgUrls.length == 1) { + // 只有一个URL时,同时设置为车头和车尾照片 + String singlePhoto = carImgUrls[0].trim(); + delivery.setCarFrontPhoto(singlePhoto); + delivery.setCarBehindPhoto(singlePhoto); + + System.out.println("详情查询-单张车身照片: " + singlePhoto); + } else { + // 没有有效URL + delivery.setCarFrontPhoto(""); + delivery.setCarBehindPhoto(""); + System.out.println("详情查询-无有效车身照片URL"); + } + } else { + // 如果司机信息中没有照片,清空delivery表中的照片 + delivery.setCarFrontPhoto(""); + delivery.setCarBehindPhoto(""); + System.out.println("详情查询-司机信息中无照片,清空车身照片"); + } + + // 如果车身照片仍然为空,尝试根据司机姓名和车牌号查询其他相关记录 + if ((delivery.getCarFrontPhoto() == null || delivery.getCarFrontPhoto().isEmpty()) && + (delivery.getCarBehindPhoto() == null || delivery.getCarBehindPhoto().isEmpty()) && + driverName != null && licensePlate != null) { + try { + System.out.println("尝试根据司机姓名和车牌号查询相关记录"); + Map relatedDriver = memberDriverMapper.selectDriverByNameAndPlate( + driverName, licensePlate); + if (relatedDriver != null) { + String relatedCarImg = (String) relatedDriver.get("car_img"); + String relatedDriverMobile = (String) relatedDriver.get("mobile"); + + if (relatedCarImg != null && !relatedCarImg.isEmpty()) { + delivery.setCarFrontPhoto(relatedCarImg); + delivery.setCarBehindPhoto(relatedCarImg); // 使用同一张照片作为前后照片 + System.out.println("从相关记录设置车身照片: " + relatedCarImg); + } + + // 如果当前司机手机号为空,使用相关记录的手机号 + if ((delivery.getDriverMobile() == null || delivery.getDriverMobile().isEmpty()) && + relatedDriverMobile != null && !relatedDriverMobile.isEmpty()) { + delivery.setDriverMobile(relatedDriverMobile); + System.out.println("从相关记录设置司机手机号: " + relatedDriverMobile); + } + } + } catch (Exception e) { + System.out.println("查询相关司机记录失败: " + e.getMessage()); + } + } + } else { + System.out.println("未找到司机信息,driverId: " + delivery.getDriverId()); + } + } catch (Exception e) { + // 忽略查询错误,继续处理 + System.out.println("查询司机信息异常: " + e.getMessage()); + e.printStackTrace(); + } + } else { + System.out.println("driverId为空,跳过司机信息查询"); + } + + //根据运单信息,查询最新的预警信息 + List warningLogs = warningLogMapper.selectList( + new LambdaQueryWrapper().eq(WarningLog::getDeliveryId, id).notIn(WarningLog::getWarningType, "0","1").orderByDesc(WarningLog::getWarningTime)); + if(CollectionUtils.isNotEmpty(warningLogs)){ + warningLogs.forEach(warningLog -> { + String warningType = warningLog.getWarningType(); + if(StringUtils.isNotEmpty(warningType)){ + warningLog.setWarningTypeDesc(EnumUtil.getEnumConstant(WarningStatusAdminEnum.class , Integer.parseInt(warningType)).getDescription()); + } + }); + } + //查询核验人名称 + SysUser sysUser = sysUserMapper.selectById(delivery.getCheckBy()); + if(null != sysUser){ + String checkByName = sysUser.getName(); + delivery.setCheckByName(checkByName); + } + resMap.put("delivery", delivery); + resMap.put("warningLog", warningLogs); + //查询主机信息 + DeliveryDevice deliveryDevices = deliveryDeviceMapper.selectOne(new LambdaQueryWrapper().eq(DeliveryDevice::getDeliveryId, id).eq(DeliveryDevice::getDeviceType, 1).last("limit 1")); + if(null != deliveryDevices){ + resMap.put("serverIds", deliveryDevices.getDeviceId()); + }else{ + resMap.put("serverIds", ""); + } + return AjaxResult.success(resMap); + } + + /** + * 获取核验状态的中文描述 + * @param status 状态码 + * @return 中文描述 + */ + private String getStatusDescription(Integer status) { + if (status == null) { + return "未知状态"; + } + + switch (status) { + case 1: + return "待装车"; + case 2: + return "已装车/待资金方付款"; + case 3: + return "待核验/资金方已付款"; + case 4: + return "已核验/待买家付款"; + case 5: + return "买家已付款"; + default: + return "未知状态"; + } + } + + +} diff --git a/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/service/impl/JbqClientLogServiceImpl.java b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/service/impl/JbqClientLogServiceImpl.java new file mode 100644 index 0000000..c994008 --- /dev/null +++ b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/service/impl/JbqClientLogServiceImpl.java @@ -0,0 +1,53 @@ +package com.aiotagro.cattletrade.business.service.impl; + +import com.aiotagro.cattletrade.business.dto.JbqLogDto; +import com.aiotagro.cattletrade.business.entity.Delivery; +import com.aiotagro.cattletrade.business.entity.JbqClientLog; +import com.aiotagro.cattletrade.business.mapper.DeliveryMapper; +import com.aiotagro.cattletrade.business.mapper.JbqClientLogMapper; +import com.aiotagro.cattletrade.business.service.IJbqClientLogService; +import com.aiotagro.common.core.web.domain.PageResultResponse; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.github.pagehelper.Page; +import com.github.pagehelper.PageHelper; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.Date; +import java.util.List; + +/** + *

+ * 智能耳标日志表 服务实现类 + *

+ * + * @author admin + * @since 2024-12-26 + */ +@Service +public class JbqClientLogServiceImpl extends ServiceImpl implements IJbqClientLogService { + + @Autowired + private JbqClientLogMapper jbqServerMapper; + @Autowired + private DeliveryMapper deliveryMapper; + @Override + public PageResultResponse jbqLogList(JbqLogDto dto) { + if (dto.getDeliveryId() == null) { + return new PageResultResponse(500, "运单id不能为空"); + } + Delivery delivery = deliveryMapper.selectById(dto.getDeliveryId()); + if (delivery == null) { + return new PageResultResponse(500, "运单不存在"); + } + Integer deliveryId = dto.getDeliveryId(); + Date createTime = delivery.getCreateTime(); + Date checkTime = delivery.getCheckTime(); + if (checkTime == null) { + checkTime = new Date(); + } + Page result = PageHelper.startPage(dto.getPageNum(), dto.getPageSize()); + List list = jbqServerMapper.jbqLogList(deliveryId,createTime,checkTime); + return new PageResultResponse(result.getTotal(), list); + } +} diff --git a/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/service/impl/JbqClientServiceImpl.java b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/service/impl/JbqClientServiceImpl.java new file mode 100644 index 0000000..5896888 --- /dev/null +++ b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/service/impl/JbqClientServiceImpl.java @@ -0,0 +1,125 @@ +package com.aiotagro.cattletrade.business.service.impl; + +import com.aiotagro.cattletrade.business.dto.DeviceDto; +import com.aiotagro.cattletrade.business.entity.DeliveryDevice; +import com.aiotagro.cattletrade.business.entity.JbqClient; +import com.aiotagro.cattletrade.business.mapper.DeliveryDeviceMapper; +import com.aiotagro.cattletrade.business.mapper.JbqClientMapper; +import com.aiotagro.cattletrade.business.service.IJbqClientService; +import com.aiotagro.cattletrade.business.vo.JbqClientVo; +import com.aiotagro.common.core.web.domain.AjaxResult; +import com.aiotagro.common.core.web.domain.PageResultResponse; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.github.pagehelper.Page; +import com.github.pagehelper.PageHelper; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + *

+ * 智能耳标表 服务实现类 + *

+ * + * @author admin + * @since 2024-12-26 + */ +@Service +public class JbqClientServiceImpl extends ServiceImpl implements IJbqClientService { + + @Autowired + private JbqClientMapper jbqClientMapper; + + @Autowired + private DeliveryDeviceMapper deliveryDeviceMapper; + + @Override + public PageResultResponse jbqList(DeviceDto dto) { + Page result = PageHelper.startPage(dto.getPageNum(), dto.getPageSize()); + + try { + // 如果指定了只查询未分配的设备 + if (dto.getUnassignedOnly() != null && dto.getUnassignedOnly()) { + System.out.println("查询未分配的智能耳标,参数: " + dto); + List list = jbqClientMapper.selectUnassignedJbqList(dto); + System.out.println("未分配智能耳标查询结果数量: " + (list != null ? list.size() : 0)); + return new PageResultResponse(result.getTotal(), list); + } else { + System.out.println("查询所有智能耳标,参数: " + dto); + List list = jbqClientMapper.JbqList(dto); + System.out.println("所有智能耳标查询结果数量: " + (list != null ? list.size() : 0)); + return new PageResultResponse(result.getTotal(), list); + } + } catch (Exception e) { + System.out.println("查询智能耳标失败: " + e.getMessage()); + e.printStackTrace(); + return new PageResultResponse(0L, new ArrayList<>()); + } + } + + @Override + public AjaxResult getDevicesByDeliveryId(Integer deliveryId, Integer deviceType) { + System.out.println("=== 查询耳标设备,deliveryId: " + deliveryId + ", deviceType: " + deviceType); + + if (deliveryId == null) { + return AjaxResult.error("运送清单ID不能为空"); + } + + // 查询运送清单关联的设备 + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(DeliveryDevice::getDeliveryId, deliveryId); + wrapper.eq(DeliveryDevice::getDeviceType, deviceType); + List deliveryDevices = deliveryDeviceMapper.selectList(wrapper); + + System.out.println("=== 查询到delivery_device记录数: " + deliveryDevices.size()); + if (!deliveryDevices.isEmpty()) { + System.out.println("=== delivery_device记录详情: " + deliveryDevices); + } + + if (deliveryDevices.isEmpty()) { + return AjaxResult.success(new ArrayList<>()); + } + + // 获取设备ID列表 + List deviceIds = deliveryDevices.stream() + .map(DeliveryDevice::getDeviceId) + .collect(Collectors.toList()); + + // 查询设备详细信息 - 只查询基础字段,避免查询不存在的扩展字段 + LambdaQueryWrapper deviceWrapper = new LambdaQueryWrapper<>(); + deviceWrapper.in(JbqClient::getDeviceId, deviceIds) + .select(JbqClient::getId, + JbqClient::getDeviceId, + JbqClient::getDeviceVoltage, + JbqClient::getDeviceTemp, + JbqClient::getServerDeviceId, + JbqClient::getLatitude, + JbqClient::getLongitude, + JbqClient::getWalkSteps, + JbqClient::getYWalkSteps, + JbqClient::getCreateTime, + JbqClient::getCreateBy, + JbqClient::getUpdateTime, + JbqClient::getUpdateBy, + JbqClient::getSourceId); + List devices = jbqClientMapper.selectList(deviceWrapper); + + System.out.println("=== 查询到的耳标设备数量: " + devices.size()); + for (JbqClient device : devices) { + System.out.println("=== 耳标设备: " + device); + } + + // 返回分页格式数据 + Map result = new HashMap<>(); + result.put("rows", devices); + result.put("total", devices.size()); + + return AjaxResult.success(result); + } +} diff --git a/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/service/impl/JbqServerLogServiceImpl.java b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/service/impl/JbqServerLogServiceImpl.java new file mode 100644 index 0000000..343aa8f --- /dev/null +++ b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/service/impl/JbqServerLogServiceImpl.java @@ -0,0 +1,20 @@ +package com.aiotagro.cattletrade.business.service.impl; + +import com.aiotagro.cattletrade.business.entity.JbqServerLog; +import com.aiotagro.cattletrade.business.mapper.JbqServerLogMapper; +import com.aiotagro.cattletrade.business.service.IJbqServerLogService; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import org.springframework.stereotype.Service; + +/** + *

+ * 智能主机日志表 服务实现类 + *

+ * + * @author admin + * @since 2024-12-26 + */ +@Service +public class JbqServerLogServiceImpl extends ServiceImpl implements IJbqServerLogService { + +} diff --git a/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/service/impl/JbqServerServiceImpl.java b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/service/impl/JbqServerServiceImpl.java new file mode 100644 index 0000000..8572612 --- /dev/null +++ b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/service/impl/JbqServerServiceImpl.java @@ -0,0 +1,142 @@ +package com.aiotagro.cattletrade.business.service.impl; + +import com.aiotagro.cattletrade.business.dto.DeviceDto; +import com.aiotagro.cattletrade.business.dto.ServerLocationDto; +import com.aiotagro.cattletrade.business.entity.Delivery; +import com.aiotagro.cattletrade.business.entity.JbqServer; +import com.aiotagro.cattletrade.business.mapper.DeliveryMapper; +import com.aiotagro.cattletrade.business.mapper.JbqServerLogMapper; +import com.aiotagro.cattletrade.business.mapper.JbqServerMapper; +import com.aiotagro.cattletrade.business.service.IJbqServerService; +import com.aiotagro.cattletrade.business.vo.ServerClientVo; +import com.aiotagro.cattletrade.business.vo.ServerLocationVo; +import com.aiotagro.common.core.utils.DateUtils; +import com.aiotagro.common.core.utils.LngLonUtil; +import com.aiotagro.common.core.utils.StringUtils; +import com.aiotagro.common.core.web.domain.AjaxResult; +import com.aiotagro.common.core.web.domain.PageResultResponse; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.github.pagehelper.Page; +import com.github.pagehelper.PageHelper; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.Date; +import java.util.List; +import java.util.stream.Collectors; + +/** + *

+ * 智能主机表 服务实现类 + *

+ * + * @author admin + * @since 2024-12-26 + */ +@Service +public class JbqServerServiceImpl extends ServiceImpl implements IJbqServerService { + + @Autowired + private JbqServerMapper jbqServerMapper; + @Autowired + private JbqServerLogMapper jbqServerLogMapper; + @Autowired + private DeliveryMapper deliveryMapper; + @Override + public PageResultResponse serverList(DeviceDto dto) { + Page result = PageHelper.startPage(dto.getPageNum(), dto.getPageSize()); + List list = jbqServerMapper.serverList(dto); + return new PageResultResponse(result.getTotal(), list); + } + + @Override + public AjaxResult serverLocation(ServerLocationDto dto) { + if (!StringUtils.hasText(dto.getServerDeviceId())){ + return AjaxResult.error("主机编号不能为空"); + } + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(JbqServer::getDeviceId, dto.getServerDeviceId()); + //查询主机数据 + JbqServer server = jbqServerMapper.selectOne(wrapper); + if (server == null) { + return AjaxResult.error("未找到对应的设备信息"); + } + // 获取设备的经纬度 + String latitude = server.getLatitude(); + String longitude = server.getLongitude(); + + // 判断经纬度是否为空或为0 + if (isInvalidLocation(latitude, longitude)) { + return AjaxResult.error("暂无定位信息"); + } + + // 转换为百度经纬度 + double lat = Double.parseDouble(latitude); + double lon = Double.parseDouble(longitude); + double[] baiduLocation = LngLonUtil.gps84_To_bd09(lat, lon); + + ServerLocationVo serverLocationVo = new ServerLocationVo(); + serverLocationVo.setDeviceId(dto.getServerDeviceId()); + serverLocationVo.setLatitude(String.valueOf(baiduLocation[0])); + serverLocationVo.setLongitude(String.valueOf(baiduLocation[1])); + serverLocationVo.setUpdateTime(server.getUpdateTime()); + return AjaxResult.success(serverLocationVo); + } + + @Override + public AjaxResult serverTrack(ServerLocationDto dto) { + if (dto.getDeliveryId() == null) { + return AjaxResult.error("id不能为空"); + } + if (!StringUtils.hasText(dto.getServerDeviceId())){ + return AjaxResult.error("主机编号不能为空"); + } + //启运时间 + Delivery delivery = deliveryMapper.selectById(dto.getDeliveryId()); + if (delivery == null) { + return AjaxResult.error("未找到相关运单信息"); + } + + Date deliverTime = delivery.getCreateTime(); + Date checkTime = (delivery.getCheckTime() != null) ? delivery.getCheckTime() : + DateUtils.stringToDate(DateUtils.getTime(), DateUtils.YYYY_MM_DD_HH_MM_SS); + + if (deliverTime == null) { + return AjaxResult.error("启运时间为空"); + } + // 轨迹 + List serverLocations = jbqServerLogMapper.selectServerLocationByTime(dto.getServerDeviceId(), deliverTime, checkTime); + + if (serverLocations != null && !serverLocations.isEmpty()) { + // 过滤掉经纬度/坐标转换 + serverLocations = serverLocations.stream() + .filter(location -> { + return StringUtils.hasText(location.getLatitude()) + && StringUtils.hasText(location.getLongitude()) + && !"0".equals(location.getLatitude()) + && !"0".equals(location.getLongitude()); + }) + .peek(location -> { + double latitude = Double.parseDouble(location.getLatitude()); + double longitude = Double.parseDouble(location.getLongitude()); + double[] bd09 = LngLonUtil.gps84_To_bd09(latitude, longitude); + + location.setLatitude(String.valueOf(bd09[0])); + location.setLongitude(String.valueOf(bd09[1])); + }) + .collect(Collectors.toList()); + return AjaxResult.success(serverLocations); + } else { + return AjaxResult.error("没有找到符合条件的设备位置信息"); + } + + } + + + + public boolean isInvalidLocation(String latitude, String longitude) { + return latitude == null || latitude.isEmpty() || "0".equals(latitude) || + longitude == null || longitude.isEmpty() || "0".equals(longitude); + } +} diff --git a/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/service/impl/LoginServiceImpl.java b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/service/impl/LoginServiceImpl.java new file mode 100644 index 0000000..4ae6de0 --- /dev/null +++ b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/service/impl/LoginServiceImpl.java @@ -0,0 +1,180 @@ +package com.aiotagro.cattletrade.business.service.impl; + +import cn.dev33.satoken.SaManager; +import cn.dev33.satoken.stp.SaLoginConfig; +import cn.dev33.satoken.stp.StpUtil; +import cn.hutool.core.util.RandomUtil; +import cn.hutool.crypto.digest.DigestUtil; +import com.aiotagro.cattletrade.business.dto.LoginDto; +import com.aiotagro.cattletrade.business.entity.SysMenu; +import com.aiotagro.cattletrade.business.entity.SysRole; +import com.aiotagro.cattletrade.business.entity.SysUser; +import com.aiotagro.cattletrade.business.mapper.SysMenuMapper; +import com.aiotagro.cattletrade.business.mapper.SysRoleMapper; +import com.aiotagro.cattletrade.business.mapper.SysUserMapper; +import com.aiotagro.cattletrade.business.service.LoginService; +import com.aiotagro.cattletrade.business.service.TencentSmsCodeService; +import com.aiotagro.common.core.constant.Constants; +import com.aiotagro.common.core.constant.RoleConstants; +import com.aiotagro.common.core.exception.ServiceException; +import com.aiotagro.common.core.utils.SecurityUtil; +import com.aiotagro.common.core.web.domain.AjaxResult; +import com.aiotagro.common.redis.service.RedisService; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import lombok.extern.slf4j.Slf4j; +import com.aiotagro.common.core.utils.StringUtils; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; +import java.util.*; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; + +@Slf4j +@Service +public class LoginServiceImpl implements LoginService { + + @Resource + private RedisService redisService; + + @Resource + SysUserMapper userMapper; + + @Resource + TencentSmsCodeService tencentSmsCodeService; + + @Resource + SysMenuMapper menuMapper; + + @Resource + SysRoleMapper roleMapper; + + + @Override + public AjaxResult sendLoginSmsCode(String mobile) { + // 临时跳过Redis验证码存储,直接返回成功 + SysUser user = userMapper.selectOne(new QueryWrapper().lambda().eq(SysUser::getMobile, mobile).eq(SysUser::getIsDelete, 0)); + if (null == user || user.getIsDelete() == 1) { + return AjaxResult.error("手机号不存在!"); + } + if (user.getStatus() == 0) { + return AjaxResult.error("手机号已被禁用!"); + } + + log.warn("短信验证码发送暂时跳过Redis存储,直接返回成功"); + return AjaxResult.success("验证码发送成功"); + } + + @Override + public AjaxResult login(LoginDto dto) { + log.info(dto.toString()); + SysUser user = userMapper.selectOne(new QueryWrapper().lambda().eq(SysUser::getMobile, dto.getMobile()).eq(SysUser::getIsDelete, 0)); + if (dto.getLoginType() == 0) { + // 判断用户存不存在 + if (null == user || user.getIsDelete() == 1) { + return AjaxResult.error("员工不存在!"); + } + if (user.getStatus() == 0) { + return AjaxResult.error("此账号已禁用!"); + } + // 密码校验 + String passwordMd5 = DigestUtil.md5Hex(Constants.USER_PASSWORD_PREFIX + dto.getPassword()); + if (!passwordMd5.equals(user.getPassword())) { + throw new ServiceException("密码错误!"); + } + } else if (dto.getLoginType() == 1) { + // 短信验证码校验 - 临时跳过Redis验证 + log.warn("短信验证码登录暂时跳过Redis验证,直接允许登录"); + } + + // 更新登录时间 + user.setLastLoginTime(new Date()); + userMapper.updateById(user); + + log.info(user.toString()); + // 登录成功,写入缓存数据并返回token值 + StpUtil.login(user.getId(), SaLoginConfig + .setExtra("username", user.getName()) + .setExtra("userId", user.getId()) + .setExtra("mobile", user.getMobile()) + .setExtra("roleId", user.getRoleId()) + ); + + // 存储角色ID到session + StpUtil.getTokenSession().set("roleId", user.getRoleId()); + // 存储用户名与手机号到session,便于业务侧读取 + StpUtil.getTokenSession().set("username", user.getName()); + StpUtil.getTokenSession().set("mobile", user.getMobile()); + + // 调试日志:验证TokenSession写入 + log.info("=== TokenSession 写入调试 ==="); + log.info("写入 username: {}", user.getName()); + log.info("写入 mobile: {}", user.getMobile()); + log.info("写入 roleId: {}", user.getRoleId()); + + // 立即验证读取 + Object sessionUsername = StpUtil.getTokenSession().get("username"); + Object sessionMobile = StpUtil.getTokenSession().get("mobile"); + Object sessionRoleId = StpUtil.getTokenSession().get("roleId"); + log.info("验证读取 username: {}", sessionUsername); + log.info("验证读取 mobile: {}", sessionMobile); + log.info("验证读取 roleId: {}", sessionRoleId); + + // 查询用户权限列表 + List permissions = queryUserPermissions(user.getRoleId()); + StpUtil.getTokenSession().set("permissions", permissions); + + // 查询用户角色信息 + SysRole role = roleMapper.selectById(user.getRoleId()); + String roleCode = role != null ? role.getName() : ""; + StpUtil.getTokenSession().set("roleCode", roleCode); + + Map map = new HashMap(); + map.put("token", SaManager.getConfig().getTokenPrefix() + " " + StpUtil.getTokenValue()); + map.put("username", user.getName()); + map.put("userId", user.getId()); + map.put("mobile", user.getMobile()); + map.put("roleId", user.getRoleId()); + map.put("permissions", permissions); // 返回权限列表 + return AjaxResult.success("登录成功", map); + } + + @Override + public AjaxResult getUserMenus() { + Integer userId = SecurityUtil.getCurrentUserId(); + List menus = menuMapper.queryMenusByUserId(userId); + return AjaxResult.success("查询成功", menus); + } + + @Override + public AjaxResult getUserInfo() { + SysUser user = userMapper.getUserInfoById(SecurityUtil.getCurrentUserId()); + return AjaxResult.success(user); + } + + /** + * 查询用户权限列表 + * + * @param roleId 角色ID + * @return 权限列表 + */ + private List queryUserPermissions(Integer roleId) { + if (roleId == null) { + return Collections.emptyList(); + } + + // 如果是超级管理员,返回所有权限 + if (roleId.equals(RoleConstants.SUPER_ADMIN_ROLE_ID)) { + return Collections.singletonList(RoleConstants.ALL_PERMISSION); + } + + // 查询角色关联的菜单权限 + List menus = menuMapper.selectMenusByRoleId(roleId); + return menus.stream() + .filter(menu -> StringUtils.isNotEmpty(menu.getAuthority())) + .map(SysMenu::getAuthority) + .distinct() + .collect(Collectors.toList()); + } + +} diff --git a/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/service/impl/SysRoleServiceImpl.java b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/service/impl/SysRoleServiceImpl.java new file mode 100644 index 0000000..fc52e71 --- /dev/null +++ b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/service/impl/SysRoleServiceImpl.java @@ -0,0 +1,117 @@ +package com.aiotagro.cattletrade.business.service.impl; + +import com.aiotagro.cattletrade.business.dto.SysRoleDto; +import com.aiotagro.cattletrade.business.entity.SysMenu; +import com.aiotagro.cattletrade.business.entity.SysRole; +import com.aiotagro.cattletrade.business.entity.SysRoleMenu; +import com.aiotagro.cattletrade.business.entity.SysUser; +import com.aiotagro.cattletrade.business.mapper.SysMenuMapper; +import com.aiotagro.cattletrade.business.mapper.SysRoleMapper; +import com.aiotagro.cattletrade.business.mapper.SysRoleMenuMapper; +import com.aiotagro.cattletrade.business.mapper.SysUserMapper; +import com.aiotagro.cattletrade.business.service.ISysRoleService; +import com.aiotagro.common.core.utils.StringUtils; +import com.aiotagro.common.core.web.domain.AjaxResult; +import com.aiotagro.common.core.web.domain.PageResultResponse; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.github.pagehelper.Page; +import com.github.pagehelper.PageHelper; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.CollectionUtils; + +import java.util.Date; +import java.util.List; + +/** + *

+ * 岗位管理表 服务实现类 + *

+ * + * @author admin + * @since 2024-12-26 + */ +@Service +public class SysRoleServiceImpl extends ServiceImpl implements ISysRoleService { + + @Autowired + private SysUserMapper userMapper; + + @Autowired + private SysRoleMenuMapper roleMenuMapper; + + @Autowired + private SysMenuMapper menuMapper; + + @Override + public PageResultResponse queryList(SysRoleDto dto) { + Page result = PageHelper.startPage(dto.getPageNum(), dto.getPageSize()); + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(SysRole::getIsDelete, 0).orderByDesc(SysRole::getCreateTime); + if (StringUtils.hasText(dto.getName())) { + wrapper.like(SysRole::getName, dto.getName()); + } + List list = list(wrapper); + return new PageResultResponse(result.getTotal(), list); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public AjaxResult saveRole(SysRole role) { + if (role.getId() == null) { + save(role); + } else { + role.setUpdateTime(new Date()); + updateById(role); + } + + // 先删除岗位菜单 + roleMenuMapper.delete(Wrappers.query().lambda().eq(SysRoleMenu::getRoleId, role.getId())); + // 再新增 + for (Integer menuId : role.getMenuIds()) { + SysRoleMenu roleMenu = new SysRoleMenu(); + roleMenu.setRoleId(role.getId()); + roleMenu.setMenuId(menuId); + roleMenuMapper.insert(roleMenu); + } + + return AjaxResult.success(); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public AjaxResult delete(Integer id) { + SysRole role = getById(id); + if (null == role || role.getIsDelete() == 1) { + return AjaxResult.error("岗位不存在"); + } + List users = userMapper.selectList(Wrappers.query().lambda().eq(SysUser::getIsDelete, 0).eq(SysUser::getRoleId, id)); + if (!CollectionUtils.isEmpty(users)) { + return AjaxResult.error("此岗位关联员工,请移除员工后再删除"); + } + // 删除岗位/角色 + removeById(id); + + // 删除岗位/角色菜单 + roleMenuMapper.delete(Wrappers.query().lambda().eq(SysRoleMenu::getRoleId, id)); + return AjaxResult.success(); + } + + @Override + public AjaxResult getMenus(Integer id) { + SysRole role = null; + if (id != null) { + role = getById(id); + } + if (role == null) { + role = new SysRole(); + } + List menus = menuMapper.queryListByRoleId(id); + role.setMenuList(menus); + return AjaxResult.success(role); + } + +} diff --git a/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/service/impl/SysUserServiceImpl.java b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/service/impl/SysUserServiceImpl.java new file mode 100644 index 0000000..4c5996f --- /dev/null +++ b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/service/impl/SysUserServiceImpl.java @@ -0,0 +1,86 @@ +package com.aiotagro.cattletrade.business.service.impl; + +import cn.hutool.crypto.digest.DigestUtil; +import com.aiotagro.cattletrade.business.dto.SysUserDto; +import com.aiotagro.cattletrade.business.entity.SysUser; +import com.aiotagro.cattletrade.business.mapper.SysUserMapper; +import com.aiotagro.cattletrade.business.service.ISysUserService; +import com.aiotagro.common.core.constant.Constants; +import com.aiotagro.common.core.utils.SecurityUtil; +import com.aiotagro.common.core.web.domain.AjaxResult; +import com.aiotagro.common.core.web.domain.PageResultResponse; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.github.pagehelper.Page; +import com.github.pagehelper.PageHelper; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.CollectionUtils; +import com.aiotagro.common.core.utils.StringUtils; + +import java.util.List; + +/** + *

+ * 员工管理表 服务实现类 + *

+ * + * @author admin + * @since 2024-12-26 + */ +@Service +public class SysUserServiceImpl extends ServiceImpl implements ISysUserService { + + @Autowired(required = false) + private SysUserMapper sysUserMapper; + + @Override + public PageResultResponse queryList(SysUserDto dto) { + Page result = PageHelper.startPage(dto.getPageNum(), dto.getPageSize()); + List list = sysUserMapper.queryList(dto); + return new PageResultResponse(result.getTotal(), list); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public AjaxResult saveUser(SysUser user) { + List users = sysUserMapper.selectList(Wrappers.query().lambda().eq(SysUser::getIsDelete, 0).eq(SysUser::getMobile, user.getMobile())); + // 新增 + if (user.getId() == null) { + if (!CollectionUtils.isEmpty(users)) { + return AjaxResult.error("手机号已存在"); + } + if (StringUtils.isEmpty(user.getPassword())) { + user.setPassword("123456"); + } + user.setPassword(DigestUtil.md5Hex(Constants.USER_PASSWORD_PREFIX + user.getPassword())); + user.setCreateBy(SecurityUtil.getCurrentUserId()); + save(user); + } else { + // 编辑 + if (!CollectionUtils.isEmpty(users) && !users.get(0).getId().equals(user.getId())) { + return AjaxResult.error("手机号已存在"); + } + SysUser sysUser = getById(user.getId()); + sysUser.setName(user.getName()); + sysUser.setRoleId(user.getRoleId()); + sysUser.setMobile(user.getMobile()); + sysUser.setStatus(user.getStatus()); + // 传了新密码才更新 + if (StringUtils.isNotEmpty(user.getPassword())) { + sysUser.setPassword(DigestUtil.md5Hex(Constants.USER_PASSWORD_PREFIX + user.getPassword())); + } + sysUser.setUpdateBy(SecurityUtil.getCurrentUserId()); + updateById(sysUser); + } + return AjaxResult.success(); + } + + @Override + public AjaxResult delete(Integer id) { + removeById(id); + return AjaxResult.success(); + } + +} diff --git a/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/service/impl/TencentSmsCodeServiceImpl.java b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/service/impl/TencentSmsCodeServiceImpl.java new file mode 100644 index 0000000..05a7190 --- /dev/null +++ b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/service/impl/TencentSmsCodeServiceImpl.java @@ -0,0 +1,102 @@ +package com.aiotagro.cattletrade.business.service.impl; + +import com.aiotagro.cattletrade.business.service.TencentSmsCodeService; +import com.tencentcloudapi.common.Credential; +import com.tencentcloudapi.common.profile.ClientProfile; +import com.tencentcloudapi.common.profile.HttpProfile; +import com.tencentcloudapi.sms.v20190711.SmsClient; +import com.tencentcloudapi.sms.v20190711.models.SendSmsRequest; +import com.tencentcloudapi.sms.v20190711.models.SendSmsResponse; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; + +@Service +public class TencentSmsCodeServiceImpl implements TencentSmsCodeService { + + + @Value("${sms.secret-id}") + private String secretId; + @Value("${sms.secret-key}") + private String secretKey; + @Value("${sms.end-point}") + private String endPoint; + @Value("${sms.appid}") + private String appid; + @Value("${sms.sign}") + private String sign; + @Value("${sms.template-id}") + private String templateId; + + @Override + public String sendSmsCode(String mobile, String code) { + Credential cred = new Credential(secretId, secretKey); + // 实例化一个 http 选项,可选,无特殊需求时可以跳过 + HttpProfile httpProfile = new HttpProfile(); + /* SDK 默认使用 POST 方法。 + * 如需使用 GET 方法,可以在此处设置,但 GET 方法无法处理较大的请求 */ + httpProfile.setReqMethod("POST"); + /* SDK 有默认的超时时间,非必要请不要进行调整 + * 如有需要请在代码中查阅以获取最新的默认值 */ + httpProfile.setConnTimeout(60); + /* SDK 会自动指定域名,通常无需指定域名,但访问金融区的服务时必须手动指定域名 + * 例如 SMS 的上海金融区域名为 sms.ap-shanghai-fsi.tencentcloudapi.com */ + httpProfile.setEndpoint(endPoint); + + /* 非必要步骤: + * 实例化一个客户端配置对象,可以指定超时时间等配置 */ + ClientProfile clientProfile = new ClientProfile(); + /* SDK 默认用 TC3-HMAC-SHA256 进行签名 + * 非必要请不要修改该字段 */ + clientProfile.setSignMethod("HmacSHA256"); + clientProfile.setHttpProfile(httpProfile); + /* 实例化 SMS 的 client 对象 + * 第二个参数是地域信息,可以直接填写字符串 ap-guangzhou,或者引用预设的常量 */ + SmsClient client = new SmsClient(cred, "ap-guangzhou", clientProfile); + /* 实例化一个请求对象,根据调用的接口和实际情况,可以进一步设置请求参数 + * 您可以直接查询 SDK 源码确定接口有哪些属性可以设置 + * 属性可能是基本类型,也可能引用了另一个数据结构 + * 推荐使用 IDE 进行开发,可以方便地跳转查阅各个接口和数据结构的文档说明 */ + SendSmsRequest req = new SendSmsRequest(); + + /* 填充请求参数,这里 request 对象的成员变量即对应接口的入参 + * 您可以通过官网接口文档或跳转到 request 对象的定义处查看请求参数的定义 + * 基本类型的设置: + * 帮助链接: + * 短信控制台:https://console.cloud.tencent.com/smsv2 + * sms helper:https://cloud.tencent.com/document/product/382/3773 */ + + /* 短信应用 ID: 在 [短信控制台] 添加应用后生成的实际 SDKAppID,例如1400006666 */ + req.setSmsSdkAppid(appid); + + /* 短信签名内容: 使用 UTF-8 编码,必须填写已审核通过的签名,可登录 [短信控制台] 查看签名信息 */ + req.setSign(sign); + + /* 模板 ID: 必须填写已审核通过的模板 ID,可登录 [短信控制台] 查看模板 ID */ + req.setTemplateID(templateId); + + /* 下发手机号码,采用 e.164 标准,+[国家或地区码][手机号] + * 例如+8613711112222, 其中前面有一个+号 ,86为国家码,13711112222为手机号,最多不要超过200个手机号*/ + String[] phoneNumbers = {"+86" + mobile}; + req.setPhoneNumberSet(phoneNumbers); + + /* 模板参数: 若无模板参数,则设置为空*/ + String[] templateParams = {code}; + req.setTemplateParamSet(templateParams); + + try { + /* 通过 client 对象调用 SendSms 方法发起请求。注意请求方法名与请求对象是对应的 + * 返回的 res 是一个 SendSmsResponse 类的实例,与请求对象对应 */ + SendSmsResponse res = client.SendSms(req); + + // 输出 JSON 格式的字符串回包 + System.out.println(SendSmsResponse.toJsonString(res)); + + // 可以取出单个值,您可以通过官网接口文档或跳转到 response 对象的定义处查看返回字段的定义 + System.out.println(res.getRequestId()); + } catch (Exception e) { + return "发送失败"; + } + return null; + } + +} diff --git a/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/service/impl/WarningLogServiceImpl.java b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/service/impl/WarningLogServiceImpl.java new file mode 100644 index 0000000..99098a4 --- /dev/null +++ b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/service/impl/WarningLogServiceImpl.java @@ -0,0 +1,239 @@ +package com.aiotagro.cattletrade.business.service.impl; + +import com.aiotagro.cattletrade.business.dto.DeliveryQueryDto; +import com.aiotagro.cattletrade.business.dto.WarningDetailDto; +import com.aiotagro.cattletrade.business.entity.Delivery; +import com.aiotagro.cattletrade.business.entity.DeliveryDevice; +import com.aiotagro.cattletrade.business.entity.JbqServerLog; +import com.aiotagro.cattletrade.business.entity.WarningLog; +import com.aiotagro.cattletrade.business.mapper.DeliveryDeviceMapper; +import com.aiotagro.cattletrade.business.mapper.DeliveryMapper; +import com.aiotagro.cattletrade.business.mapper.JbqServerLogMapper; +import com.aiotagro.cattletrade.business.mapper.WarningLogMapper; +import com.aiotagro.cattletrade.business.service.IWarningLogService; +import com.aiotagro.cattletrade.business.vo.DeliveryLogVo; +import com.aiotagro.cattletrade.constant.WarningStatusAdminEnum; +import com.aiotagro.cattletrade.constant.WarningStatusEnum; +import com.aiotagro.common.core.utils.EnumUtil; +import com.aiotagro.common.core.utils.LngLonUtil; +import com.aiotagro.common.core.utils.SecurityUtil; +import com.aiotagro.common.core.utils.StringUtils; +import com.aiotagro.common.core.utils.uuid.DistinctUtil; +import com.aiotagro.common.core.web.domain.AjaxResult; +import com.aiotagro.common.core.web.domain.PageResultResponse; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.github.pagehelper.Page; +import com.github.pagehelper.PageHelper; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections4.CollectionUtils; +import org.springframework.beans.BeanUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.time.Duration; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.ZoneId; +import java.util.*; +import java.util.stream.Collectors; + +/** + *

+ * 预警记录表 服务实现类 + *

+ * + * @author admin + * @since 2024-12-26 + */ +@Service +@Slf4j +public class WarningLogServiceImpl extends ServiceImpl implements IWarningLogService { + + + @Autowired + private WarningLogMapper warningLogMapper; + @Autowired + private DeliveryMapper deliveryMapper; + @Autowired + private DeliveryDeviceMapper deliveryDeviceMapper; + @Autowired + private JbqServerLogMapper serverLogMapper; + + /** + * 预警统计 + * + * @return + */ + @Override + public AjaxResult warningCount() { + //获取当前登录人信息 + Integer currentUserId = SecurityUtil.getCurrentUserId(); + //返回结果集 + Map resMap = new HashMap<>(); + resMap.put("checkCount", 0); + resMap.put("rangeCount", 0); + DeliveryQueryDto deliveryQueryDto = new DeliveryQueryDto(); + deliveryQueryDto.setUserId(currentUserId); + //查询最新的一条上报数据的预警记录结果 + List newWarningLog = warningLogMapper.getNewWarningLog(deliveryQueryDto); + if (CollectionUtils.isNotEmpty(newWarningLog)) { + //数量盘点预警 + long checkCount = newWarningLog.stream().filter(warningLog -> "2".equals(warningLog.getWarningType())).count(); + long rangeCount = newWarningLog.stream().filter(warningLog -> "3".equals(warningLog.getWarningType())).count(); + resMap.put("checkCount", checkCount); + resMap.put("rangeCount", rangeCount); + } + return AjaxResult.success("查询成功", resMap); + } + + @Override + public PageResultResponse queryList(DeliveryQueryDto dto) { + //返回结果集对象 + List resList = new ArrayList<>(); + Page result = PageHelper.startPage(dto.getPageNum(), dto.getPageSize()); + dto.setUserId(SecurityUtil.getCurrentUserId());//当前登录人 + List newWarningLog = warningLogMapper.getNewWarningLog(dto); + if (CollectionUtils.isNotEmpty(newWarningLog)) { + List deliveryIds = newWarningLog.stream().map(WarningLog::getDeliveryId).collect(Collectors.toList()); + //根据运单id查询运单的信息 + List deliveryList = deliveryMapper.selectBatchIds(deliveryIds); + if (CollectionUtils.isNotEmpty(deliveryList)) { + Map deliveryMap = deliveryList.stream().collect(Collectors.toMap(Delivery::getId, delivery -> delivery)); + newWarningLog.forEach(warningLog -> { + Delivery delivery = deliveryMap.get(warningLog.getDeliveryId()); + if (delivery != null) { + DeliveryLogVo deliveryLogVo = new DeliveryLogVo(); + deliveryLogVo.setId(warningLog.getId()); + deliveryLogVo.setLicensePlate(delivery.getLicensePlate()); + deliveryLogVo.setDeliveryId(warningLog.getDeliveryId()); + deliveryLogVo.setDeliveryNumber(delivery.getDeliveryNumber()); + deliveryLogVo.setStatus(delivery.getStatus()); + deliveryLogVo.setStatusDesc(2 == delivery.getStatus() ? "已核验" : "待核验"); + deliveryLogVo.setCarFrontPhoto(delivery.getCarFrontPhoto()); + deliveryLogVo.setRegisteredJbqCount(delivery.getRegisteredJbqCount()); + deliveryLogVo.setInventoryJbqCount(warningLog.getInventoryJbqCount()); + deliveryLogVo.setActualDistance(warningLog.getActualDistance()); + deliveryLogVo.setExpectedDistance(warningLog.getExpectedDistance()); + String warningType = warningLog.getWarningType(); + deliveryLogVo.setWarningType(warningType); + if (StringUtils.isNotEmpty(warningType)) { + deliveryLogVo.setWarningTypeDesc(EnumUtil.getEnumConstant(WarningStatusAdminEnum.class , Integer.parseInt(warningType)).getDescription()); + } + deliveryLogVo.setWarningTime(warningLog.getWarningTime()); + resList.add(deliveryLogVo); + } + }); + } + } + return new PageResultResponse(result.getTotal(), resList); + } + + @Override + public AjaxResult warningDetail(Integer id) { + WarningDetailDto warningDetailDto = new WarningDetailDto(); + //预警记录信息 + WarningLog warningLog = warningLogMapper.selectById(id); + BeanUtils.copyProperties(warningLog, warningDetailDto); + String warningType = warningLog.getWarningType(); + if (StringUtils.isNotEmpty(warningType)) { + warningDetailDto.setWarningReason(EnumUtil.getEnumConstant(WarningStatusEnum.class , Integer.parseInt(warningType)).getDescription()); + } + //获取当前记录的运单信息 + Delivery delivery = deliveryMapper.selectById(warningLog.getDeliveryId()); + BeanUtils.copyProperties(delivery, warningDetailDto); + warningDetailDto.setWarningTime(warningLog.getWarningTime()); + warningDetailDto.setWarningType(warningType); + warningDetailDto.setInventoryJbqCount(warningLog.getInventoryJbqCount()); + //获取当前运单关联的设备信息 + List deliveryDevices = deliveryDeviceMapper.selectList(new LambdaQueryWrapper().eq(DeliveryDevice::getDeliveryId, delivery.getId())); + if (CollectionUtils.isNotEmpty(deliveryDevices)) { + List serverCollect = deliveryDevices.stream().filter(deliveryDevice -> deliveryDevice.getDeviceType() == 1).map(DeliveryDevice::getDeviceId).collect(Collectors.toList()); + if (CollectionUtils.isNotEmpty(serverCollect)) { + warningDetailDto.setServerDeviceSn(serverCollect.get(0)); + } + List jbqDeviceCollect = deliveryDevices.stream().filter(deliveryDevice -> deliveryDevice.getDeviceType() == 2).map(DeliveryDevice::getDeviceId).collect(Collectors.toList()); + warningDetailDto.setJbqDeviceSn(jbqDeviceCollect); + } + return AjaxResult.success(warningDetailDto); + } + + @Override + public AjaxResult savaWarn(String deviceId) { + // 查询主机关联的最新运单 + List list = deliveryDeviceMapper.selectList(new LambdaQueryWrapper().eq(DeliveryDevice::getDeviceId, deviceId) + .orderByDesc(DeliveryDevice::getId)); + // 主机未绑定运单 无需操作 + if (CollectionUtils.isEmpty(list)) { + return AjaxResult.success(); + } + Delivery delivery = deliveryMapper.selectById(list.get(0).getDeliveryId()); + // 运单已核验 无需操作 + if (delivery.getStatus() == 2) { + return AjaxResult.success(); + } + + // 查询运单创建后的主机上报日志 + List logs = serverLogMapper.selectList(new LambdaQueryWrapper().eq(JbqServerLog::getDeviceId, deviceId) + .ge(JbqServerLog::getCreateTime, delivery.getCreateTime()).orderByDesc(JbqServerLog::getCreateTime)); + LocalDateTime endTime = logs.get(0).getCreateTime().toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime(); + LocalDateTime createTime; + Double endLat = Double.parseDouble(logs.get(0).getLatitude()); + Double endLon = Double.parseDouble(logs.get(0).getLongitude()); + // 转换成高德经纬度 + double[] gcjDouble = LngLonUtil.gps84_To_Gcj02(endLat, endLon); + endLat = gcjDouble[0]; + endLon = gcjDouble[1]; + Double startLat; + Double startLon; + if (logs.size() == 1) { + // 第一次上报 计算上报时间到运单创建时间的时间段 + createTime = delivery.getCreateTime().toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime(); + // 腾讯地图经纬度无需转换 + startLat = Double.parseDouble(delivery.getStartLat()); + startLon = Double.parseDouble(delivery.getStartLon()); + } else { + // 多次上报 计算上报时间到上一次上报时间的时间段 + createTime = logs.get(1).getCreateTime().toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime(); + startLat = Double.parseDouble(logs.get(1).getLatitude()); + startLon = Double.parseDouble(logs.get(1).getLongitude()); + // 转换成高德经纬度 + double[] gcjDouble1 = LngLonUtil.gps84_To_Gcj02(startLat, startLon); + startLat = gcjDouble1[0]; + startLon = gcjDouble1[1]; + } + + // 计算时间差,单位为小时 + double hoursBetween = Duration.between(createTime, endTime).toMinutes() / 60.0; + // 确定当前上报时间是在白天还是晚上 + boolean isDayTime = !endTime.toLocalTime().isBefore(LocalTime.of(8, 0)) && + !endTime.toLocalTime().isAfter(LocalTime.of(20, 0)); + // 计算基准距离 + double baseDistance = isDayTime ? (80 * hoursBetween * 0.9) : (80 * hoursBetween * 0.8); + // 四舍五入 保留3位小数 + baseDistance = BigDecimal.valueOf(baseDistance).setScale(3, RoundingMode.HALF_UP).doubleValue(); + // 计算本次上报点 和上次上报点之间的经纬度导航实际距离 km + try { + double distance = DistinctUtil.calculateDistance(startLat, startLon, endLat, endLon); + // 小于基准距离 发起预警 + WarningLog log = new WarningLog(); + log.setWarningTime(new Date()); + if (distance < baseDistance) { + log.setWarningType("3"); + } else { + log.setWarningType("1"); + } + log.setDeliveryId(delivery.getId()); + log.setActualDistance(String.valueOf(distance)); + log.setExpectedDistance(String.valueOf(baseDistance)); + save(log); + } catch (Exception e) { + log.error("请求高德api计算距离失败:", e); + return AjaxResult.error("请求高德api计算距离失败"); + } + return AjaxResult.success(); + } + +} diff --git a/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/service/impl/XqClientServiceImpl.java b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/service/impl/XqClientServiceImpl.java new file mode 100644 index 0000000..3fb7b93 --- /dev/null +++ b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/service/impl/XqClientServiceImpl.java @@ -0,0 +1,294 @@ +package com.aiotagro.cattletrade.business.service.impl; + +import com.aiotagro.cattletrade.business.entity.DeliveryDevice; +import com.aiotagro.cattletrade.business.entity.XqClient; +import com.aiotagro.cattletrade.business.mapper.DeliveryDeviceMapper; +import com.aiotagro.cattletrade.business.mapper.XqClientMapper; +import com.aiotagro.cattletrade.business.service.IXqClientService; +import com.aiotagro.common.core.web.domain.AjaxResult; +import com.aiotagro.common.core.web.domain.PageResultResponse; +import com.aiotagro.common.core.utils.LngLonUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.github.pagehelper.Page; +import com.github.pagehelper.PageHelper; +import org.springframework.stereotype.Service; +import com.aiotagro.common.core.utils.StringUtils; +import java.text.SimpleDateFormat; +import java.util.Date; + +import javax.annotation.Resource; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * 项圈客户端服务实现 + * + * @author System + * @date 2025-01-16 + */ +@Service +public class XqClientServiceImpl extends ServiceImpl implements IXqClientService { + + @Resource + private XqClientMapper xqClientMapper; + + @Resource + private DeliveryDeviceMapper deliveryDeviceMapper; + + @Override + public PageResultResponse xqList(Map params) { + Integer pageNum = params.get("pageNum") != null ? (Integer) params.get("pageNum") : 1; + Integer pageSize = params.get("pageSize") != null ? (Integer) params.get("pageSize") : 10; + String sn = (String) params.get("sn"); + String startNo = (String) params.get("startNo"); + String endNo = (String) params.get("endNo"); + Boolean unassignedOnly = (Boolean) params.get("unassignedOnly"); + + Page> result = PageHelper.startPage(pageNum, pageSize); + + try { + List> list; + if (unassignedOnly != null && unassignedOnly) { + // 查询未分配的智能项圈 + System.out.println("查询未分配的智能项圈,参数: " + params); + list = xqClientMapper.selectUnassignedXqClientList(sn, startNo, endNo); + System.out.println("未分配智能项圈查询结果数量: " + (list != null ? list.size() : 0)); + } else { + // 使用关联查询获取完整信息 + System.out.println("查询所有智能项圈,参数: " + params); + list = xqClientMapper.selectXqClientListWithRelations(sn, startNo, endNo); + System.out.println("所有智能项圈查询结果数量: " + (list != null ? list.size() : 0)); + } + + return new PageResultResponse(result.getTotal(), list); + } catch (Exception e) { + System.out.println("查询智能项圈失败: " + e.getMessage()); + e.printStackTrace(); + return new PageResultResponse(0L, new ArrayList<>()); + } + } + + @Override + public AjaxResult getDevicesByDeliveryId(Integer deliveryId, Integer deviceType) { + System.out.println("=== 查询项圈设备,deliveryId: " + deliveryId + ", deviceType: " + deviceType); + + if (deliveryId == null) { + return AjaxResult.error("运送清单ID不能为空"); + } + + // 查询运送清单关联的设备 + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(DeliveryDevice::getDeliveryId, deliveryId); + wrapper.eq(DeliveryDevice::getDeviceType, deviceType); + List deliveryDevices = deliveryDeviceMapper.selectList(wrapper); + + System.out.println("=== 查询到delivery_device记录数: " + deliveryDevices.size()); + if (!deliveryDevices.isEmpty()) { + System.out.println("=== delivery_device记录详情: " + deliveryDevices); + } + + if (deliveryDevices.isEmpty()) { + return AjaxResult.success(new ArrayList<>()); + } + + // 获取设备ID列表 + List deviceIds = deliveryDevices.stream() + .map(DeliveryDevice::getDeviceId) + .collect(Collectors.toList()); + + // 查询设备详细信息 + LambdaQueryWrapper deviceWrapper = new LambdaQueryWrapper<>(); + if (deviceType != null && deviceType == 3) { + // 项圈:历史上有的存 deviceId,有的存 sn,需要兼容两种字段 + deviceWrapper.and(w -> w.in(XqClient::getDeviceId, deviceIds) + .or() + .in(XqClient::getSn, deviceIds)); + } else { + // 耳标:部分库没有扩展字段(delivery_number、license_plate),避免 select * 触发未知列错误 + deviceWrapper.in(XqClient::getDeviceId, deviceIds) + .select(XqClient::getId, + XqClient::getDeviceId, + XqClient::getSn, + XqClient::getState, + XqClient::getLongitude, + XqClient::getLatitude, + XqClient::getAltitude, + XqClient::getGpsState, + XqClient::getNsat, + XqClient::getRsrp, + XqClient::getBattery, + XqClient::getTemperature, + XqClient::getSteps, + XqClient::getAccX, + XqClient::getAccY, + XqClient::getAccZ, + XqClient::getBandgeStatus, + XqClient::getVer, + XqClient::getTime, + XqClient::getUptime, + XqClient::getYSteps, + XqClient::getIsWear, + XqClient::getIsTemperature, + XqClient::getSourceId, + XqClient::getLoctime, + XqClient::getSubType, + XqClient::getFrequency, + XqClient::getVehicleId, + XqClient::getEveningFrequency, + XqClient::getTenantId, + XqClient::getDeliveryId, + XqClient::getDeliveryNumber, + XqClient::getLicensePlate); + } + List devices = xqClientMapper.selectList(deviceWrapper); + + System.out.println("=== 查询到的项圈设备数量: " + devices.size()); + for (XqClient device : devices) { + System.out.println("=== 项圈设备: " + device); + } + + // 返回分页格式数据 + Map result = new HashMap<>(); + result.put("rows", devices); + result.put("total", devices.size()); + + return AjaxResult.success(result); + } + + @Override + public AjaxResult getCollarLocation(String deliveryId, String xqDeviceId) { + if (StringUtils.isEmpty(xqDeviceId)) { + return AjaxResult.error("项圈设备ID不能为空"); + } + + // 查询项圈设备信息 - 使用selectList避免TooManyResultsException + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + // 兼容老数据:有的记录存 deviceId,有的使用 sn 作为设备唯一编号 + wrapper.and(w -> w.eq(XqClient::getDeviceId, xqDeviceId) + .or() + .eq(XqClient::getSn, xqDeviceId)); + wrapper.orderByDesc(XqClient::getUptime); // 按更新时间倒序,取最新的记录 + List collars = xqClientMapper.selectList(wrapper); + + if (collars.isEmpty()) { + return AjaxResult.error("未找到对应的项圈设备信息"); + } + + // 取第一条记录(最新的) + XqClient collar = collars.get(0); + + // 获取设备的经纬度 + String latitude = collar.getLatitude(); + String longitude = collar.getLongitude(); + + // 判断经纬度是否为空或为0 + if (isInvalidLocation(latitude, longitude)) { + return AjaxResult.error("暂无定位信息"); + } + + // 转换为百度经纬度 + double lat = Double.parseDouble(latitude); + double lon = Double.parseDouble(longitude); + double[] baiduLocation = LngLonUtil.gps84_To_bd09(lat, lon); + + // 构建返回数据 + Map locationData = new java.util.HashMap<>(); + locationData.put("deviceId", xqDeviceId); + locationData.put("latitude", String.valueOf(baiduLocation[0])); + locationData.put("longitude", String.valueOf(baiduLocation[1])); + locationData.put("altitude", collar.getAltitude()); + locationData.put("battery", collar.getBattery()); + locationData.put("temperature", collar.getTemperature()); + locationData.put("updateTime", collar.getUptime()); + locationData.put("gpsState", collar.getGpsState()); + locationData.put("nsat", collar.getNsat()); + + return AjaxResult.success(locationData); + } + + @Override + public AjaxResult getCollarTrack(String deliveryId, String xqDeviceId, String trackTime, String trackEndTime) { + if (StringUtils.isEmpty(xqDeviceId)) { + return AjaxResult.error("项圈设备ID不能为空"); + } + + try { + // 构建查询条件 + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(XqClient::getDeviceId, xqDeviceId); + wrapper.orderByAsc(XqClient::getUptime); // 按时间正序排列 + + // 添加时间范围条件 + if (StringUtils.isNotEmpty(trackTime) && StringUtils.isNotEmpty(trackEndTime)) { + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + Date startTime = sdf.parse(trackTime); + Date endTime = sdf.parse(trackEndTime); + wrapper.between(XqClient::getUptime, startTime, endTime); + } + + // 查询轨迹数据 + List trackData = xqClientMapper.selectList(wrapper); + + if (trackData.isEmpty()) { + return AjaxResult.error("暂无轨迹数据"); + } + + // 构建返回数据 + List> trackList = new ArrayList<>(); + for (XqClient collar : trackData) { + if (!isInvalidLocation(collar.getLatitude(), collar.getLongitude())) { + // 转换为百度经纬度 + double lat = Double.parseDouble(collar.getLatitude()); + double lon = Double.parseDouble(collar.getLongitude()); + double[] baiduLocation = LngLonUtil.gps84_To_bd09(lat, lon); + + Map trackPoint = new java.util.HashMap<>(); + trackPoint.put("longitude", String.valueOf(baiduLocation[1])); + trackPoint.put("latitude", String.valueOf(baiduLocation[0])); + trackPoint.put("altitude", collar.getAltitude()); + trackPoint.put("updateTime", collar.getUptime()); + trackPoint.put("battery", collar.getBattery()); + trackPoint.put("temperature", collar.getTemperature()); + trackList.add(trackPoint); + } + } + + if (trackList.isEmpty()) { + return AjaxResult.error("暂无有效轨迹数据"); + } + + return AjaxResult.success(trackList); + } catch (Exception e) { + e.printStackTrace(); + return AjaxResult.error("查询轨迹失败: " + e.getMessage()); + } + } + + @Override + public AjaxResult getCollarDeviceTrack(String deliveryId, String xqDeviceId, String trackTime, String trackEndTime) { + // 设备轨迹查询逻辑与项圈轨迹相同 + return getCollarTrack(deliveryId, xqDeviceId, trackTime, trackEndTime); + } + + /** + * 判断位置信息是否无效 + */ + private boolean isInvalidLocation(String latitude, String longitude) { + if (StringUtils.isEmpty(latitude) || StringUtils.isEmpty(longitude)) { + return true; + } + + try { + double lat = Double.parseDouble(latitude); + double lon = Double.parseDouble(longitude); + return lat == 0.0 && lon == 0.0; + } catch (NumberFormatException e) { + return true; + } + } +} + diff --git a/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/utils/DataScopeUtil.java b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/utils/DataScopeUtil.java new file mode 100644 index 0000000..84b7a7b --- /dev/null +++ b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/utils/DataScopeUtil.java @@ -0,0 +1,59 @@ +package com.aiotagro.cattletrade.business.utils; + +import com.aiotagro.common.core.utils.SecurityUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.support.SFunction; + +/** + * 数据权限工具类 + * + * @author System + * @date 2025-10-11 + */ +public class DataScopeUtil { + + /** + * 应用数据权限过滤(基于创建人) + * 超级管理员不过滤,普通用户只能看自己创建的数据 + * + * @param wrapper 查询条件包装器 + * @param createdByField 创建人字段 + * @param 实体类型 + */ + public static void applyDataScope(LambdaQueryWrapper wrapper, + SFunction createdByField) { + // 超级管理员不过滤 + if (SecurityUtil.isSuperAdmin()) { + return; + } + + // 普通用户只能查看自己创建的数据 + Integer currentUserId = SecurityUtil.getCurrentUserId(); + wrapper.eq(createdByField, currentUserId); + } + + /** + * 应用数据权限过滤(基于创建人或核验人) + * 用户可以看到自己创建或自己核验的数据 + * + * @param wrapper 查询条件包装器 + * @param createdByField 创建人字段 + * @param checkByField 核验人字段 + * @param 实体类型 + */ + public static void applyDataScopeWithChecker(LambdaQueryWrapper wrapper, + SFunction createdByField, + SFunction checkByField) { + // 超级管理员不过滤 + if (SecurityUtil.isSuperAdmin()) { + return; + } + + // 普通用户可以查看自己创建或自己核验的数据 + Integer currentUserId = SecurityUtil.getCurrentUserId(); + wrapper.and(w -> w.eq(createdByField, currentUserId) + .or() + .eq(checkByField, currentUserId)); + } +} + diff --git a/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/vo/DeliveryLogVo.java b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/vo/DeliveryLogVo.java new file mode 100644 index 0000000..88d8a5c --- /dev/null +++ b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/vo/DeliveryLogVo.java @@ -0,0 +1,86 @@ +package com.aiotagro.cattletrade.business.vo; + +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; + +import java.util.Date; + +@Data +public class DeliveryLogVo { + + + /** + * 预警记录主键ID + */ + private Integer id; + /** + * 运单Id + */ + private Integer deliveryId; + /** + * 车头照片 + */ + private String carFrontPhoto; + + /** + * 运单号 + */ + private String deliveryNumber; + + /** + * 车牌号 + */ + private String licensePlate; + + /** + * 登记智能耳标数 + */ + private Integer registeredJbqCount; + + /** + * 车内盘点耳标数量 + */ + private Integer inventoryJbqCount; + + /** + * 预警类型, 1-正常 2-盘点预警 3-距离预警 + */ + private String warningType; + + /** + * 预警类型描述, 1-正常 2-盘点预警 3-距离预警 + */ + private String warningTypeDesc; + /** + * 预警时间 + */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + private Date warningTime; + + /** + * 状态,1-待核验,2-已核验 + */ + private Integer status; + + /** + * 状态,1-待核验,2-已核验 + */ + private String statusDesc; + + /** + * 预计形式距离 + */ + private String expectedDistance; + + /** + * 实际行驶距离 + */ + private String actualDistance; + + /** + * 创建人名称 + */ + private String createByDesc; + + +} diff --git a/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/vo/JbqClientVo.java b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/vo/JbqClientVo.java new file mode 100644 index 0000000..c880517 --- /dev/null +++ b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/vo/JbqClientVo.java @@ -0,0 +1,47 @@ +package com.aiotagro.cattletrade.business.vo; + +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; + +import java.util.Date; + +@Data +public class JbqClientVo { + + /** + * 唯一标识 + */ + private Integer id; + + /** + * 耳标编号 + */ + private String deviceId; + + /** + * 设备电量 + */ + private String deviceVoltage; + + /** + * 设备温度 + */ + private String deviceTemp; + + /** + * 运单号 + */ + private String deliveryNumber; + + /** + * 车牌号 + */ + private String licensePlate; + + /** + * 绑定时间 + */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + private Date deliveryCreateTime; + +} \ No newline at end of file diff --git a/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/vo/ServerClientVo.java b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/vo/ServerClientVo.java new file mode 100644 index 0000000..f3c4ed3 --- /dev/null +++ b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/vo/ServerClientVo.java @@ -0,0 +1,50 @@ +package com.aiotagro.cattletrade.business.vo; + +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; + +import java.util.Date; + +@Data +public class ServerClientVo { + /** + * 唯一标识 + */ + private Integer id; + + /** + * 耳标编号 + */ + private String deviceId; + + /** + * 设备电量 + */ + private String deviceVoltage; + + /** + * 设备温度 + */ + private String deviceTemp; + + /** + * 运单号 + */ + private String deliveryNumber; + + /** + * 车牌号 + */ + private String licensePlate; + + /** + * 绑定时间 + */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + private Date deliveryCreateTime; + + /** + * 联网状态描述 + */ + private String onlineStatusDesc; +} diff --git a/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/vo/ServerLocationVo.java b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/vo/ServerLocationVo.java new file mode 100644 index 0000000..0e3964f --- /dev/null +++ b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/vo/ServerLocationVo.java @@ -0,0 +1,29 @@ +package com.aiotagro.cattletrade.business.vo; + +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; + +import java.util.Date; + +@Data +public class ServerLocationVo { + /** + * 纬度 + */ + private String latitude; + + /** + * 经度 + */ + private String longitude; + + /** + * 主机编号 + */ + private String deviceId; + /** + * 更新时间 + */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + private Date updateTime; +} diff --git a/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/config/CustomStpLogic.java b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/config/CustomStpLogic.java new file mode 100644 index 0000000..a4d128b --- /dev/null +++ b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/config/CustomStpLogic.java @@ -0,0 +1,115 @@ +package com.aiotagro.cattletrade.config; + +import cn.dev33.satoken.stp.StpLogic; +import com.aiotagro.common.core.constant.RoleConstants; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +import java.util.List; + +/** + * 自定义 StpLogic,支持通配符权限匹配 + */ +@Slf4j +@Component +public class CustomStpLogic extends StpLogic { + + public CustomStpLogic() { + super("login"); + } + + /** + * 重写权限校验方法,支持通配符匹配 + * + * @param permission 需要校验的权限码 + * @return 是否拥有该权限 + */ + @Override + public boolean hasPermission(String permission) { + try { + // 获取当前用户的权限列表 + List permissionList = getPermissionList(); + + // 如果权限列表为空,返回false + if (permissionList == null || permissionList.isEmpty()) { + log.debug("权限列表为空,permission={}", permission); + return false; + } + + // 检查是否有通配符权限 *:*:* + if (permissionList.contains(RoleConstants.ALL_PERMISSION)) { + log.debug("拥有通配符权限 *:*:*,直接放行,permission={}", permission); + return true; + } + + // 检查是否有精确匹配的权限 + if (permissionList.contains(permission)) { + log.debug("精确匹配权限,permission={}", permission); + return true; + } + + // 支持通配符匹配,例如:system:* 可以匹配 system:user:list + for (String userPermission : permissionList) { + if (matchPermission(userPermission, permission)) { + log.debug("通配符匹配成功,userPermission={}, requirePermission={}", userPermission, permission); + return true; + } + } + + log.debug("权限校验失败,permission={}, userPermissions={}", permission, permissionList); + return false; + + } catch (Exception e) { + log.error("权限校验异常: {}", e.getMessage(), e); + return false; + } + } + + /** + * 通配符权限匹配 + * + * @param pattern 权限模式(可能包含*) + * @param permission 需要匹配的权限 + * @return 是否匹配 + */ + private boolean matchPermission(String pattern, String permission) { + // 如果完全相等 + if (pattern.equals(permission)) { + return true; + } + + // 如果是通配符 *:*:* + if ("*:*:*".equals(pattern) || "*".equals(pattern)) { + return true; + } + + // 分割权限字符串 + String[] patternParts = pattern.split(":"); + String[] permissionParts = permission.split(":"); + + // 如果模式部分数量大于权限部分,肯定不匹配 + if (patternParts.length > permissionParts.length) { + return false; + } + + // 逐级匹配 + for (int i = 0; i < patternParts.length; i++) { + String patternPart = patternParts[i]; + String permissionPart = permissionParts[i]; + + // 如果是*,匹配任意 + if ("*".equals(patternPart)) { + return true; // 后续所有部分都匹配 + } + + // 如果不相等,不匹配 + if (!patternPart.equals(permissionPart)) { + return false; + } + } + + // 如果模式部分全部匹配,且模式长度等于权限长度,则匹配 + return patternParts.length == permissionParts.length; + } +} + diff --git a/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/config/DruidDataSourceConfig.java b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/config/DruidDataSourceConfig.java new file mode 100644 index 0000000..7131af3 --- /dev/null +++ b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/config/DruidDataSourceConfig.java @@ -0,0 +1,35 @@ +package com.aiotagro.cattletrade.config; + +import com.alibaba.druid.pool.DruidDataSource; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import javax.sql.DataSource; + +/** + * @author Carson + * @package_name com.aiotagro.payinfo.config + * @date 2024/2/6 17:00 + */ +@Configuration +public class DruidDataSourceConfig { + + //编写方法,注入DruidDataSource + //为什么我们注入自己的DataSource , 默认的HiKariDatasource失效? + //1. 默认的数据源是如配置? @ConditionalOnMissingBean({ DataSource.class, XADataSource.class }) + // 解读通过@ConditionalOnMissingBean({ DataSource.class}) 判断如果容器有DataSource Bean 就不注入默认的HiKariDatasource + @ConfigurationProperties("spring.datasource") + @Bean + public DataSource dataSource() { + //1. 配置了 @ConfigurationProperties("spring.datasource") + // 就可以读取到application.yml的配置,注意:我们需要将bean注入到spring ioc容器中、bean中提供get\set方法 + //2. 我们就不需要调用DruidDataSource 对象的setXxx, 会自动关联 + + DruidDataSource druidDataSource = new DruidDataSource(); + //druidDataSource.setUrl(); + //druidDataSource.setUsername(); + //druidDataSource.setPassword(); + return druidDataSource; + } +} diff --git a/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/config/LogRecordConfiguration.java b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/config/LogRecordConfiguration.java new file mode 100644 index 0000000..4ad769c --- /dev/null +++ b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/config/LogRecordConfiguration.java @@ -0,0 +1,22 @@ +package com.aiotagro.cattletrade.config; + +import com.aiotagro.common.core.utils.SecurityUtil; +import com.mzt.logapi.beans.Operator; +import com.mzt.logapi.service.IOperatorGetService; +import org.springframework.stereotype.Service; + +/** + * @author Carson + * @package_name com.aiotagro.payinfo.config + * @date 2024/2/7 14:22 + */ +@Service +public class LogRecordConfiguration implements IOperatorGetService { + + @Override + public Operator getUser() { + Operator e = new Operator(); + e.setOperatorId(SecurityUtil.getUserId().toString()); + return e; + } +} diff --git a/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/config/MybatisPlusConfig.java b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/config/MybatisPlusConfig.java new file mode 100644 index 0000000..36e53fc --- /dev/null +++ b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/config/MybatisPlusConfig.java @@ -0,0 +1,69 @@ +package com.aiotagro.cattletrade.config; + +import com.baomidou.mybatisplus.annotation.DbType; +import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; +import com.baomidou.mybatisplus.extension.plugins.inner.BlockAttackInnerInterceptor; +import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor; +import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; +import lombok.AllArgsConstructor; +import org.mybatis.spring.annotation.MapperScan; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.transaction.annotation.EnableTransactionManagement; + +/** + * @author Carson + * @package_name com.aiotagro.payinfo.config + * @date 2024/2/6 14:23 + */ +@EnableTransactionManagement(proxyTargetClass = true) +@Configuration +@AllArgsConstructor +@MapperScan("com.aiotagro.cattletrade.business.mapper") +public class MybatisPlusConfig { + + + + @Bean + public MybatisPlusInterceptor mybatisPlusInterceptor() { + MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); + + // 分页插件(ps:如果项目中有用到分页插件可以添加如下这行代码,但是必须要写在多租户插件后面) + interceptor.addInnerInterceptor(paginationInnerInterceptor()); + // 乐观锁插件 + interceptor.addInnerInterceptor(optimisticLockerInnerInterceptor()); + // 阻断插件 + interceptor.addInnerInterceptor(blockAttackInnerInterceptor()); + return interceptor; + } + + + /** + * 分页插件,自动识别数据库类型 https://baomidou.com/guide/interceptor-pagination.html + */ + public PaginationInnerInterceptor paginationInnerInterceptor() { + PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor(); + // 设置数据库类型为mysql + paginationInnerInterceptor.setDbType(DbType.MYSQL); + // 设置最大单页限制数量,默认 500 条,-1 不受限制 + paginationInnerInterceptor.setMaxLimit(-1L); + return paginationInnerInterceptor; + } + + /** + * 乐观锁插件 https://baomidou.com/guide/interceptor-optimistic-locker.html + */ + public OptimisticLockerInnerInterceptor optimisticLockerInnerInterceptor() { + return new OptimisticLockerInnerInterceptor(); + } + + /** + * 如果是对全表的删除或更新操作,就会终止该操作 https://baomidou.com/guide/interceptor-block-attack.html + */ + public BlockAttackInnerInterceptor blockAttackInnerInterceptor() { + return new BlockAttackInnerInterceptor(); + } + + + +} diff --git a/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/config/SaTokenConfig.java b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/config/SaTokenConfig.java new file mode 100644 index 0000000..cc4697c --- /dev/null +++ b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/config/SaTokenConfig.java @@ -0,0 +1,42 @@ +package com.aiotagro.cattletrade.config; + +import cn.dev33.satoken.dao.SaTokenDao; +import cn.dev33.satoken.dao.SaTokenDaoDefaultImpl; +import cn.dev33.satoken.stp.StpLogic; +import cn.dev33.satoken.stp.StpUtil; +import lombok.extern.slf4j.Slf4j; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Primary; + +/** + * Sa-Token配置类 + */ +@Slf4j +@Configuration +public class SaTokenConfig { + + /** + * 注册自定义的StpLogic + */ + @Bean + @Primary + public StpLogic getStpLogic() { + CustomStpLogic customStpLogic = new CustomStpLogic(); + log.info("注册自定义StpLogic,支持通配符权限匹配"); + // 将自定义的StpLogic设置为默认的StpLogic + StpUtil.setStpLogic(customStpLogic); + return customStpLogic; + } + + /** + * 强制使用内存存储替代Redis + */ + @Bean + @Primary + public SaTokenDao saTokenDao() { + log.info("强制使用内存存储替代Redis"); + return new SaTokenDaoDefaultImpl(); + } +} + diff --git a/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/config/StpInterfaceImpl.java b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/config/StpInterfaceImpl.java new file mode 100644 index 0000000..0aa08dd --- /dev/null +++ b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/config/StpInterfaceImpl.java @@ -0,0 +1,99 @@ +package com.aiotagro.cattletrade.config; + +import cn.dev33.satoken.stp.StpInterface; +import cn.dev33.satoken.stp.StpUtil; +import com.aiotagro.common.core.constant.RoleConstants; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * Sa-Token 权限验证接口实现 + * + * @author System + * @date 2025-01-16 + */ +@Slf4j +@Component +public class StpInterfaceImpl implements StpInterface { + + /** + * 返回一个账号所拥有的权限码集合 + */ + @Override + public List getPermissionList(Object loginId, String loginType) { + try { + // 从session中获取角色ID + Object roleIdObj = StpUtil.getTokenSession().get("roleId"); + if (roleIdObj == null) { + log.warn("用户 {} 的roleId为空", loginId); + return Collections.emptyList(); + } + + Integer roleId = Integer.parseInt(roleIdObj.toString()); + + // 判断是否是超级管理员 + if (roleId.equals(RoleConstants.SUPER_ADMIN_ROLE_ID)) { + log.info("用户 {} 是超级管理员,拥有所有权限", loginId); + // 超级管理员返回通配符权限,表示拥有所有权限 + return Collections.singletonList(RoleConstants.ALL_PERMISSION); + } + + // 从session中获取权限列表 + Object permissionsObj = StpUtil.getTokenSession().get("permissions"); + if (permissionsObj != null) { + @SuppressWarnings("unchecked") + List permissions = (List) permissionsObj; + log.info("用户 {} 拥有 {} 个权限", loginId, permissions.size()); + return permissions; + } + + log.warn("用户 {} 的permissions为空", loginId); + return Collections.emptyList(); + + } catch (Exception e) { + log.error("获取用户权限失败: {}", e.getMessage(), e); + return Collections.emptyList(); + } + } + + /** + * 返回一个账号所拥有的角色标识集合 (权限与角色可分开校验) + */ + @Override + public List getRoleList(Object loginId, String loginType) { + try { + // 从session中获取角色编码 + Object roleCodeObj = StpUtil.getTokenSession().get("roleCode"); + if (roleCodeObj != null) { + String roleCode = roleCodeObj.toString(); + List roleList = new ArrayList<>(); + roleList.add(roleCode); + + // 如果是超级管理员,额外添加admin标识 + Object roleIdObj = StpUtil.getTokenSession().get("roleId"); + if (roleIdObj != null) { + Integer roleId = Integer.parseInt(roleIdObj.toString()); + if (roleId.equals(RoleConstants.SUPER_ADMIN_ROLE_ID)) { + roleList.add("admin"); + roleList.add("super_admin"); + } + } + + log.info("用户 {} 的角色: {}", loginId, roleList); + return roleList; + } + + log.warn("用户 {} 的roleCode为空", loginId); + return Collections.emptyList(); + + } catch (Exception e) { + log.error("获取用户角色失败: {}", e.getMessage(), e); + return Collections.emptyList(); + } + } +} + diff --git a/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/config/XxlJobConfig.java b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/config/XxlJobConfig.java new file mode 100644 index 0000000..e2a6030 --- /dev/null +++ b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/config/XxlJobConfig.java @@ -0,0 +1,78 @@ +package com.aiotagro.cattletrade.config; + +import com.xxl.job.core.executor.impl.XxlJobSpringExecutor; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * xxl-job config + * + * @author xuxueli 2017-04-28 + */ +@Configuration +public class XxlJobConfig { + private Logger logger = LoggerFactory.getLogger(XxlJobConfig.class); + + @Value("${xxl.job.admin.addresses}") + private String adminAddresses; + + @Value("${xxl.job.accessToken}") + private String accessToken; + + @Value("${xxl.job.executor.appname}") + private String appname; + + @Value("${xxl.job.executor.address}") + private String address; + + @Value("${xxl.job.executor.ip}") + private String ip; + + @Value("${xxl.job.executor.port}") + private int port; + + @Value("${xxl.job.executor.logpath}") + private String logPath; + + @Value("${xxl.job.executor.logretentiondays}") + private int logRetentionDays; + + + @Bean + public XxlJobSpringExecutor xxlJobExecutor() { + logger.info(">>>>>>>>>>> xxl-job config init."); + XxlJobSpringExecutor xxlJobSpringExecutor = new XxlJobSpringExecutor(); + xxlJobSpringExecutor.setAdminAddresses(adminAddresses); + xxlJobSpringExecutor.setAppname(appname); + xxlJobSpringExecutor.setAddress(address); + xxlJobSpringExecutor.setIp(ip); + xxlJobSpringExecutor.setPort(port); + xxlJobSpringExecutor.setAccessToken(accessToken); + xxlJobSpringExecutor.setLogPath(logPath); + xxlJobSpringExecutor.setLogRetentionDays(logRetentionDays); + + return xxlJobSpringExecutor; + } + + /** + * 针对多网卡、容器内部署等情况,可借助 "spring-cloud-commons" 提供的 "InetUtils" 组件灵活定制注册IP; + * + * 1、引入依赖: + * + * org.springframework.cloud + * spring-cloud-commons + * ${version} + * + * + * 2、配置文件,或者容器启动变量 + * spring.cloud.inetutils.preferred-networks: 'xxx.xxx.xxx.' + * + * 3、获取IP + * String ip_ = inetUtils.findFirstNonLoopbackHostInfo().getIpAddress(); + */ + + +} \ No newline at end of file diff --git a/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/constant/HttpStatus.java b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/constant/HttpStatus.java new file mode 100644 index 0000000..cc9ec96 --- /dev/null +++ b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/constant/HttpStatus.java @@ -0,0 +1,94 @@ +package com.aiotagro.cattletrade.constant; + +/** + * 返回状态码 + * + * @author Carson + */ +public class HttpStatus +{ + /** + * 操作成功 + */ + public static final int SUCCESS = 200; + + /** + * 对象创建成功 + */ + public static final int CREATED = 201; + + /** + * 请求已经被接受 + */ + public static final int ACCEPTED = 202; + + /** + * 操作已经执行成功,但是没有返回数据 + */ + public static final int NO_CONTENT = 204; + + /** + * 资源已被移除 + */ + public static final int MOVED_PERM = 301; + + /** + * 重定向 + */ + public static final int SEE_OTHER = 303; + + /** + * 资源没有被修改 + */ + public static final int NOT_MODIFIED = 304; + + /** + * 参数列表错误(缺少,格式不匹配) + */ + public static final int BAD_REQUEST = 400; + + /** + * 未授权 + */ + public static final int UNAUTHORIZED = 401; + + /** + * 访问受限,授权过期 + */ + public static final int FORBIDDEN = 403; + + /** + * 资源,服务未找到 + */ + public static final int NOT_FOUND = 404; + + /** + * 不允许的http方法 + */ + public static final int BAD_METHOD = 405; + + /** + * 资源冲突,或者资源被锁 + */ + public static final int CONFLICT = 409; + + /** + * 不支持的数据,媒体类型 + */ + public static final int UNSUPPORTED_TYPE = 415; + + /** + * 系统内部错误 + */ + public static final int ERROR = 500; + + /** + * 接口未实现 + */ + public static final int NOT_IMPLEMENTED = 501; + + /** + * 系统警告消息 + */ + public static final int WARN = 601; +} diff --git a/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/constant/SaobeiConstant.java b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/constant/SaobeiConstant.java new file mode 100644 index 0000000..4099108 --- /dev/null +++ b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/constant/SaobeiConstant.java @@ -0,0 +1,10 @@ +package com.aiotagro.cattletrade.constant; + +public class SaobeiConstant { + + public static String SAOBEI_DATE_TIME_FORMAT_SHORT = "yyyyMMddHHmmss"; + public static String SAOBEI_SUCCESS_STATUS = "01"; + public static String SAOBEI_CONTENT_TYPE = "application/json;charset:utf-8;"; + public static int SAOBEI_TIMEOUT = 10000; + +} diff --git a/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/constant/WarningStatusAdminEnum.java b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/constant/WarningStatusAdminEnum.java new file mode 100644 index 0000000..5790d4f --- /dev/null +++ b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/constant/WarningStatusAdminEnum.java @@ -0,0 +1,22 @@ +package com.aiotagro.cattletrade.constant; + +import lombok.Getter; + +@Getter +public enum WarningStatusAdminEnum { + /** + * 牲畜类型 + */ + CATTLE(0, "盘点正常"), + SHEEP(1, "距离正常"), + PIG(2, "数量盘点预警"), + DEER(3, "运输距离预警"); + + private final int value ; + private final String description ; + + WarningStatusAdminEnum(int value, String desc) { + this.value = value; + this.description = desc; + } +} diff --git a/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/constant/WarningStatusEnum.java b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/constant/WarningStatusEnum.java new file mode 100644 index 0000000..977b2c5 --- /dev/null +++ b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/constant/WarningStatusEnum.java @@ -0,0 +1,22 @@ +package com.aiotagro.cattletrade.constant; + +import lombok.Getter; + +@Getter +public enum WarningStatusEnum { + /** + * 牲畜类型 + */ + CATTLE(0, "盘点正常"), + SHEEP(1, "距离正常"), + PIG(2, "主机盘点耳标数量与登记数量不一致"), + DEER(3, "未达到预定行驶距离"); + + private final int value ; + private final String description ; + + WarningStatusEnum(int value, String desc) { + this.value = value; + this.description = desc; + } +} diff --git a/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/exception/GlobalException.java b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/exception/GlobalException.java new file mode 100644 index 0000000..0ac2d38 --- /dev/null +++ b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/exception/GlobalException.java @@ -0,0 +1,47 @@ +package com.aiotagro.cattletrade.exception; + +import cn.dev33.satoken.exception.NotLoginException; +import cn.dev33.satoken.exception.NotPermissionException; +import cn.dev33.satoken.exception.NotRoleException; +import com.aiotagro.common.core.web.domain.AjaxResult; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +/** + * 全局异常处理 + */ +@RestControllerAdvice +@Slf4j +public class GlobalException { + + // 全局异常拦截(拦截项目中的所有异常) + @ExceptionHandler + public AjaxResult handlerException(Exception e, HttpServletRequest request, HttpServletResponse response) + throws Exception { + + // 不同异常返回不同状态码 + AjaxResult aj = null; + if (e instanceof NotLoginException) { // 如果是未登录异常 + NotLoginException ee = (NotLoginException) e; + aj = new AjaxResult(401, ee.getMessage()); + } else if(e instanceof NotRoleException) { // 如果是角色异常 + NotRoleException ee = (NotRoleException) e; + aj = new AjaxResult(402, "无此角色:" + ee.getRole()); + } else if(e instanceof NotPermissionException) { // 如果是权限异常 + NotPermissionException ee = (NotPermissionException) e; + aj = new AjaxResult(403, "无此权限:" + ee.getCode()); + } else { // 普通异常, 输出:500 + 异常信息 + aj = new AjaxResult(500, e.getMessage()); + // 打印堆栈,以供调试 + log.error("异常信息:{}", e.getMessage()); + e.printStackTrace(); + } + + // 返回给前端 + return aj; + } +} \ No newline at end of file diff --git a/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/generater/FastCodeGenerator.java b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/generater/FastCodeGenerator.java new file mode 100644 index 0000000..4c07a46 --- /dev/null +++ b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/generater/FastCodeGenerator.java @@ -0,0 +1,346 @@ +package com.aiotagro.cattletrade.generater; + +import com.baomidou.mybatisplus.annotation.FieldFill; +import com.baomidou.mybatisplus.core.exceptions.MybatisPlusException; +import com.baomidou.mybatisplus.core.toolkit.StringUtils; +import com.baomidou.mybatisplus.generator.FastAutoGenerator; +import com.baomidou.mybatisplus.generator.config.OutputFile; +import com.baomidou.mybatisplus.generator.config.rules.DateType; +import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine; +import com.baomidou.mybatisplus.generator.fill.Column; + +import java.util.*; + +/** + *

+ * 代码生成器(快速版本) + *

+ * + * @author admin + * @since 2021-10-22 0022 16:51 + */ +public class FastCodeGenerator { + + // 基础信息配置 + // 数据库连接字符 + private static final String URL = "jdbc:mysql://129.211.213.226:3306/cattletrade?useUnicode=true&serverTimezone=UTC&useSSL=false&characterEncoding=utf8"; + // 数据库用户名 + private static final String USERNAME = "root"; + // 数据库密码 + private static final String PASSWORD = "Aiotagro@741"; + // 项目根路径。生成结果如: + private static final String projectRootPath = System.getProperty("user.dir"); + // 项目根路径(测试用,非通用)(此句是本项目测试用的。实际项目中,包括多模块项目,请注释掉此句,使用上句) +// private static final String projectRootPath = System.getProperty("user.dir") + "/study-mybatis-plus-fast-generator"; + // 父包名。用于生成的java文件的import。 +// private static final String parentPackageName = "com.cxhit.mybatisplus.generator"; + private static final String parentPackageName = "com.aiotagro.cattletrade"; + + /** + * 执行此处 + */ + public static void main(String[] args) { + // 简单示例,适用于单模块项目 + String tables = "delivery,warning_log,delivery_device,jbq_client,jbq_client_log,jbq_server,jbq_server_log"; +// simpleGenerator(); + // 完整示例,适用于多模块项目 + // scanner.apply("请输入表名,多个英文逗号分隔?生成所有表,请输入[all]:") + completeGenerator(tables); + } + + /** + * 【单模块】简单的实现方案 + */ + protected static void simpleGenerator() { + + // 包路径 + String packagePath = projectRootPath + "/src/main/java"; + // XML文件的路径 + String mapperXmlPath = projectRootPath + "/src/main/resources/mapper"; + + // 开始执行代码生成 + FastAutoGenerator.create(URL, USERNAME, PASSWORD) + // 1. 全局配置 + .globalConfig(builder -> builder + // 作者名称 + .author("admin") + // 开启覆盖已生成的文件。注释掉则关闭覆盖。 + .fileOverride() + // 禁止打开输出目录。注释掉则生成完毕后,自动打开生成的文件目录。 + .disableOpenDir() + // 指定输出目录。如果指定,Windows生成至D盘根目录下,Linux or MAC 生成至 /tmp 目录下。 + .outputDir(packagePath) + // 开启swagger2.注释掉则默认关闭。 + // .enableSwagger() + // 指定时间策略。 + .dateType(DateType.TIME_PACK) + // 注释时间策略。 + .commentDate("yyyy-MM-dd") + ) + + // 2. 包配置 + .packageConfig((scanner, builder) -> builder + // 设置父表名 + .parent(parentPackageName) + .moduleName("model") + // mapper.xml 文件的路径。单模块下,其他文件路径默认即可。 + .pathInfo(Collections.singletonMap(OutputFile.mapperXml, mapperXmlPath)) + ) + + // 3. 策略配置 + .strategyConfig((scanner, builder) -> builder.addInclude(getTables(scanner.apply("请输入表名,多个英文逗号分隔?生成所有表,请输入[all]:"))) + // 阶段1:Entity实体类策略配置 + .entityBuilder() + // 开启生成实体时生成字段注解。 + // 会在实体类的属性前,添加[@TableField("nickname")] + .enableTableFieldAnnotation() + // 逻辑删除字段名(数据库)。 +// .logicDeleteColumnName("is_delete") + // 逻辑删除属性名(实体)。 + // 会在实体类的该字段属性前加注解[@TableLogic] +// .logicDeletePropertyName("isDelete") + // 会在实体类的该字段上追加注解[@TableField(value = "create_time", fill = FieldFill.INSERT)] + .addTableFills(new Column("create_time", FieldFill.INSERT)) + // 会在实体类的该字段上追加注解[@TableField(value = "update_time", fill = FieldFill.INSERT_UPDATE)] + .addTableFills(new Column("update_time", FieldFill.INSERT_UPDATE)) + // 阶段2:Mapper策略配置 + .mapperBuilder() + // 开启 @Mapper 注解。 + // 会在mapper接口上添加注解[@Mapper] + .enableMapperAnnotation() + // 启用 BaseResultMap 生成。 + // 会在mapper.xml文件生成[通用查询映射结果]配置。 + .enableBaseResultMap() + // 启用 BaseColumnList。 + // 会在mapper.xml文件生成[通用查询结果列 ]配置 + .enableBaseColumnList() + // 阶段4:Controller策略配置 + .controllerBuilder() + // 会在控制类中加[@RestController]注解。 + .enableRestStyle() + // 开启驼峰转连字符 + .enableHyphenStyle() + .build() + ) + + // 4. 模板引擎配置,默认 Velocity 可选模板引擎 Beetl 或 Freemarker + //.templateEngine(new BeetlTemplateEngine()) + .templateEngine(new FreemarkerTemplateEngine()) + + // 5. 执行 + .execute(); + } + + /** + * 【多模块使用】完整的实现方案 + */ + protected static void completeGenerator(String tables) { + //【1】基础信息配置 + // 指定模块名,用于生成的java文件的import。 + String moduleName = "business"; +// String moduleName = scanner("请输入模块名:"); + // 六个文件的路径。多模块项目下,一般来说每个文件的路径都是不同的(根据项目实际,可能在不同的模块下)。 + // common-dao/src/main/resources/com/aiotagro/db/dao/DtsAccountTraceMapper.xml + // common-dao/src/main/java/com/aiotagro/db/domain/DtsAccountTrace.java + String entityPath = projectRootPath + "/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/entity/" ; + String mapperPath = projectRootPath + "/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/mapper/" ; + String mapperXmlPath = projectRootPath + "/aiotagro-cattle-trade/src/main/resources/mapper/" ; + String servicePath = projectRootPath + "/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/service/" ; + String serviceImplPath = projectRootPath + "/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/service/impl"; + String controllerPath = projectRootPath + "/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/controller/"; + // 关于以上写法的解释: + // --- 假设我们的项目有四个模块:project-entity、project-dao、project-service、project-controller + // --- project-entity 的包路径:com.yourdomain.projectname.eneity, + // ---则生成system模块下的sys_user表,生成的实体文件将放在:com.yourdomain.projectname.entity.system包下,SysUser.java。 + // --- project-dao 的包路径:com.yourdomain.projectname.mapper, + // ---则生成system模块下的sys_user表,生成的mapper接口文件将放在:com.yourdomain.projectname.mapper.system包下,类名为:SysUserMapper.java。 + // --- 其他文件以此类推,即每个模块放MVC结构中对应的类型文件。 + // --- 注意:这里最后的文件路径修改了,下文配置中的【2 包配置】中的包路径也要同步修改!否则生成的java文件,首句import会报错。原因是路径错误。 + + // 【2】开始执行代码生成 + FastAutoGenerator.create(URL, USERNAME, PASSWORD) + // 1. 全局配置 + .globalConfig(builder -> builder + // 作者名称 + .author("admin") + // 开启覆盖已生成的文件。注释掉则关闭覆盖。请谨慎开启此选项! + .fileOverride() + // 禁止打开输出目录。注释掉则生成完毕后,自动打开生成的文件目录。 + .disableOpenDir() + // 指定输出目录。多模块下,每个类型的文件输出目录不一致,在包配置阶段配置。 + // .outputDir(packagePath) + // 开启swagger2。注释掉则默认关闭。 + // .enableSwagger() + // 开启 kotlin 模式。注释掉则关闭此模式 + // .enableKotlin() + // 指定时间策略。 + .dateType(DateType.ONLY_DATE) + // 注释时间策略。 + .commentDate("yyyy-MM-dd") + ) + + // 2. 包配置 + .packageConfig((scanner, builder) -> builder + // 阶段1:各个文件的包名设置,用来拼接每个java文件的第一句:package com.XXX.XXX.XXX.xxx; + // 父包名配置 + .parent(parentPackageName) + // 输入模块名。此模块名会在下面的几个包名前加。多模块项目,请根据实际选择是否添加。 + .moduleName(moduleName) + .entity("entity" ) + .mapper("mapper" ) + .service("service" ) + .serviceImpl("service" + ".impl") + .controller("controller") + .other("other") + // 阶段2:所有文件的生成路径配置 + .pathInfo( + new HashMap() {{ + // 实体类的保存路径 + put(OutputFile.entity, entityPath); + // mapper接口的保存路径 + put(OutputFile.mapper, mapperPath); + // mapper.xml文件的保存路径 + put(OutputFile.mapperXml, mapperXmlPath); + // service层接口的保存路径 + put(OutputFile.service, servicePath); + // service层接口实现类的保存路径 + put(OutputFile.serviceImpl, serviceImplPath); + // 控制类的保存路径 + put(OutputFile.controller, controllerPath); + }} + ) + ) + + // 3. 策略配置【请仔细阅读每一行,根据项目实际项目需求,修改、增删!!!】 + .strategyConfig((scanner, builder) -> builder.addInclude(getTables(tables)) + // 阶段1:Entity实体类策略配置 + .entityBuilder() + // 设置父类。会在生成的实体类名后:extends BaseEntity + // .superClass(BaseEntity.class) + // 禁用生成 serialVersionUID。(不推荐禁用) + // .disableSerialVersionUID() + // 开启生成字段常量。 + // 会在实体类末尾生成一系列 [public static final String NICKNAME = "nickname";] 的语句。(一般在写wapper时,会用到) + // .enableColumnConstant() + // 开启链式模型。 + // 会在实体类前添加 [@Accessors(chain = true)] 注解。用法如 [User user=new User().setAge(31).setName("snzl");](这是Lombok的注解,需要添加Lombok依赖) + // .enableChainModel() + // 开启 lombok 模型。 + // 会在实体类前添加 [@Getter] 和 [@Setter] 注解。(这是Lombok的注解,需要添加Lombok依赖) + .enableLombok() + // 开启 Boolean 类型字段移除 is 前缀。 + // .enableRemoveIsPrefix() + // 开启生成实体时生成字段注解。 + // 会在实体类的属性前,添加[@TableField("nickname")] + .enableTableFieldAnnotation() + // 逻辑删除字段名(数据库)。 +// .logicDeleteColumnName("is_delete") + // 逻辑删除属性名(实体)。 + // 会在实体类的该字段属性前加注解[@TableLogic] +// .logicDeletePropertyName("isDelete") + // 数据库表映射到实体的命名策略(默认下划线转驼峰)。一般不用设置 + // .naming(NamingStrategy.underline_to_camel) + // 数据库表字段映射到实体的命名策略(默认为 null,未指定按照 naming 执行)。一般不用设置 + // .columnNaming(NamingStrategy.underline_to_camel) + // 添加父类公共字段。 + // 这些字段不会出现在新增的实体类中。 +// .addSuperEntityColumns("id", "delete_time") + // 添加忽略字段。 + // 这些字段不会出现在新增的实体类中。 + // .addIgnoreColumns("password") + // 添加表字段填充 + // 会在实体类的该字段上追加注解[@TableField(value = "create_time", fill = FieldFill.INSERT)] +// .addTableFills(new Column("create_time", FieldFill.INSERT)) + // 会在实体类的该字段上追加注解[@TableField(value = "update_time", fill = FieldFill.INSERT_UPDATE)] +// .addTableFills(new Column("update_time", FieldFill.INSERT_UPDATE)) + // 全局主键类型。如果MySQL主键设置为自增,则不需要设置此项。 + // .idType(IdType.AUTO) + // 格式化文件名称。 + // 如果不设置,如表[sys_user]的实体类名是[SysUser]。设置成下面这样,将是[SysUserEntity] + // .formatFileName("%sEntity") + + // 阶段2:Mapper策略配置 + .mapperBuilder() + // 设置父类 + // 会在mapper接口方法集成[extends BaseMapper] + // .superClass(BaseMapper.class) + // 开启 @Mapper 注解。 + // 会在mapper接口上添加注解[@Mapper] + .enableMapperAnnotation() + // 启用 BaseResultMap 生成。 + // 会在mapper.xml文件生成[通用查询映射结果]配置。 + .enableBaseResultMap() + // 启用 BaseColumnList。 + // 会在mapper.xml文件生成[通用查询结果列 ]配置 + .enableBaseColumnList() + // 设置缓存实现类 + // .cache(MyMapperCache.class) + // 格式化 mapper 文件名称。 + // 如果不设置,如表[sys_user],默认的文件名是[SysUserMapper]。写成下面这种形式后,将变成[SysUserDao]。 + // .formatMapperFileName("%sDao") + // 格式化 xml 实现类文件名称。 + // 如果不设置,如表[sys_user],默认的文件名是[SysUserMapper.xml],写成下面这种形式后,将变成[SysUserXml.xml]。 + // .formatXmlFileName("%sXml") + + // 阶段3:Service策略配置 + // .serviceBuilder() + // 设置 service 接口父类 + // .superServiceClass(BaseService.class) + // 设置 service 实现类父类 + // .superServiceImplClass(BaseServiceImpl.class) + // 格式化 service 接口文件名称 + // 如果不设置,如表[sys_user],默认的是[ISysUserService]。写成下面这种形式后,将变成[SysUserService]。 + // .formatServiceFileName("%sService") + // 格式化 service 实现类文件名称 + // 如果不设置,如表[sys_user],默认的是[SysUserServiceImpl]。 + // .formatServiceImplFileName("%sServiceImpl") + + // 阶段4:Controller策略配置 + .controllerBuilder() + // 设置父类。 + // 会集成此父类。 + // .superClass(BaseController.class) + // 开启生成 @RestController 控制器 + // 会在控制类中加[@RestController]注解。 + .enableRestStyle() + // 开启驼峰转连字符 + .enableHyphenStyle() + + // 最后:构建 + .build() + ) + + //模板引擎配置,默认 Velocity 可选模板引擎 Beetl 或 Freemarker + //.templateEngine(new BeetlTemplateEngine()) + .templateEngine(new FreemarkerTemplateEngine()) + + // 执行 + .execute(); + } + + // 处理 all 情况 + protected static List getTables(String tables) { + return "all".equals(tables) ? Collections.emptyList() : Arrays.asList(tables.split(",")); + } + + /** + *

+ * 读取控制台内容 + *

+ */ + private static String scanner(String tip) { + Scanner scanner = new Scanner(System.in); + StringBuilder help = new StringBuilder(); + help.append("请输入").append(tip).append(":"); + System.out.println(help.toString()); + if (scanner.hasNext()) { + String ipt = scanner.next(); + if (StringUtils.isNotBlank(ipt)) { + return ipt; + } + } + throw new MybatisPlusException("请输入正确的" + tip + "!"); + } +} + + diff --git a/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/job/AutoNumWarningJob.java b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/job/AutoNumWarningJob.java new file mode 100644 index 0000000..7bc4c0b --- /dev/null +++ b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/job/AutoNumWarningJob.java @@ -0,0 +1,109 @@ +package com.aiotagro.cattletrade.job; + +import com.aiotagro.cattletrade.business.entity.Delivery; +import com.aiotagro.cattletrade.business.entity.DeliveryDevice; +import com.aiotagro.cattletrade.business.entity.JbqClientLog; +import com.aiotagro.cattletrade.business.entity.WarningLog; +import com.aiotagro.cattletrade.business.mapper.DeliveryDeviceMapper; +import com.aiotagro.cattletrade.business.mapper.DeliveryMapper; +import com.aiotagro.cattletrade.business.mapper.JbqClientLogMapper; +import com.aiotagro.cattletrade.business.mapper.WarningLogMapper; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.xxl.job.core.handler.annotation.XxlJob; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections4.CollectionUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.*; +import java.util.concurrent.atomic.AtomicReference; +import java.util.stream.Collectors; + +/** + * 电子围栏预警 + */ +@Slf4j +@Component +public class AutoNumWarningJob { + + @Autowired + private DeliveryMapper deliveryMapper; + + @Autowired + private DeliveryDeviceMapper deliveryDeviceMapper; + + @Autowired + private WarningLogMapper warningLogMapper; + + @Autowired + private JbqClientLogMapper jbqClientLogMapper; + + /** + * 监管端电子围栏预警 + */ + @XxlJob("numberWarningJob") + public void executeJob() { + log.info("数量盘点预警开始!"); + long startTime = System.currentTimeMillis(); + //当前日期 + Date date=new Date(); + //查询当前时间四个小时之前的数据 + Calendar calendar = Calendar.getInstance(); + calendar.setTime(date); + calendar.add(Calendar.HOUR_OF_DAY, -4); + Date paramDate = calendar.getTime(); + //查询所有未核验的运单数据 + List deliverys = deliveryMapper.selectList(new LambdaQueryWrapper().eq(Delivery::getStatus, 1)); + if(CollectionUtils.isNotEmpty(deliverys)){ + deliverys.forEach(delivery -> { + //查询运单关联的设备信息 + Integer deliveryId = delivery.getId(); + List deliverieDevices = deliveryDeviceMapper.selectList(new LambdaQueryWrapper().eq(DeliveryDevice::getDeliveryId, deliveryId)); + if(CollectionUtils.isNotEmpty(deliverieDevices)){ + //运单绑定的主机信息 + List serverDeviceIds = deliverieDevices.stream().filter(deliveryDevice -> deliveryDevice.getDeviceType() == 1).map(DeliveryDevice::getDeviceId).collect(Collectors.toList()); + //获取所有的耳标的信息 + List earDeviceIds = deliverieDevices.stream().filter(deliveryDevice -> deliveryDevice.getDeviceType() == 2).map(DeliveryDevice::getDeviceId).collect(Collectors.toList()); + //查询当前绑定的耳标四个小时之前的上报数据 + List jbqClientLogs = jbqClientLogMapper.selectList(new LambdaQueryWrapper().gt(JbqClientLog::getCreateTime, paramDate).in(JbqClientLog::getDeviceId,earDeviceIds)); + //对日志信息去重 + jbqClientLogs = + jbqClientLogs.stream().collect(Collectors.collectingAndThen(Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(o -> o.getDeviceId() + "#" + o.getServerDeviceId()))), ArrayList::new)); + Map> jbqCount = jbqClientLogs.stream().collect(Collectors.groupingBy(JbqClientLog::getServerDeviceId)); + //获取运单申报的耳标数 + Integer declaredJbqCount = delivery.getRegisteredJbqCount(); + AtomicReference warningtype = new AtomicReference<>(""); + AtomicReference inventoryJbqCount = new AtomicReference<>(0); + if(CollectionUtils.isNotEmpty(serverDeviceIds)){ + serverDeviceIds.forEach(serverDeviceId -> { + List jbqClientLogList = jbqCount.get(serverDeviceId); + //判断当前运单绑定的主机是否上报数据 + if(CollectionUtils.isNotEmpty(jbqClientLogList)){ + if(declaredJbqCount == jbqClientLogList.size()){ + warningtype.set("0"); + }else{ + warningtype.set("2"); + } + inventoryJbqCount.set(jbqClientLogList.size()); + }else{ + warningtype.set("2"); + } + }); + WarningLog warningLog = new WarningLog(); + warningLog.setDeliveryId(deliveryId); + warningLog.setWarningType(warningtype.get()); + warningLog.setInventoryJbqCount(inventoryJbqCount.get()); + warningLog.setWarningTime(date); + warningLogMapper.insert(warningLog); + } + } + }); + + } + //查询当前时间点的设备上报数据 + long endTime = System.currentTimeMillis(); + log.info("数量盘点预警结束!耗时(毫秒):{}",endTime-startTime); + + } + +} diff --git a/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/properties/PlatformExpensesProperties.java b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/properties/PlatformExpensesProperties.java new file mode 100644 index 0000000..0bec43a --- /dev/null +++ b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/properties/PlatformExpensesProperties.java @@ -0,0 +1,37 @@ +package com.aiotagro.cattletrade.properties; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +/** + * @author Carson + * @package_name com.aiotagro.payinfo.properties + * @date 2024/2/20 09:20 + */ +@Data +@Component +@ConfigurationProperties(prefix = "platform") +public class PlatformExpensesProperties { + + /** + * 网络支付平台支付通道费千分位 + **/ + private Double expenses ; + + /** + * POS机储蓄卡费率最低值 + **/ + private Double posCardRate; + + /** + * POS机单笔交易最大费率值 + **/ + private Integer posCardTopPrice; + + /** + * POS机信用卡费率最低值 + **/ + private Double posCreditCardRate; + +} diff --git a/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/properties/TenantConfigProperties.java b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/properties/TenantConfigProperties.java new file mode 100644 index 0000000..3fd1701 --- /dev/null +++ b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/properties/TenantConfigProperties.java @@ -0,0 +1,32 @@ +package com.aiotagro.cattletrade.properties; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author Carson + * @package_name com.aiotagro.payinfo.properties + * @date 2024/2/6 14:18 + */ +@Data +@Component +@ConfigurationProperties(prefix = "paydatainfo.tenant") +public class TenantConfigProperties { + + /* + ** * 租户表字段名 + */ + private String column = "tenant_id"; + + + + /** + * 不是多租户的表 + **/ + private List tables = new ArrayList<>(); + +} diff --git a/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/remote/TestApi.java b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/remote/TestApi.java new file mode 100644 index 0000000..d09718a --- /dev/null +++ b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/remote/TestApi.java @@ -0,0 +1,12 @@ +package com.aiotagro.cattletrade.remote; + +import com.github.lianjiatech.retrofit.spring.boot.annotation.RetrofitClient; +import retrofit2.http.GET; + +@RetrofitClient(baseUrl = "${remote.baseUrl}") +public interface TestApi { + + @GET("/auth/code") + Object getCode(); + +} diff --git a/tradeCattle/aiotagro-cattle-trade/src/main/resources/application-demo.yml b/tradeCattle/aiotagro-cattle-trade/src/main/resources/application-demo.yml new file mode 100644 index 0000000..2573493 --- /dev/null +++ b/tradeCattle/aiotagro-cattle-trade/src/main/resources/application-demo.yml @@ -0,0 +1,108 @@ +spring: + # redis 配置 + redis: + # 地址 + host: 129.211.213.226 + # 端口,默认为6379 + port: 6379 + # 数据库索引 + database: 11 + # 密码 + password: Aiotagro@741 + # 连接超时时间 + timeout: 10s + lettuce: + pool: + # 连接池中的最小空闲连接 + min-idle: 0 + # 连接池中的最大空闲连接 + max-idle: 8 + # 连接池的最大数据库连接数 + max-active: 8 + # #连接池最大阻塞等待时间(使用负值表示没有限制) + max-wait: -1ms + datasource: + driver-class-name: com.mysql.cj.jdbc.Driver + url: jdbc:mysql://129.211.213.226:3306/cattletrade?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8 + username: root + password: Aiotagro@741 + +sa-token: + # token 名称(同时也是 cookie 名称) + token-name: Authorization + # token 有效期(单位:秒) 默认30天,-1 代表永久有效 + timeout: 259200 + # token 最低活跃频率(单位:秒),如果 token 超过此时间没有访问系统就会被冻结,默认-1 代表不限制,永不冻结 + active-timeout: 86400 + # 是否允许同一账号多地同时登录 (为 true 时允许一起登录, 为 false 时新登录挤掉旧登录) + is-concurrent: false + # 在多人登录同一账号时,是否共用一个 token (为 true 时所有登录共用一个 token, 为 false 时每次登录新建一个 token) + is-share: false + # token 风格(默认可取值:uuid、simple-uuid、random-32、random-64、random-128、tik) + token-style: random-64 + # 是否输出操作日志 + is-log: true + # 同一账号最大登录数量 + max-login-count: 1 + # 是否尝试从请求体里读取token + is-read-body: false + # 是否尝试从请求头读取token + is-read-header: true + # 是否尝试从cookie读取token,为false时登录也不会再往前端注入cookie + is-read-cookie: false + # 是否在登录后将 Token 写入到响应头 + is-write-header: true + # 自动续签 + auto-renew: true + token-prefix: Bearer + # 是否在初始化配置时打印版本 + is-print: false + # 是否打印操作日志 + is-color-log: true + # jwt秘钥 + jwt-secret-key: aiotagrocommonpayplatformsystem + +remote: + baseUrl: http://127.0.0.1:8080/ + +retrofit: + # 日志打印配置 + log: + # 启用日志打印 + enable: true + # 日志打印拦截器 + logging-interceptor: com.github.lianjiatech.retrofit.spring.boot.interceptor.DefaultLoggingInterceptor + # 全局日志打印级别 + global-log-level: info + # 全局日志打印策略 + global-log-strategy: body + # 全局连接超时时间 + global-connect-timeout-ms: 3000 + # 全局读取超时时间 + global-read-timeout-ms: 3000 + # 全局写入超时时间 + global-write-timeout-ms: 35000 + # 全局完整调用超时时间 + global-call-timeout-ms: 0 + +xxl: + job: + admin: + addresses: https://demo-xxljob.aiotagro.com #测试地址,生产地址换成https://xxl-job-admin.aiotagro.com/ + accessToken: default #和xxl-job服务里的accessToken保持一致 此项目前可以保持默认 + executor: + # 执行器名称,跟管理平台设置的Appname保持一致 + appname: trade-cattle + #ip: 10.123.1.53 端口要保证全局唯一 + port: 9620 + # 日志地址 + logpath: /data/applogs/xxl-job/jobhandler + # 日志保存时间 + logretentiondays: 30 + address: + ip: +# 日志配置 +logging: + level: + com.aiotagro: debug + org.springframework: info \ No newline at end of file diff --git a/tradeCattle/aiotagro-cattle-trade/src/main/resources/application-dev.yml b/tradeCattle/aiotagro-cattle-trade/src/main/resources/application-dev.yml new file mode 100644 index 0000000..e1d5c03 --- /dev/null +++ b/tradeCattle/aiotagro-cattle-trade/src/main/resources/application-dev.yml @@ -0,0 +1,118 @@ +spring: + # redis 配置 - 暂时禁用 + # redis: + # # 地址 + # host: 129.211.213.226 + # # 端口,默认为6379 + # port: 6379 + # # 数据库索引 + # database: 11 + # # 密码 + # password: Aiotj#gfdh7jk58s&12 + # # 连接超时时间 + # timeout: 30s + # lettuce: + # pool: + # # 连接池中的最小空闲连接 + # min-idle: 2 + # # 连接池中的最大空闲连接 + # max-idle: 10 + # # 连接池的最大数据库连接数 + # max-active: 20 + # # #连接池最大阻塞等待时间(使用负值表示没有限制) + # max-wait: 10s + datasource: + # url: jdbc:mysql://mysql57-iot.aiotagro.com:60026/cattletrade?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8 + # username: iot-plateform + # password: 3qJ7$bV%N9mE + driver-class-name: com.mysql.cj.jdbc.Driver + url: jdbc:mysql://129.211.213.226:3306/cattletrade?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8 + username: root + password: Aiotagro@741 + +sa-token: + # token 名称(同时也是 cookie 名称) + token-name: Authorization + # token 有效期(单位:秒) 默认30天,-1 代表永久有效 + timeout: 259200 + # token 最低活跃频率(单位:秒),如果 token 超过此时间没有访问系统就会被冻结,默认-1 代表不限制,永不冻结 + active-timeout: 86400 + # 是否允许同一账号多地同时登录 (为 true 时允许一起登录, 为 false 时新登录挤掉旧登录) + is-concurrent: false + # 在多人登录同一账号时,是否共用一个 token (为 true 时所有登录共用一个 token, 为 false 时每次登录新建一个 token) + is-share: false + # token 风格(默认可取值:uuid、simple-uuid、random-32、random-64、random-128、tik) + token-style: random-64 + # 是否输出操作日志 + is-log: true + # 同一账号最大登录数量 + max-login-count: 1 + # 是否尝试从请求体里读取token + is-read-body: false + # 是否尝试从请求头读取token + is-read-header: true + # 是否尝试从cookie读取token,为false时登录也不会再往前端注入cookie + is-read-cookie: false + # 是否在登录后将 Token 写入到响应头 + is-write-header: true + # 自动续签 + auto-renew: true + token-prefix: Bearer + # 是否在初始化配置时打印版本 + is-print: false + # 是否打印操作日志 + is-color-log: true + # jwt秘钥 + jwt-secret-key: aiotagrocommonpayplatformsystem + # 权限验证策略 + check-id-token: true + # 开启通配符权限匹配 + match-mode: true + # 使用内存存储替代Redis + alone-redis: false + +remote: + baseUrl: http://127.0.0.1:8080/ + +retrofit: + # 日志打印配置 + log: + # 启用日志打印 + enable: true + # 日志打印拦截器 + logging-interceptor: com.github.lianjiatech.retrofit.spring.boot.interceptor.DefaultLoggingInterceptor + # 全局日志打印级别 + global-log-level: info + # 全局日志打印策略 + global-log-strategy: body + # 全局连接超时时间 + global-connect-timeout-ms: 3000 + # 全局读取超时时间 + global-read-timeout-ms: 3000 + # 全局写入超时时间 + global-write-timeout-ms: 35000 + # 全局完整调用超时时间 + global-call-timeout-ms: 0 + +xxl: + job: + admin: + addresses: https://demo-xxljob.aiotagro.com #测试地址,生产地址换成https://xxl-job-admin.aiotagro.com/ + accessToken: default #和xxl-job服务里的accessToken保持一致 此项目前可以保持默认 + executor: + # 执行器名称,跟管理平台设置的Appname保持一致 + appname: trade-cattle + #ip: 10.123.1.53 端口要保证全局唯一 + port: 9620 + # 日志地址 + logpath: /data/applogs/xxl-job/jobhandler + # 日志保存时间 + logretentiondays: 30 + address: + ip: + +# 日志配置 +logging: + level: + com.aiotagro: debug + org.springframework: info \ No newline at end of file diff --git a/tradeCattle/aiotagro-cattle-trade/src/main/resources/application-prod.yml b/tradeCattle/aiotagro-cattle-trade/src/main/resources/application-prod.yml new file mode 100644 index 0000000..eebe93e --- /dev/null +++ b/tradeCattle/aiotagro-cattle-trade/src/main/resources/application-prod.yml @@ -0,0 +1,110 @@ +spring: + # redis 配置 - 暂时禁用 + # redis: + # # 地址 + # host: 127.0.0.1 + # # 端口,默认为6379 + # port: 6379 + # # 数据库索引 + # database: 11 + # # 密码 + # password: Aiotagro@741 + # # 连接超时时间 + # timeout: 10s + # lettuce: + # pool: + # # 连接池中的最小空闲连接 + # min-idle: 0 + # # 连接池中的最大空闲连接 + # max-idle: 8 + # # 连接池的最大数据库连接数 + # max-active: 8 + # # #连接池最大阻塞等待时间(使用负值表示没有限制) + # max-wait: -1ms + datasource: + driver-class-name: com.mysql.cj.jdbc.Driver + url: jdbc:mysql://129.211.213.226:3306/cattletrade?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8 + username: root + password: Aiotagro@741 + +sa-token: + # token 名称(同时也是 cookie 名称) + token-name: Authorization + # token 有效期(单位:秒) 默认30天,-1 代表永久有效 + timeout: 259200 + # token 最低活跃频率(单位:秒),如果 token 超过此时间没有访问系统就会被冻结,默认-1 代表不限制,永不冻结 + active-timeout: 86400 + # 是否允许同一账号多地同时登录 (为 true 时允许一起登录, 为 false 时新登录挤掉旧登录) + is-concurrent: false + # 在多人登录同一账号时,是否共用一个 token (为 true 时所有登录共用一个 token, 为 false 时每次登录新建一个 token) + is-share: false + # token 风格(默认可取值:uuid、simple-uuid、random-32、random-64、random-128、tik) + token-style: random-64 + # 是否输出操作日志 + is-log: true + # 同一账号最大登录数量 + max-login-count: 1 + # 是否尝试从请求体里读取token + is-read-body: false + # 是否尝试从请求头读取token + is-read-header: true + # 是否尝试从cookie读取token,为false时登录也不会再往前端注入cookie + is-read-cookie: false + # 是否在登录后将 Token 写入到响应头 + is-write-header: true + # 自动续签 + auto-renew: true + token-prefix: Bearer + # 是否在初始化配置时打印版本 + is-print: false + # 是否打印操作日志 + is-color-log: true + # jwt秘钥 + jwt-secret-key: aiotagrocommonpayplatformsystem + # 使用内存存储替代Redis + alone-redis: false + +remote: + baseUrl: http://127.0.0.1:8080/ + +retrofit: + # 日志打印配置 + log: + # 启用日志打印 + enable: true + # 日志打印拦截器 + logging-interceptor: com.github.lianjiatech.retrofit.spring.boot.interceptor.DefaultLoggingInterceptor + # 全局日志打印级别 + global-log-level: info + # 全局日志打印策略 + global-log-strategy: body + # 全局连接超时时间 + global-connect-timeout-ms: 3000 + # 全局读取超时时间 + global-read-timeout-ms: 3000 + # 全局写入超时时间 + global-write-timeout-ms: 35000 + # 全局完整调用超时时间 + global-call-timeout-ms: 0 + +xxl: + job: + admin: + addresses: https://xxl-job-admin.aiotagro.com/ #测试地址,生产地址换成https://xxl-job-admin.aiotagro.com/ + accessToken: default #和xxl-job服务里的accessToken保持一致 此项目前可以保持默认 + executor: + # 执行器名称,跟管理平台设置的Appname保持一致 + appname: trade-cattle + #ip: 10.123.1.53 端口要保证全局唯一 + port: 9620 + # 日志地址 + logpath: /data/applogs/xxl-job/jobhandler + # 日志保存时间 + logretentiondays: 30 + address: + ip: +# 日志配置 +logging: + level: + com.aiotagro: debug + org.springframework: info \ No newline at end of file diff --git a/tradeCattle/aiotagro-cattle-trade/src/main/resources/application.yml b/tradeCattle/aiotagro-cattle-trade/src/main/resources/application.yml new file mode 100644 index 0000000..88b3de3 --- /dev/null +++ b/tradeCattle/aiotagro-cattle-trade/src/main/resources/application.yml @@ -0,0 +1,97 @@ +server: + # 服务器的HTTP端口,默认为8080 + port: 16200 + servlet: + # 应用的访问路径 + context-path: /api + tomcat: + # tomcat的URI编码 + uri-encoding: UTF-8 + # 连接数满后的排队数,默认为100 + accept-count: 1000 + threads: + # tomcat最大线程数,默认为200 + max: 800 + # Tomcat启动初始化的线程数,默认值10 + min-spare: 100 +spring: + profiles: + active: prod + # 文件上传 + servlet: + multipart: + # 单个文件大小 + max-file-size: 50MB + # 设置总上传的文件大小 + max-request-size: 50MB + # 服务模块 + devtools: + restart: + # 热部署开关 + enabled: true + #设置热部署要监听的目录 + additional-paths: src/main/java + # 解决项目自动重新编译后接口报404的问题 + poll-interval: 3000 + quiet-period: 1000 + + + +# MyBatis配置 +#mybatis: +# # 搜索指定包别名 +# type-aliases-package: com.aiotagro.payinfo.domain.mapper +# # 配置mapper的扫描,找到所有的mapper.xml映射文件 +# mapper-locations: classpath*:mapper/**/*Mapper.xml +# # 加载全局的配置文件 +# config-location: classpath:mybatis/mybatis-config.xml + +# mybatis-plus配置 +mybatis-plus: + # 搜索指定包别名 + typeAliasesPackage: com.aiotagro.payinfo.domain.mapper + # 配置mapper的扫描,找到所有的mapper.xml映射文件 + mapper-locations: classpath*:mapper/**/*Mapper.xml + # 加载全局的配置文件 + configLocation: classpath:mybatis/mybatis-config.xml + global-config: + db-config: + logic-delete-field: del_flag + logic-delete-value: 1 + logic-not-delete-value: 0 + + + +# PageHelper分页插件 +pagehelper: + helper-dialect: mysql #设置数据库类型 + reasonable: true #开启合理化:页码<=0 查询第一页,页码>=总页数查询最后一页 + support-methods-arguments: true #支持通过 Mapper 接口参数来传递分页参数 + params: count=countsql + + +aiot-lims: + tenant: + column: customer_code + tables: + - sys_users + - sys_log + - coo_customer + - sys_citys + - dgtx_places + - products + +#短信相关配置 +sms: + secret-id: AKIDpoms4MmHtUDvhSHC1y115DTmFfCq2CSq + secret-key: PqL0EOuOyRsEayMi8dDv228IY8ywohe4 + end-point: sms.tencentcloudapi.com + appid: 1400915506 + sign: 武汉爱农云联科技 + template-id: 2175348 + +openapi: + url: http://api.aiotagro.com/api/business/xq/locationLog + +iot: + url: http://aiot.aiotagro.com/iotPlateform/iotBusiness/sendCmd diff --git a/tradeCattle/aiotagro-cattle-trade/src/main/resources/mapper/DeliveryDeviceMapper.xml b/tradeCattle/aiotagro-cattle-trade/src/main/resources/mapper/DeliveryDeviceMapper.xml new file mode 100644 index 0000000..7cd57a5 --- /dev/null +++ b/tradeCattle/aiotagro-cattle-trade/src/main/resources/mapper/DeliveryDeviceMapper.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + id, delivery_id, device_id, device_type + + + diff --git a/tradeCattle/aiotagro-cattle-trade/src/main/resources/mapper/DeliveryMapper.xml b/tradeCattle/aiotagro-cattle-trade/src/main/resources/mapper/DeliveryMapper.xml new file mode 100644 index 0000000..43ba317 --- /dev/null +++ b/tradeCattle/aiotagro-cattle-trade/src/main/resources/mapper/DeliveryMapper.xml @@ -0,0 +1,93 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + id, delivery_number, buyer_price, sale_price, firm_price, status, license_plate, car_front_photo, car_behind_photo, car_video, start_location, start_lat, start_lon, end_location, end_lat, end_lon, estimated_delivery_time, registered_jbq_count, driver_name, driver_mobile, create_time, created_by, check_by, check_time, check_video + + + + + diff --git a/tradeCattle/aiotagro-cattle-trade/src/main/resources/mapper/JbqClientLogMapper.xml b/tradeCattle/aiotagro-cattle-trade/src/main/resources/mapper/JbqClientLogMapper.xml new file mode 100644 index 0000000..4d2b93a --- /dev/null +++ b/tradeCattle/aiotagro-cattle-trade/src/main/resources/mapper/JbqClientLogMapper.xml @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + id, device_id, device_voltage, device_temp, server_device_id, latitude, longitude, walk_steps, y_walk_steps, create_time, create_by, update_time, update_by + + + + diff --git a/tradeCattle/aiotagro-cattle-trade/src/main/resources/mapper/JbqClientMapper.xml b/tradeCattle/aiotagro-cattle-trade/src/main/resources/mapper/JbqClientMapper.xml new file mode 100644 index 0000000..8a96f5e --- /dev/null +++ b/tradeCattle/aiotagro-cattle-trade/src/main/resources/mapper/JbqClientMapper.xml @@ -0,0 +1,92 @@ + + + + + + + + + + + + + + + + + + + + + + + + + id, device_id, device_voltage, device_temp, server_device_id, latitude, longitude, walk_steps, y_walk_steps, create_time, create_by, update_time, update_by, source_id + + + + + + + diff --git a/tradeCattle/aiotagro-cattle-trade/src/main/resources/mapper/JbqServerLogMapper.xml b/tradeCattle/aiotagro-cattle-trade/src/main/resources/mapper/JbqServerLogMapper.xml new file mode 100644 index 0000000..9bd0f5c --- /dev/null +++ b/tradeCattle/aiotagro-cattle-trade/src/main/resources/mapper/JbqServerLogMapper.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + id, device_id, device_voltage, device_temp, latitude, longitude, walk_steps, y_walk_steps, create_time, create_by, update_time, update_by + + + + diff --git a/tradeCattle/aiotagro-cattle-trade/src/main/resources/mapper/JbqServerMapper.xml b/tradeCattle/aiotagro-cattle-trade/src/main/resources/mapper/JbqServerMapper.xml new file mode 100644 index 0000000..9b9cd9c --- /dev/null +++ b/tradeCattle/aiotagro-cattle-trade/src/main/resources/mapper/JbqServerMapper.xml @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + + id, device_id, device_voltage, device_temp, latitude, longitude, walk_steps, y_walk_steps, create_time, create_by, update_time, update_by, source_id + + + + diff --git a/tradeCattle/aiotagro-cattle-trade/src/main/resources/mapper/SysMenuMapper.xml b/tradeCattle/aiotagro-cattle-trade/src/main/resources/mapper/SysMenuMapper.xml new file mode 100644 index 0000000..8a8f5f0 --- /dev/null +++ b/tradeCattle/aiotagro-cattle-trade/src/main/resources/mapper/SysMenuMapper.xml @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + + + + + + + + id, parent_id, type, icon, name, sort, route_url, page_url, authority, create_time, update_time, is_delete + + + + + + + + diff --git a/tradeCattle/aiotagro-cattle-trade/src/main/resources/mapper/SysRoleMapper.xml b/tradeCattle/aiotagro-cattle-trade/src/main/resources/mapper/SysRoleMapper.xml new file mode 100644 index 0000000..93cc093 --- /dev/null +++ b/tradeCattle/aiotagro-cattle-trade/src/main/resources/mapper/SysRoleMapper.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + id, name, description, company_id, type, create_time, update_time, is_delete + + + diff --git a/tradeCattle/aiotagro-cattle-trade/src/main/resources/mapper/SysRoleMenuMapper.xml b/tradeCattle/aiotagro-cattle-trade/src/main/resources/mapper/SysRoleMenuMapper.xml new file mode 100644 index 0000000..a54a26c --- /dev/null +++ b/tradeCattle/aiotagro-cattle-trade/src/main/resources/mapper/SysRoleMenuMapper.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + id, role_id, menu_id + + + diff --git a/tradeCattle/aiotagro-cattle-trade/src/main/resources/mapper/SysUserMapper.xml b/tradeCattle/aiotagro-cattle-trade/src/main/resources/mapper/SysUserMapper.xml new file mode 100644 index 0000000..e4694ec --- /dev/null +++ b/tradeCattle/aiotagro-cattle-trade/src/main/resources/mapper/SysUserMapper.xml @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + + + + + + id, name, mobile, role_id, password, status, type, company_name, conpany_id, create_time, create_by, update_time, update_by, last_login_time, is_delete + + + + + + + + + diff --git a/tradeCattle/aiotagro-cattle-trade/src/main/resources/mapper/WarningLogMapper.xml b/tradeCattle/aiotagro-cattle-trade/src/main/resources/mapper/WarningLogMapper.xml new file mode 100644 index 0000000..9ac2411 --- /dev/null +++ b/tradeCattle/aiotagro-cattle-trade/src/main/resources/mapper/WarningLogMapper.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + id, delivery_id, warning_type, inventory_jbq_count, expected_distance, actual_distance, warning_time + + + + + diff --git a/tradeCattle/aiotagro-cattle-trade/src/main/resources/mybatis/mybatis-config.xml b/tradeCattle/aiotagro-cattle-trade/src/main/resources/mybatis/mybatis-config.xml new file mode 100644 index 0000000..10cf6b0 --- /dev/null +++ b/tradeCattle/aiotagro-cattle-trade/src/main/resources/mybatis/mybatis-config.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + diff --git a/tradeCattle/aiotagro-core/pom.xml b/tradeCattle/aiotagro-core/pom.xml new file mode 100644 index 0000000..23e63ed --- /dev/null +++ b/tradeCattle/aiotagro-core/pom.xml @@ -0,0 +1,156 @@ + + + 4.0.0 + + com.aiotagro + cattletrade + 1.0.1 + + + aiotagro-core + + + aiot-core核心模块 + + + + + + + org.springframework + spring-context-support + + + + + org.springframework + spring-web + + + + + com.alibaba + transmittable-thread-local + + + + + org.springframework.boot + spring-boot-starter-validation + + + + + com.fasterxml.jackson.core + jackson-databind + + + + + com.alibaba.fastjson2 + fastjson2 + + + + + io.jsonwebtoken + jjwt + + + + + javax.xml.bind + jaxb-api + + + + + org.apache.commons + commons-lang3 + + + + + commons-io + commons-io + + + + + org.apache.poi + poi-ooxml + + + + + javax.servlet + javax.servlet-api + + + + + io.github.mouzt + bizlog-sdk + + + + org.projectlombok + lombok + + + + + cn.dev33 + sa-token-spring-boot-starter + + + + + + cn.dev33 + sa-token-redis-jackson + + + + + cn.dev33 + sa-token-jwt + + + + + + com.tencentcloudapi + tencentcloud-sdk-java + + + + com.qcloud + cos_api + + + org.apache.commons + commons-collections4 + 4.4 + + + + + + com.alibaba + easyexcel + 3.1.0 + + + poi-ooxml-schemas + org.apache.poi + + + + + + + + \ No newline at end of file diff --git a/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/annotation/Excel.java b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/annotation/Excel.java new file mode 100644 index 0000000..530cbad --- /dev/null +++ b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/annotation/Excel.java @@ -0,0 +1,183 @@ +package com.aiotagro.common.core.annotation; + +import com.aiotagro.common.core.utils.poi.ExcelHandlerAdapter; +import org.apache.poi.ss.usermodel.HorizontalAlignment; +import org.apache.poi.ss.usermodel.IndexedColors; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.math.BigDecimal; + +/** + * 自定义导出Excel数据注解 + * + * @author Carson + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +public @interface Excel +{ + /** + * 导出时在excel中排序 + */ + public int sort() default Integer.MAX_VALUE; + + /** + * 导出到Excel中的名字. + */ + public String name() default ""; + + /** + * 日期格式, 如: yyyy-MM-dd + */ + public String dateFormat() default ""; + + /** + * 读取内容转表达式 (如: 0=男,1=女,2=未知) + */ + public String readConverterExp() default ""; + + /** + * 分隔符,读取字符串组内容 + */ + public String separator() default ","; + + /** + * BigDecimal 精度 默认:-1(默认不开启BigDecimal格式化) + */ + public int scale() default -1; + + /** + * BigDecimal 舍入规则 默认:BigDecimal.ROUND_HALF_EVEN + */ + public int roundingMode() default BigDecimal.ROUND_HALF_EVEN; + + /** + * 导出时在excel中每个列的高度 + */ + public double height() default 14; + + /** + * 导出时在excel中每个列的宽度 + */ + public double width() default 16; + + /** + * 文字后缀,如% 90 变成90% + */ + public String suffix() default ""; + + /** + * 当值为空时,字段的默认值 + */ + public String defaultValue() default ""; + + /** + * 提示信息 + */ + public String prompt() default ""; + + /** + * 设置只能选择不能输入的列内容. + */ + public String[] combo() default {}; + + /** + * 是否需要纵向合并单元格,应对需求:含有list集合单元格) + */ + public boolean needMerge() default false; + + /** + * 是否导出数据,应对需求:有时我们需要导出一份模板,这是标题需要但内容需要用户手工填写. + */ + public boolean isExport() default true; + + /** + * 另一个类中的属性名称,支持多级获取,以小数点隔开 + */ + public String targetAttr() default ""; + + /** + * 是否自动统计数据,在最后追加一行统计数据总和 + */ + public boolean isStatistics() default false; + + /** + * 导出类型(0数字 1字符串) + */ + public ColumnType cellType() default ColumnType.STRING; + + /** + * 导出列头背景颜色 + */ + public IndexedColors headerBackgroundColor() default IndexedColors.GREY_50_PERCENT; + + /** + * 导出列头字体颜色 + */ + public IndexedColors headerColor() default IndexedColors.WHITE; + + /** + * 导出单元格背景颜色 + */ + public IndexedColors backgroundColor() default IndexedColors.WHITE; + + /** + * 导出单元格字体颜色 + */ + public IndexedColors color() default IndexedColors.BLACK; + + /** + * 导出字段对齐方式 + */ + public HorizontalAlignment align() default HorizontalAlignment.CENTER; + + /** + * 自定义数据处理器 + */ + public Class handler() default ExcelHandlerAdapter.class; + + /** + * 自定义数据处理器参数 + */ + public String[] args() default {}; + + /** + * 字段类型(0:导出导入;1:仅导出;2:仅导入) + */ + Type type() default Type.ALL; + + public enum Type + { + ALL(0), EXPORT(1), IMPORT(2); + private final int value; + + Type(int value) + { + this.value = value; + } + + public int value() + { + return this.value; + } + } + + public enum ColumnType + { + NUMERIC(0), STRING(1), IMAGE(2); + private final int value; + + ColumnType(int value) + { + this.value = value; + } + + public int value() + { + return this.value; + } + } +} \ No newline at end of file diff --git a/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/annotation/Excels.java b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/annotation/Excels.java new file mode 100644 index 0000000..018dc95 --- /dev/null +++ b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/annotation/Excels.java @@ -0,0 +1,18 @@ +package com.aiotagro.common.core.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Excel注解集 + * + * @author Carson + */ +@Target(ElementType.FIELD) +@Retention(RetentionPolicy.RUNTIME) +public @interface Excels +{ + Excel[] value(); +} \ No newline at end of file diff --git a/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/constant/CacheConstants.java b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/constant/CacheConstants.java new file mode 100644 index 0000000..7992c83 --- /dev/null +++ b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/constant/CacheConstants.java @@ -0,0 +1,59 @@ +package com.aiotagro.common.core.constant; + +/** + * 缓存常量信息 + * + * @author Carson + */ +public class CacheConstants +{ + /** + * 缓存有效期,默认720(分钟) + */ + public final static long EXPIRATION = 720; + + /** + * 缓存刷新时间,默认120(分钟) + */ + public final static long REFRESH_TIME = 120; + + /** + * 密码最大错误次数 + */ + public final static int PASSWORD_MAX_RETRY_COUNT = 5; + + /** + * 密码锁定时间,默认10(分钟) + */ + public final static long PASSWORD_LOCK_TIME = 10; + + /** + * 权限缓存前缀 + */ + public final static String LOGIN_TOKEN_KEY = "login_tokens:"; + + /** + * 验证码 redis key + */ + public static final String CAPTCHA_CODE_KEY = "captcha_codes:"; + + /** + * 参数管理 cache key + */ + public static final String SYS_CONFIG_KEY = "sys_config:"; + + /** + * 字典管理 cache key + */ + public static final String SYS_DICT_KEY = "sys_dict:"; + + /** + * 登录账户密码错误次数 redis key + */ + public static final String PWD_ERR_CNT_KEY = "pwd_err_cnt:"; + + /** + * 登录IP黑名单 cache key + */ + public static final String SYS_LOGIN_BLACKIPLIST = SYS_CONFIG_KEY + "sys.login.blackIPList"; +} diff --git a/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/constant/Constants.java b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/constant/Constants.java new file mode 100644 index 0000000..0c04c0f --- /dev/null +++ b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/constant/Constants.java @@ -0,0 +1,142 @@ +package com.aiotagro.common.core.constant; + +import java.util.Arrays; +import java.util.List; + +/** + * 通用常量信息 + * + * @author Carson + */ +public class Constants { + /** + * UTF-8 字符集 + */ + public static final String UTF8 = "UTF-8"; + + /** + * GBK 字符集 + */ + public static final String GBK = "GBK"; + + /** + * www主域 + */ + public static final String WWW = "www."; + + /** + * RMI 远程方法调用 + */ + public static final String LOOKUP_RMI = "rmi:"; + + /** + * LDAP 远程方法调用 + */ + public static final String LOOKUP_LDAP = "ldap:"; + + /** + * LDAPS 远程方法调用 + */ + public static final String LOOKUP_LDAPS = "ldaps:"; + + /** + * http请求 + */ + public static final String HTTP = "http://"; + + /** + * https请求 + */ + public static final String HTTPS = "https://"; + + /** + * 成功标记 + */ + public static final Integer SUCCESS = 200; + + /** + * 失败标记 + */ + public static final Integer FAIL = 500; + + /** + * 登录成功状态 + */ + public static final String LOGIN_SUCCESS_STATUS = "0"; + + /** + * 登录失败状态 + */ + public static final String LOGIN_FAIL_STATUS = "1"; + + /** + * 登录成功 + */ + public static final String LOGIN_SUCCESS = "Success"; + + /** + * 注销 + */ + public static final String LOGOUT = "Logout"; + + /** + * 注册 + */ + public static final String REGISTER = "Register"; + + /** + * 登录失败 + */ + public static final String LOGIN_FAIL = "Error"; + + /** + * 当前记录起始索引 + */ + public static final String PAGE_NUM = "pageNum"; + + /** + * 每页显示记录数 + */ + public static final String PAGE_SIZE = "pageSize"; + + /** + * 排序列 + */ + public static final String ORDER_BY_COLUMN = "orderByColumn"; + + /** + * 排序的方向 "desc" 或者 "asc". + */ + public static final String IS_ASC = "isAsc"; + + /** + * 验证码有效期(分钟) + */ + public static final long CAPTCHA_EXPIRATION = 2; + + /** + * 资源映射路径 前缀 + */ + public static final String RESOURCE_PREFIX = "/profile"; + + /** + * 自动识别json对象白名单配置(仅允许解析的包名,范围越小越安全) + */ + public static final String[] JSON_WHITELIST_STR = {"org.springframework", "com.aiotagro"}; + + /** + * 定时任务白名单配置(仅允许访问的包名,如其他需要可以自行添加) + */ + public static final String[] JOB_WHITELIST_STR = {"com.aiotagro"}; + + /** + * 定时任务违规的字符 + */ + public static final String[] JOB_ERROR_STR = {"java.net.URL", "javax.naming.InitialContext", "org.yaml.snakeyaml", + "org.springframework", "org.apache", "com.aiotagro.common.core.utils.file"}; + + /** + * 密码加密前缀 + */ + public static final String USER_PASSWORD_PREFIX = "aiot"; +} diff --git a/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/constant/GenConstants.java b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/constant/GenConstants.java new file mode 100644 index 0000000..bd100ce --- /dev/null +++ b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/constant/GenConstants.java @@ -0,0 +1,117 @@ +package com.aiotagro.common.core.constant; + +/** + * 代码生成通用常量 + * + * @author Carson + */ +public class GenConstants +{ + /** 单表(增删改查) */ + public static final String TPL_CRUD = "crud"; + + /** 树表(增删改查) */ + public static final String TPL_TREE = "tree"; + + /** 主子表(增删改查) */ + public static final String TPL_SUB = "sub"; + + /** 树编码字段 */ + public static final String TREE_CODE = "treeCode"; + + /** 树父编码字段 */ + public static final String TREE_PARENT_CODE = "treeParentCode"; + + /** 树名称字段 */ + public static final String TREE_NAME = "treeName"; + + /** 上级菜单ID字段 */ + public static final String PARENT_MENU_ID = "parentMenuId"; + + /** 上级菜单名称字段 */ + public static final String PARENT_MENU_NAME = "parentMenuName"; + + /** 数据库字符串类型 */ + public static final String[] COLUMNTYPE_STR = { "char", "varchar", "nvarchar", "varchar2" }; + + /** 数据库文本类型 */ + public static final String[] COLUMNTYPE_TEXT = { "tinytext", "text", "mediumtext", "longtext" }; + + /** 数据库时间类型 */ + public static final String[] COLUMNTYPE_TIME = { "datetime", "time", "date", "timestamp" }; + + /** 数据库数字类型 */ + public static final String[] COLUMNTYPE_NUMBER = { "tinyint", "smallint", "mediumint", "int", "number", "integer", + "bigint", "float", "double", "decimal" }; + + /** 页面不需要编辑字段 */ + public static final String[] COLUMNNAME_NOT_EDIT = { "id", "create_by", "create_time", "del_flag" }; + + /** 页面不需要显示的列表字段 */ + public static final String[] COLUMNNAME_NOT_LIST = { "id", "create_by", "create_time", "del_flag", "update_by", + "update_time" }; + + /** 页面不需要查询字段 */ + public static final String[] COLUMNNAME_NOT_QUERY = { "id", "create_by", "create_time", "del_flag", "update_by", + "update_time", "remark" }; + + /** Entity基类字段 */ + public static final String[] BASE_ENTITY = { "createBy", "createTime", "updateBy", "updateTime", "remark" }; + + /** Tree基类字段 */ + public static final String[] TREE_ENTITY = { "parentName", "parentId", "orderNum", "ancestors" }; + + /** 文本框 */ + public static final String HTML_INPUT = "input"; + + /** 文本域 */ + public static final String HTML_TEXTAREA = "textarea"; + + /** 下拉框 */ + public static final String HTML_SELECT = "select"; + + /** 单选框 */ + public static final String HTML_RADIO = "radio"; + + /** 复选框 */ + public static final String HTML_CHECKBOX = "checkbox"; + + /** 日期控件 */ + public static final String HTML_DATETIME = "datetime"; + + /** 图片上传控件 */ + public static final String HTML_IMAGE_UPLOAD = "imageUpload"; + + /** 文件上传控件 */ + public static final String HTML_FILE_UPLOAD = "fileUpload"; + + /** 富文本控件 */ + public static final String HTML_EDITOR = "editor"; + + /** 字符串类型 */ + public static final String TYPE_STRING = "String"; + + /** 整型 */ + public static final String TYPE_INTEGER = "Integer"; + + /** 长整型 */ + public static final String TYPE_LONG = "Long"; + + /** 浮点型 */ + public static final String TYPE_DOUBLE = "Double"; + + /** 高精度计算类型 */ + public static final String TYPE_BIGDECIMAL = "BigDecimal"; + + /** 时间类型 */ + public static final String TYPE_DATE = "Date"; + + /** 模糊查询 */ + public static final String QUERY_LIKE = "LIKE"; + + /** 相等查询 */ + public static final String QUERY_EQ = "EQ"; + + /** 需要 */ + public static final String REQUIRE = "1"; +} diff --git a/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/constant/HttpStatus.java b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/constant/HttpStatus.java new file mode 100644 index 0000000..214ceb4 --- /dev/null +++ b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/constant/HttpStatus.java @@ -0,0 +1,94 @@ +package com.aiotagro.common.core.constant; + +/** + * 返回状态码 + * + * @author Carson + */ +public class HttpStatus +{ + /** + * 操作成功 + */ + public static final int SUCCESS = 200; + + /** + * 对象创建成功 + */ + public static final int CREATED = 201; + + /** + * 请求已经被接受 + */ + public static final int ACCEPTED = 202; + + /** + * 操作已经执行成功,但是没有返回数据 + */ + public static final int NO_CONTENT = 204; + + /** + * 资源已被移除 + */ + public static final int MOVED_PERM = 301; + + /** + * 重定向 + */ + public static final int SEE_OTHER = 303; + + /** + * 资源没有被修改 + */ + public static final int NOT_MODIFIED = 304; + + /** + * 参数列表错误(缺少,格式不匹配) + */ + public static final int BAD_REQUEST = 400; + + /** + * 未授权 + */ + public static final int UNAUTHORIZED = 401; + + /** + * 访问受限,授权过期 + */ + public static final int FORBIDDEN = 403; + + /** + * 资源,服务未找到 + */ + public static final int NOT_FOUND = 404; + + /** + * 不允许的http方法 + */ + public static final int BAD_METHOD = 405; + + /** + * 资源冲突,或者资源被锁 + */ + public static final int CONFLICT = 409; + + /** + * 不支持的数据,媒体类型 + */ + public static final int UNSUPPORTED_TYPE = 415; + + /** + * 系统内部错误 + */ + public static final int ERROR = 500; + + /** + * 接口未实现 + */ + public static final int NOT_IMPLEMENTED = 501; + + /** + * 系统警告消息 + */ + public static final int WARN = 601; +} diff --git a/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/constant/LogRecordType/CustomerRecordType.java b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/constant/LogRecordType/CustomerRecordType.java new file mode 100644 index 0000000..f4cbd40 --- /dev/null +++ b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/constant/LogRecordType/CustomerRecordType.java @@ -0,0 +1,16 @@ +package com.aiotagro.common.core.constant.LogRecordType; + +/** + * @author Carson + * @package_name com.aiotagro.common.core.constant.LogRecordType + * @date 2024/2/23 15:17 + */ +public class CustomerRecordType { + public static final String model = "CUSTOMER"; + + public static final String save ="SAVE"; + + public static final String delete = "DELETE"; + + public static final String changeStatus = "CHANGESTATUS"; +} diff --git a/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/constant/LogRecordType/ProductsRecordType.java b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/constant/LogRecordType/ProductsRecordType.java new file mode 100644 index 0000000..abee31f --- /dev/null +++ b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/constant/LogRecordType/ProductsRecordType.java @@ -0,0 +1,13 @@ +package com.aiotagro.common.core.constant.LogRecordType; + +/** + * @author Carson + * @package_name com.aiotagro.common.core.constant.LogRecordType + * @date 2024/2/23 15:29 + */ +public class ProductsRecordType { + public static final String model = "PRODUCTS"; + + public static final String save = "SAVE"; + public static final String delete = "DELETE"; +} diff --git a/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/constant/LogRecordType/TradeMarketRecordType.java b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/constant/LogRecordType/TradeMarketRecordType.java new file mode 100644 index 0000000..5aa4abd --- /dev/null +++ b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/constant/LogRecordType/TradeMarketRecordType.java @@ -0,0 +1,16 @@ +package com.aiotagro.common.core.constant.LogRecordType; + +/** + * @author Carson + * @package_name com.aiotagro.common.core.constant.LogRecordType + * @date 2024/2/23 15:21 + */ +public class TradeMarketRecordType { + public static final String model = "TradeMarket"; + + public static final String save = "SAVE"; + + public static final String addBatch = "ADDBATCH"; + + public static final String delete = "DELETE"; +} diff --git a/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/constant/LogRecordType/TradeMarketRuleRecordType.java b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/constant/LogRecordType/TradeMarketRuleRecordType.java new file mode 100644 index 0000000..21761c8 --- /dev/null +++ b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/constant/LogRecordType/TradeMarketRuleRecordType.java @@ -0,0 +1,15 @@ +package com.aiotagro.common.core.constant.LogRecordType; + +/** + * @author Carson + * @package_name com.aiotagro.common.core.constant.LogRecordType + * @date 2024/2/23 15:25 + */ +public class TradeMarketRuleRecordType { + + public static final String model = "RULE"; + + public static final String save = "SAVE"; + public static final String delete = "DELETE"; + +} diff --git a/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/constant/LogRecordType/UserRecordType.java b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/constant/LogRecordType/UserRecordType.java new file mode 100644 index 0000000..ee9341a --- /dev/null +++ b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/constant/LogRecordType/UserRecordType.java @@ -0,0 +1,19 @@ +package com.aiotagro.common.core.constant.LogRecordType; + +/** + * @author Carson + * @package_name com.aiotagro.common.core.constant + * @date 2024/2/7 14:14 + */ +public class UserRecordType { + public static final String USER = "USER"; + public static final String USERLOGIN = "LOGIN"; + public static final String USERADD = "AddUser"; + public static final String USERDELETE = "DELETE"; + public static final String USERSTATUSCHANGE = "CHANGESTATUS"; + + + + + +} diff --git a/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/constant/RoleConstants.java b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/constant/RoleConstants.java new file mode 100644 index 0000000..dfba361 --- /dev/null +++ b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/constant/RoleConstants.java @@ -0,0 +1,26 @@ +package com.aiotagro.common.core.constant; + +/** + * 角色相关常量 + * + * @author System + * @date 2025-10-11 + */ +public class RoleConstants { + + /** + * 超级管理员角色ID + */ + public static final Integer SUPER_ADMIN_ROLE_ID = 1; + + /** + * 超级管理员角色编码 + */ + public static final String SUPER_ADMIN_ROLE_CODE = "ROLE_SUPER_ADMIN"; + + /** + * 所有权限标识 + */ + public static final String ALL_PERMISSION = "*:*:*"; +} + diff --git a/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/constant/ScheduleConstants.java b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/constant/ScheduleConstants.java new file mode 100644 index 0000000..79fe7d7 --- /dev/null +++ b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/constant/ScheduleConstants.java @@ -0,0 +1,50 @@ +package com.aiotagro.common.core.constant; + +/** + * 任务调度通用常量 + * + * @author Carson + */ +public class ScheduleConstants +{ + public static final String TASK_CLASS_NAME = "TASK_CLASS_NAME"; + + /** 执行目标key */ + public static final String TASK_PROPERTIES = "TASK_PROPERTIES"; + + /** 默认 */ + public static final String MISFIRE_DEFAULT = "0"; + + /** 立即触发执行 */ + public static final String MISFIRE_IGNORE_MISFIRES = "1"; + + /** 触发一次执行 */ + public static final String MISFIRE_FIRE_AND_PROCEED = "2"; + + /** 不触发立即执行 */ + public static final String MISFIRE_DO_NOTHING = "3"; + + public enum Status + { + /** + * 正常 + */ + NORMAL("0"), + /** + * 暂停 + */ + PAUSE("1"); + + private String value; + + private Status(String value) + { + this.value = value; + } + + public String getValue() + { + return value; + } + } +} diff --git a/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/constant/SecurityConstants.java b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/constant/SecurityConstants.java new file mode 100644 index 0000000..90360b9 --- /dev/null +++ b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/constant/SecurityConstants.java @@ -0,0 +1,54 @@ +package com.aiotagro.common.core.constant; + +/** + * 权限相关通用常量 + * + * @author Carson + */ +public class SecurityConstants +{ + /** + * 用户ID字段 + */ + public static final String DETAILS_USER_ID = "user_id"; + + /* + * 租户ID字段 + */ + public static final String DETAILS_TENANT_ID = "tenant_id"; + + /** + * 用户名字段 + */ + public static final String DETAILS_USERNAME = "username"; + + /** + * 授权信息字段 + */ + public static final String AUTHORIZATION_HEADER = "authorization"; + + /** + * 请求来源 + */ + public static final String FROM_SOURCE = "from-source"; + + /** + * 内部请求 + */ + public static final String INNER = "inner"; + + /** + * 用户标识 + */ + public static final String USER_KEY = "user_key"; + + /** + * 登录用户 + */ + public static final String LOGIN_USER = "login_user"; + + /** + * 角色权限 + */ + public static final String ROLE_PERMISSION = "role_permission"; +} diff --git a/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/constant/ServiceNameConstants.java b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/constant/ServiceNameConstants.java new file mode 100644 index 0000000..95a4e10 --- /dev/null +++ b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/constant/ServiceNameConstants.java @@ -0,0 +1,24 @@ +package com.aiotagro.common.core.constant; + +/** + * 服务名称 + * + * @author Carson + */ +public class ServiceNameConstants +{ + /** + * 认证服务的serviceid + */ + public static final String AUTH_SERVICE = "aiot-auth"; + + /** + * 系统模块的serviceid + */ + public static final String SYSTEM_SERVICE = "aiot-system"; + + /** + * 文件服务的serviceid + */ + public static final String FILE_SERVICE = "aiot-file"; +} diff --git a/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/constant/TencentCloudConstants.java b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/constant/TencentCloudConstants.java new file mode 100644 index 0000000..4cca8b0 --- /dev/null +++ b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/constant/TencentCloudConstants.java @@ -0,0 +1,224 @@ +package com.aiotagro.common.core.constant; + +import java.util.HashMap; +import java.util.Map; + +/** + * @author zhangq + * @date 2022/8/17 16:32 + * @description 系统常量 + */ +public class TencentCloudConstants { + + + /** + * 密码加密前缀 + */ + public static final String USER_PASSWORD_PREFIX = "aiot"; + + + /** + * 令牌 + */ + public static final String TOKEN = "token"; + + /** + * 登录用户 + */ + public static final String LOGIN_USER = "loginUser"; + + /** + * 令牌前缀 + */ + public static final String TOKEN_PREFIX = "Bearer "; + + + /** + * 登录用户 redis key + */ + public static final String LOGIN_TOKEN_KEY = "login_tokens:"; + + /** + * 登录用户角色 key + */ + public static final String LOGIN_ROLE_ID = "login_role_id:"; + + /** + * 令牌前缀 + */ + public static final String LOGIN_USER_KEY = "login_user_key"; + + + /** + * 登录失败 + */ + public static final String LOGIN_FAIL = "Error"; + + /** + * 腾讯云id + */ + public static String TENCENT_CLOUD_SECRETID = "AKIDMXTAjEKZM3g4GDvnHVTQMwGLfmjQ9KRI"; + + /** + * 腾讯云key + */ + public static String TENCENT_CLOUD_SECRETKEY = "wcw0qQvARefTLzMVWhiIi1FyUq1xjYCk"; + + /** + * 腾讯云爱彼特id + */ + public static String TENCENT_CLOUD_AIPITE_SECRETID = "AKIDgP1B8sPiogN4YnocEcmiU4PXTEm6HUWL"; + + /** + * 腾讯云爱彼特key + */ + public static String TENCENT_CLOUD_AIPITE_SECRETKEY = "iEYecnlW9bszt54uX396d5NRI0Xq078L"; + + /** + * 腾讯云时区 + */ + public static String TENCENT_CLOUD_REGION = "ap-guangzhou"; + + /** + * 腾讯云时区 + */ + public static String TENCENT_CLOUD_BUCKET = "smart-1251449951"; + + + /** + * 微信验证码key + */ + public static final String WECHAT_SMS_KEY = "wechat_sms_key:"; + + /** + * 微信验证码发送频率控制 + */ + public static final String WECHAT_SMS_INTERVAL_KEY = "wechat_sms_interval_key:"; + + public static final String AIPETELEFENCE_KEY = "aipet_elefence_key:"; + + public static final String AIPITE_I18N = "aipet_i18n:"; + + /** + * 短信验证码有效期 + */ + public static final Integer smsExpireTime = 10; + + /** + * 短信频率控制 2分钟 + */ + public static final Integer smsIntervalTime = 2; + + /** + * 微信登录token + */ + public static final String WECHAT_LOGIN_KEY = "wechat_login_token:"; + + + /** + * 微信小程序 :0-生资交易 1-云满牛 2-宠物项圈 + */ + public static final Map wechatAppIdMap; + static { + wechatAppIdMap = new HashMap(); + wechatAppIdMap.put("0","wx944d8cc44a8025ad"); + wechatAppIdMap.put("1","wx569c227c3129dd13"); + wechatAppIdMap.put("2","wx903c6e697ac3db97"); + }; + + /** + * 微信小程序Secret :0-生资交易 1-云满牛 2-宠物项圈 + */ + public static HashMap wechatAppSecretMap; + static { + wechatAppSecretMap = new HashMap(); + wechatAppSecretMap.put("0","fe6dd856a9004b39dd82374f6d34382d"); + wechatAppSecretMap.put("1","a52ff1c654b7410f8abef5a8164da511"); + wechatAppSecretMap.put("2","abb4a58a76f5fe510e28f4196d18563a"); + + }; + + + /** + * 微信小程序Mchid :0-生资交易 1-云满牛 + */ + public static HashMap wechatMchidMap; + static { + wechatMchidMap = new HashMap(); + wechatMchidMap.put("0","1540683851"); + wechatMchidMap.put("1","1540683851"); + + }; + + /** + * 微信小程序回调地址 :0-生资交易 1-云满牛 + */ + public static HashMap wechatNotifyMap; + static { + wechatNotifyMap = new HashMap(); + wechatNotifyMap.put("0","http://134.175.105.94:8989/wechat/sz/notify"); + wechatNotifyMap.put("1","http://134.175.105.94:8989/wechat/ymn/notify"); + + }; + + + /** + * 微信小程序服务地址 + */ + public static final String WECHAT_URL = "https://api.weixin.qq.com/sns/jscode2session"; + + /** + * 微信支付地址 + */ + public static final String WECHAT_PAY_URL = "https://api.mch.weixin.qq.com"; + + + /** + * 微信支付V3 uri + */ + public static final String WECHAT_PAY_V3_URL = WECHAT_PAY_URL + "/v3/pay/transactions/jsapi"; + + + public static final String WECHAT_PAY_V3_KEY = "740d316d372da28fd37eb9b34dbc92c0"; + + /** + * 微信支付结果查询uri + */ + public static final String WECHAT_PAY_QUERY_URL = WECHAT_PAY_URL + "/v3/pay/transactions/out-trade-no/%s?mchid=%s"; + + /** + * 微信支付私钥路径 + */ + public static final String WECHAT_PAY_PRIVATE_PATH = "static/private_key.pem"; + + + /** + * 微信支付码 + */ + public static String WECHAT_PAY_SERIAL_NO="508AED7BA545FF42CBD75C72A8940228C913698E"; + + /** + * 微信token获取地址 + */ + public static final String WECHAT_TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token"; + + /** + * 微信获取用户手机号 + */ + public static final String WECHAT_CELLPHONE_URL = "https://api.weixin.qq.com/wxa/business/getuserphonenumber"; + + /** + * 腾讯坐标转换地址 + */ + public static final String TENCENT_LOCATION_URL = "https://apis.map.qq.com/ws/coord/v1/translate"; + + /** + * 腾讯坐标转换key + */ + public static final String TENCENT_LOCATION_KEY = "5ENBZ-65EKQ-HDT5D-4SVW5-XVTFO-JCFBO"; + + /** + * 经纬度转地名 + */ + public static final String TENCENT_TRANS_KEY = "f9c298affa8aedea29d604ffb26c7078"; +} diff --git a/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/constant/TokenConstants.java b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/constant/TokenConstants.java new file mode 100644 index 0000000..0bdc0f4 --- /dev/null +++ b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/constant/TokenConstants.java @@ -0,0 +1,25 @@ +package com.aiotagro.common.core.constant; + +/** + * Token的Key常量 + * + * @author Carson + */ +public class TokenConstants +{ + /** + * 令牌自定义标识 + */ + public static final String AUTHENTICATION = "Authorization"; + + /** + * 令牌前缀 + */ + public static final String PREFIX = "Bearer "; + + /** + * 令牌秘钥 + */ + public final static String SECRET = "abcdefghijklmnopqrstuvwxyz"; + +} diff --git a/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/constant/UserConstants.java b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/constant/UserConstants.java new file mode 100644 index 0000000..e7102b3 --- /dev/null +++ b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/constant/UserConstants.java @@ -0,0 +1,80 @@ +package com.aiotagro.common.core.constant; + +/** + * 用户常量信息 + * + * @author Carson + */ +public class UserConstants +{ + /** + * 平台内系统用户的唯一标志 + */ + public static final String SYS_USER = "SYS_USER"; + + /** 正常状态 */ + public static final String NORMAL = "0"; + + /** 异常状态 */ + public static final String EXCEPTION = "1"; + + /** 用户封禁状态 */ + public static final String USER_DISABLE = "1"; + + /** 角色封禁状态 */ + public static final String ROLE_DISABLE = "1"; + + /** 部门正常状态 */ + public static final String DEPT_NORMAL = "0"; + + /** 部门停用状态 */ + public static final String DEPT_DISABLE = "1"; + + /** 字典正常状态 */ + public static final String DICT_NORMAL = "0"; + + /** 是否为系统默认(是) */ + public static final String YES = "Y"; + + /** 是否菜单外链(是) */ + public static final String YES_FRAME = "0"; + + /** 是否菜单外链(否) */ + public static final String NO_FRAME = "1"; + + /** 菜单类型(目录) */ + public static final String TYPE_DIR = "M"; + + /** 菜单类型(菜单) */ + public static final String TYPE_MENU = "C"; + + /** 菜单类型(按钮) */ + public static final String TYPE_BUTTON = "F"; + + /** Layout组件标识 */ + public final static String LAYOUT = "Layout"; + + /** ParentView组件标识 */ + public final static String PARENT_VIEW = "ParentView"; + + /** InnerLink组件标识 */ + public final static String INNER_LINK = "InnerLink"; + + /** 校验是否唯一的返回标识 */ + public final static boolean UNIQUE = true; + public final static boolean NOT_UNIQUE = false; + + /** + * 用户名长度限制 + */ + public static final int USERNAME_MIN_LENGTH = 2; + + public static final int USERNAME_MAX_LENGTH = 20; + + /** + * 密码长度限制 + */ + public static final int PASSWORD_MIN_LENGTH = 5; + + public static final int PASSWORD_MAX_LENGTH = 20; +} diff --git a/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/context/SecurityContextHolder.java b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/context/SecurityContextHolder.java new file mode 100644 index 0000000..1813632 --- /dev/null +++ b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/context/SecurityContextHolder.java @@ -0,0 +1,105 @@ +package com.aiotagro.common.core.context; + +import com.aiotagro.common.core.constant.SecurityConstants; +import com.aiotagro.common.core.text.Convert; +import com.aiotagro.common.core.utils.StringUtils; +import com.alibaba.ttl.TransmittableThreadLocal; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * 获取当前线程变量中的 用户id、用户名称、Token等信息 + * 注意: 必须在网关通过请求头的方法传入,同时在HeaderInterceptor拦截器设置值。 否则这里无法获取 + * + * @author Carson + */ +public class SecurityContextHolder +{ + private static final TransmittableThreadLocal> THREAD_LOCAL = new TransmittableThreadLocal<>(); + + public static void set(String key, Object value) + { + Map map = getLocalMap(); + map.put(key, value == null ? StringUtils.EMPTY : value); + } + + public static String get(String key) + { + Map map = getLocalMap(); + return Convert.toStr(map.getOrDefault(key, StringUtils.EMPTY)); + } + + public static T get(String key, Class clazz) + { + Map map = getLocalMap(); + return StringUtils.cast(map.getOrDefault(key, null)); + } + + public static Map getLocalMap() + { + Map map = THREAD_LOCAL.get(); + if (map == null) + { + map = new ConcurrentHashMap(); + THREAD_LOCAL.set(map); + } + return map; + } + + public static void setLocalMap(Map threadLocalMap) + { + THREAD_LOCAL.set(threadLocalMap); + } + + public static Long getUserId() + { + return Convert.toLong(get(SecurityConstants.DETAILS_USER_ID), 0L); + } + + public static void setUserId(String account) + { + set(SecurityConstants.DETAILS_USER_ID, account); + } + + public static Long getTenantId(){return Convert.toLong(get(SecurityConstants.DETAILS_TENANT_ID), 0L);} + + public static void setTenantId(String tenantId){set(SecurityConstants.DETAILS_TENANT_ID, tenantId);} + + + + public static String getUserName() + { + return get(SecurityConstants.DETAILS_USERNAME); + } + + public static void setUserName(String username) + { + set(SecurityConstants.DETAILS_USERNAME, username); + } + + public static String getUserKey() + { + return get(SecurityConstants.USER_KEY); + } + + public static void setUserKey(String userKey) + { + set(SecurityConstants.USER_KEY, userKey); + } + + public static String getPermission() + { + return get(SecurityConstants.ROLE_PERMISSION); + } + + public static void setPermission(String permissions) + { + set(SecurityConstants.ROLE_PERMISSION, permissions); + } + + public static void remove() + { + THREAD_LOCAL.remove(); + } +} diff --git a/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/domain/R.java b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/domain/R.java new file mode 100644 index 0000000..d8b6902 --- /dev/null +++ b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/domain/R.java @@ -0,0 +1,116 @@ +package com.aiotagro.common.core.domain; + +import com.aiotagro.common.core.constant.Constants; + +import java.io.Serializable; + +/** + * 响应信息主体 + * + * @author Carson + */ +public class R implements Serializable +{ + private static final long serialVersionUID = 1L; + + /** 成功 */ + public static final int SUCCESS = Constants.SUCCESS; + + /** 失败 */ + public static final int FAIL = Constants.FAIL; + + private int code; + + private String msg; + + private T data; + + public static R ok() + { + return restResult(null, SUCCESS, null); + } + + public static R ok(T data) + { + return restResult(data, SUCCESS, null); + } + + public static R ok(T data, String msg) + { + return restResult(data, SUCCESS, msg); + } + + public static R fail() + { + return restResult(null, FAIL, null); + } + + public static R fail(String msg) + { + return restResult(null, FAIL, msg); + } + + public static R fail(T data) + { + return restResult(data, FAIL, null); + } + + public static R fail(T data, String msg) + { + return restResult(data, FAIL, msg); + } + + public static R fail(int code, String msg) + { + return restResult(null, code, msg); + } + + private static R restResult(T data, int code, String msg) + { + R apiResult = new R<>(); + apiResult.setCode(code); + apiResult.setData(data); + apiResult.setMsg(msg); + return apiResult; + } + + public int getCode() + { + return code; + } + + public void setCode(int code) + { + this.code = code; + } + + public String getMsg() + { + return msg; + } + + public void setMsg(String msg) + { + this.msg = msg; + } + + public T getData() + { + return data; + } + + public void setData(T data) + { + this.data = data; + } + + public static Boolean isError(R ret) + { + return !isSuccess(ret); + } + + public static Boolean isSuccess(R ret) + { + return R.SUCCESS == ret.getCode(); + } +} diff --git a/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/enums/UserStatus.java b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/enums/UserStatus.java new file mode 100644 index 0000000..ada58ae --- /dev/null +++ b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/enums/UserStatus.java @@ -0,0 +1,30 @@ +package com.aiotagro.common.core.enums; + +/** + * 用户状态 + * + * @author Carson + */ +public enum UserStatus +{ + OK("0", "正常"), DISABLE("1", "停用"), DELETED("2", "删除"); + + private final String code; + private final String info; + + UserStatus(String code, String info) + { + this.code = code; + this.info = info; + } + + public String getCode() + { + return code; + } + + public String getInfo() + { + return info; + } +} diff --git a/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/exception/CaptchaException.java b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/exception/CaptchaException.java new file mode 100644 index 0000000..cc186c5 --- /dev/null +++ b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/exception/CaptchaException.java @@ -0,0 +1,16 @@ +package com.aiotagro.common.core.exception; + +/** + * 验证码错误异常类 + * + * @author Carson + */ +public class CaptchaException extends RuntimeException +{ + private static final long serialVersionUID = 1L; + + public CaptchaException(String msg) + { + super(msg); + } +} diff --git a/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/exception/CheckedException.java b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/exception/CheckedException.java new file mode 100644 index 0000000..94fcb63 --- /dev/null +++ b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/exception/CheckedException.java @@ -0,0 +1,31 @@ +package com.aiotagro.common.core.exception; + +/** + * 检查异常 + * + * @author Carson + */ +public class CheckedException extends RuntimeException +{ + private static final long serialVersionUID = 1L; + + public CheckedException(String message) + { + super(message); + } + + public CheckedException(Throwable cause) + { + super(cause); + } + + public CheckedException(String message, Throwable cause) + { + super(message, cause); + } + + public CheckedException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) + { + super(message, cause, enableSuppression, writableStackTrace); + } +} diff --git a/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/exception/DemoModeException.java b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/exception/DemoModeException.java new file mode 100644 index 0000000..e633750 --- /dev/null +++ b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/exception/DemoModeException.java @@ -0,0 +1,15 @@ +package com.aiotagro.common.core.exception; + +/** + * 演示模式异常 + * + * @author Carson + */ +public class DemoModeException extends RuntimeException +{ + private static final long serialVersionUID = 1L; + + public DemoModeException() + { + } +} diff --git a/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/exception/GlobalException.java b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/exception/GlobalException.java new file mode 100644 index 0000000..0233c89 --- /dev/null +++ b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/exception/GlobalException.java @@ -0,0 +1,58 @@ +package com.aiotagro.common.core.exception; + +/** + * 全局异常 + * + * @author Carson + */ +public class GlobalException extends RuntimeException +{ + private static final long serialVersionUID = 1L; + + /** + * 错误提示 + */ + private String message; + + /** + * 错误明细,内部调试错误 + * + * 和 {@link CommonResult#getDetailMessage()} 一致的设计 + */ + private String detailMessage; + + /** + * 空构造方法,避免反序列化问题 + */ + public GlobalException() + { + } + + public GlobalException(String message) + { + this.message = message; + } + + public String getDetailMessage() + { + return detailMessage; + } + + public GlobalException setDetailMessage(String detailMessage) + { + this.detailMessage = detailMessage; + return this; + } + + @Override + public String getMessage() + { + return message; + } + + public GlobalException setMessage(String message) + { + this.message = message; + return this; + } +} \ No newline at end of file diff --git a/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/exception/InnerAuthException.java b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/exception/InnerAuthException.java new file mode 100644 index 0000000..5f22d1c --- /dev/null +++ b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/exception/InnerAuthException.java @@ -0,0 +1,16 @@ +package com.aiotagro.common.core.exception; + +/** + * 内部认证异常 + * + * @author Carson + */ +public class InnerAuthException extends RuntimeException +{ + private static final long serialVersionUID = 1L; + + public InnerAuthException(String message) + { + super(message); + } +} diff --git a/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/exception/PreAuthorizeException.java b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/exception/PreAuthorizeException.java new file mode 100644 index 0000000..651242a --- /dev/null +++ b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/exception/PreAuthorizeException.java @@ -0,0 +1,15 @@ +package com.aiotagro.common.core.exception; + +/** + * 权限异常 + * + * @author Carson + */ +public class PreAuthorizeException extends RuntimeException +{ + private static final long serialVersionUID = 1L; + + public PreAuthorizeException() + { + } +} diff --git a/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/exception/ServiceException.java b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/exception/ServiceException.java new file mode 100644 index 0000000..573e1b9 --- /dev/null +++ b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/exception/ServiceException.java @@ -0,0 +1,74 @@ +package com.aiotagro.common.core.exception; + +/** + * 业务异常 + * + * @author Carson + */ +public final class ServiceException extends RuntimeException +{ + private static final long serialVersionUID = 1L; + + /** + * 错误码 + */ + private Integer code; + + /** + * 错误提示 + */ + private String message; + + /** + * 错误明细,内部调试错误 + * + * 和 {@link CommonResult#getDetailMessage()} 一致的设计 + */ + private String detailMessage; + + /** + * 空构造方法,避免反序列化问题 + */ + public ServiceException() + { + } + + public ServiceException(String message) + { + this.message = message; + } + + public ServiceException(String message, Integer code) + { + this.message = message; + this.code = code; + } + + public String getDetailMessage() + { + return detailMessage; + } + + @Override + public String getMessage() + { + return message; + } + + public Integer getCode() + { + return code; + } + + public ServiceException setMessage(String message) + { + this.message = message; + return this; + } + + public ServiceException setDetailMessage(String detailMessage) + { + this.detailMessage = detailMessage; + return this; + } +} \ No newline at end of file diff --git a/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/exception/UtilException.java b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/exception/UtilException.java new file mode 100644 index 0000000..0827ea2 --- /dev/null +++ b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/exception/UtilException.java @@ -0,0 +1,26 @@ +package com.aiotagro.common.core.exception; + +/** + * 工具类异常 + * + * @author Carson + */ +public class UtilException extends RuntimeException +{ + private static final long serialVersionUID = 8247610319171014183L; + + public UtilException(Throwable e) + { + super(e.getMessage(), e); + } + + public UtilException(String message) + { + super(message); + } + + public UtilException(String message, Throwable throwable) + { + super(message, throwable); + } +} diff --git a/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/exception/auth/NotLoginException.java b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/exception/auth/NotLoginException.java new file mode 100644 index 0000000..a2d489f --- /dev/null +++ b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/exception/auth/NotLoginException.java @@ -0,0 +1,16 @@ +package com.aiotagro.common.core.exception.auth; + +/** + * 未能通过的登录认证异常 + * + * @author Carson + */ +public class NotLoginException extends RuntimeException +{ + private static final long serialVersionUID = 1L; + + public NotLoginException(String message) + { + super(message); + } +} diff --git a/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/exception/auth/NotPermissionException.java b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/exception/auth/NotPermissionException.java new file mode 100644 index 0000000..f94362a --- /dev/null +++ b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/exception/auth/NotPermissionException.java @@ -0,0 +1,23 @@ +package com.aiotagro.common.core.exception.auth; + +import org.apache.commons.lang3.StringUtils; + +/** + * 未能通过的权限认证异常 + * + * @author Carson + */ +public class NotPermissionException extends RuntimeException +{ + private static final long serialVersionUID = 1L; + + public NotPermissionException(String permission) + { + super(permission); + } + + public NotPermissionException(String[] permissions) + { + super(StringUtils.join(permissions, ",")); + } +} diff --git a/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/exception/auth/NotRoleException.java b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/exception/auth/NotRoleException.java new file mode 100644 index 0000000..7c2129c --- /dev/null +++ b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/exception/auth/NotRoleException.java @@ -0,0 +1,23 @@ +package com.aiotagro.common.core.exception.auth; + +import org.apache.commons.lang3.StringUtils; + +/** + * 未能通过的角色认证异常 + * + * @author Carson + */ +public class NotRoleException extends RuntimeException +{ + private static final long serialVersionUID = 1L; + + public NotRoleException(String role) + { + super(role); + } + + public NotRoleException(String[] roles) + { + super(StringUtils.join(roles, ",")); + } +} diff --git a/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/exception/base/BaseException.java b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/exception/base/BaseException.java new file mode 100644 index 0000000..6087dec --- /dev/null +++ b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/exception/base/BaseException.java @@ -0,0 +1,79 @@ +package com.aiotagro.common.core.exception.base; + +/** + * 基础异常 + * + * @author Carson + */ +public class BaseException extends RuntimeException +{ + private static final long serialVersionUID = 1L; + + /** + * 所属模块 + */ + private String module; + + /** + * 错误码 + */ + private String code; + + /** + * 错误码对应的参数 + */ + private Object[] args; + + /** + * 错误消息 + */ + private String defaultMessage; + + public BaseException(String module, String code, Object[] args, String defaultMessage) + { + this.module = module; + this.code = code; + this.args = args; + this.defaultMessage = defaultMessage; + } + + public BaseException(String module, String code, Object[] args) + { + this(module, code, args, null); + } + + public BaseException(String module, String defaultMessage) + { + this(module, null, null, defaultMessage); + } + + public BaseException(String code, Object[] args) + { + this(null, code, args, null); + } + + public BaseException(String defaultMessage) + { + this(null, null, null, defaultMessage); + } + + public String getModule() + { + return module; + } + + public String getCode() + { + return code; + } + + public Object[] getArgs() + { + return args; + } + + public String getDefaultMessage() + { + return defaultMessage; + } +} diff --git a/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/exception/file/FileException.java b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/exception/file/FileException.java new file mode 100644 index 0000000..f9fb661 --- /dev/null +++ b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/exception/file/FileException.java @@ -0,0 +1,19 @@ +package com.aiotagro.common.core.exception.file; + +import com.aiotagro.common.core.exception.base.BaseException; + +/** + * 文件信息异常类 + * + * @author Carson + */ +public class FileException extends BaseException +{ + private static final long serialVersionUID = 1L; + + public FileException(String code, Object[] args, String msg) + { + super("file", code, args, msg); + } + +} diff --git a/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/exception/file/FileNameLengthLimitExceededException.java b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/exception/file/FileNameLengthLimitExceededException.java new file mode 100644 index 0000000..481d61c --- /dev/null +++ b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/exception/file/FileNameLengthLimitExceededException.java @@ -0,0 +1,16 @@ +package com.aiotagro.common.core.exception.file; + +/** + * 文件名称超长限制异常类 + * + * @author Carson + */ +public class FileNameLengthLimitExceededException extends FileException +{ + private static final long serialVersionUID = 1L; + + public FileNameLengthLimitExceededException(int defaultFileNameLength) + { + super("upload.filename.exceed.length", new Object[] { defaultFileNameLength }, "the filename is too long"); + } +} diff --git a/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/exception/file/FileSizeLimitExceededException.java b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/exception/file/FileSizeLimitExceededException.java new file mode 100644 index 0000000..6a0f3fe --- /dev/null +++ b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/exception/file/FileSizeLimitExceededException.java @@ -0,0 +1,16 @@ +package com.aiotagro.common.core.exception.file; + +/** + * 文件名大小限制异常类 + * + * @author Carson + */ +public class FileSizeLimitExceededException extends FileException +{ + private static final long serialVersionUID = 1L; + + public FileSizeLimitExceededException(long defaultMaxSize) + { + super("upload.exceed.maxSize", new Object[] { defaultMaxSize }, "the filesize is too large"); + } +} diff --git a/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/exception/file/FileUploadException.java b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/exception/file/FileUploadException.java new file mode 100644 index 0000000..d0770b4 --- /dev/null +++ b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/exception/file/FileUploadException.java @@ -0,0 +1,61 @@ +package com.aiotagro.common.core.exception.file; + +import java.io.PrintStream; +import java.io.PrintWriter; + +/** + * 文件上传异常类 + * + * @author Carson + */ +public class FileUploadException extends Exception +{ + + private static final long serialVersionUID = 1L; + + private final Throwable cause; + + public FileUploadException() + { + this(null, null); + } + + public FileUploadException(final String msg) + { + this(msg, null); + } + + public FileUploadException(String msg, Throwable cause) + { + super(msg); + this.cause = cause; + } + + @Override + public void printStackTrace(PrintStream stream) + { + super.printStackTrace(stream); + if (cause != null) + { + stream.println("Caused by:"); + cause.printStackTrace(stream); + } + } + + @Override + public void printStackTrace(PrintWriter writer) + { + super.printStackTrace(writer); + if (cause != null) + { + writer.println("Caused by:"); + cause.printStackTrace(writer); + } + } + + @Override + public Throwable getCause() + { + return cause; + } +} diff --git a/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/exception/file/InvalidExtensionException.java b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/exception/file/InvalidExtensionException.java new file mode 100644 index 0000000..5fe9aca --- /dev/null +++ b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/exception/file/InvalidExtensionException.java @@ -0,0 +1,80 @@ +package com.aiotagro.common.core.exception.file; + +import java.util.Arrays; + +/** + * 文件上传 误异常类 + * + * @author Carson + */ +public class InvalidExtensionException extends FileUploadException +{ + private static final long serialVersionUID = 1L; + + private String[] allowedExtension; + private String extension; + private String filename; + + public InvalidExtensionException(String[] allowedExtension, String extension, String filename) + { + super("filename : [" + filename + "], extension : [" + extension + "], allowed extension : [" + Arrays.toString(allowedExtension) + "]"); + this.allowedExtension = allowedExtension; + this.extension = extension; + this.filename = filename; + } + + public String[] getAllowedExtension() + { + return allowedExtension; + } + + public String getExtension() + { + return extension; + } + + public String getFilename() + { + return filename; + } + + public static class InvalidImageExtensionException extends InvalidExtensionException + { + private static final long serialVersionUID = 1L; + + public InvalidImageExtensionException(String[] allowedExtension, String extension, String filename) + { + super(allowedExtension, extension, filename); + } + } + + public static class InvalidFlashExtensionException extends InvalidExtensionException + { + private static final long serialVersionUID = 1L; + + public InvalidFlashExtensionException(String[] allowedExtension, String extension, String filename) + { + super(allowedExtension, extension, filename); + } + } + + public static class InvalidMediaExtensionException extends InvalidExtensionException + { + private static final long serialVersionUID = 1L; + + public InvalidMediaExtensionException(String[] allowedExtension, String extension, String filename) + { + super(allowedExtension, extension, filename); + } + } + + public static class InvalidVideoExtensionException extends InvalidExtensionException + { + private static final long serialVersionUID = 1L; + + public InvalidVideoExtensionException(String[] allowedExtension, String extension, String filename) + { + super(allowedExtension, extension, filename); + } + } +} diff --git a/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/exception/job/TaskException.java b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/exception/job/TaskException.java new file mode 100644 index 0000000..8a23abe --- /dev/null +++ b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/exception/job/TaskException.java @@ -0,0 +1,34 @@ +package com.aiotagro.common.core.exception.job; + +/** + * 计划策略异常 + * + * @author Carson + */ +public class TaskException extends Exception +{ + private static final long serialVersionUID = 1L; + + private Code code; + + public TaskException(String msg, Code code) + { + this(msg, code, null); + } + + public TaskException(String msg, Code code, Exception nestedEx) + { + super(msg, nestedEx); + this.code = code; + } + + public Code getCode() + { + return code; + } + + public enum Code + { + TASK_EXISTS, NO_TASK_EXISTS, TASK_ALREADY_STARTED, UNKNOWN, CONFIG_ERROR, TASK_NODE_NOT_AVAILABLE + } +} \ No newline at end of file diff --git a/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/exception/user/CaptchaExpireException.java b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/exception/user/CaptchaExpireException.java new file mode 100644 index 0000000..70b23e8 --- /dev/null +++ b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/exception/user/CaptchaExpireException.java @@ -0,0 +1,16 @@ +package com.aiotagro.common.core.exception.user; + +/** + * 验证码失效异常类 + * + * @author Carson + */ +public class CaptchaExpireException extends UserException +{ + private static final long serialVersionUID = 1L; + + public CaptchaExpireException() + { + super("user.jcaptcha.expire", null); + } +} diff --git a/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/exception/user/UserException.java b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/exception/user/UserException.java new file mode 100644 index 0000000..065a9dd --- /dev/null +++ b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/exception/user/UserException.java @@ -0,0 +1,18 @@ +package com.aiotagro.common.core.exception.user; + +import com.aiotagro.common.core.exception.base.BaseException; + +/** + * 用户信息异常类 + * + * @author Carson + */ +public class UserException extends BaseException +{ + private static final long serialVersionUID = 1L; + + public UserException(String code, Object[] args) + { + super("user", code, args, null); + } +} diff --git a/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/exception/user/UserPasswordNotMatchException.java b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/exception/user/UserPasswordNotMatchException.java new file mode 100644 index 0000000..3430042 --- /dev/null +++ b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/exception/user/UserPasswordNotMatchException.java @@ -0,0 +1,16 @@ +package com.aiotagro.common.core.exception.user; + +/** + * 用户密码不正确或不符合规范异常类 + * + * @author Carson + */ +public class UserPasswordNotMatchException extends UserException +{ + private static final long serialVersionUID = 1L; + + public UserPasswordNotMatchException() + { + super("user.password.not.match", null); + } +} diff --git a/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/text/CharsetKit.java b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/text/CharsetKit.java new file mode 100644 index 0000000..c261989 --- /dev/null +++ b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/text/CharsetKit.java @@ -0,0 +1,87 @@ +package com.aiotagro.common.core.text; + +import com.aiotagro.common.core.utils.StringUtils; + +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; + +/** + * 字符集工具类 + * + * @author Carson + */ +public class CharsetKit +{ + /** ISO-8859-1 */ + public static final String ISO_8859_1 = "ISO-8859-1"; + /** UTF-8 */ + public static final String UTF_8 = "UTF-8"; + /** GBK */ + public static final String GBK = "GBK"; + + /** ISO-8859-1 */ + public static final Charset CHARSET_ISO_8859_1 = Charset.forName(ISO_8859_1); + /** UTF-8 */ + public static final Charset CHARSET_UTF_8 = Charset.forName(UTF_8); + /** GBK */ + public static final Charset CHARSET_GBK = Charset.forName(GBK); + + /** + * 转换为Charset对象 + * + * @param charset 字符集,为空则返回默认字符集 + * @return Charset + */ + public static Charset charset(String charset) + { + return StringUtils.isEmpty(charset) ? Charset.defaultCharset() : Charset.forName(charset); + } + + /** + * 转换字符串的字符集编码 + * + * @param source 字符串 + * @param srcCharset 源字符集,默认ISO-8859-1 + * @param destCharset 目标字符集,默认UTF-8 + * @return 转换后的字符集 + */ + public static String convert(String source, String srcCharset, String destCharset) + { + return convert(source, Charset.forName(srcCharset), Charset.forName(destCharset)); + } + + /** + * 转换字符串的字符集编码 + * + * @param source 字符串 + * @param srcCharset 源字符集,默认ISO-8859-1 + * @param destCharset 目标字符集,默认UTF-8 + * @return 转换后的字符集 + */ + public static String convert(String source, Charset srcCharset, Charset destCharset) + { + if (null == srcCharset) + { + srcCharset = StandardCharsets.ISO_8859_1; + } + + if (null == destCharset) + { + destCharset = StandardCharsets.UTF_8; + } + + if (StringUtils.isEmpty(source) || srcCharset.equals(destCharset)) + { + return source; + } + return new String(source.getBytes(srcCharset), destCharset); + } + + /** + * @return 系统字符集编码 + */ + public static String systemCharset() + { + return Charset.defaultCharset().name(); + } +} diff --git a/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/text/Convert.java b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/text/Convert.java new file mode 100644 index 0000000..7542221 --- /dev/null +++ b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/text/Convert.java @@ -0,0 +1,1013 @@ +package com.aiotagro.common.core.text; + +import com.aiotagro.common.core.utils.StringUtils; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.math.RoundingMode; +import java.nio.ByteBuffer; +import java.nio.charset.Charset; +import java.text.NumberFormat; +import java.util.Set; + +/** + * 类型转换器 + * + * @author Carson + */ +public class Convert +{ + /** + * 转换为字符串
+ * 如果给定的值为null,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static String toStr(Object value, String defaultValue) + { + if (null == value) + { + return defaultValue; + } + if (value instanceof String) + { + return (String) value; + } + return value.toString(); + } + + /** + * 转换为字符串
+ * 如果给定的值为null,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static String toStr(Object value) + { + return toStr(value, null); + } + + /** + * 转换为字符
+ * 如果给定的值为null,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Character toChar(Object value, Character defaultValue) + { + if (null == value) + { + return defaultValue; + } + if (value instanceof Character) + { + return (Character) value; + } + + final String valueStr = toStr(value, null); + return StringUtils.isEmpty(valueStr) ? defaultValue : valueStr.charAt(0); + } + + /** + * 转换为字符
+ * 如果给定的值为null,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static Character toChar(Object value) + { + return toChar(value, null); + } + + /** + * 转换为byte
+ * 如果给定的值为null,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Byte toByte(Object value, Byte defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof Byte) + { + return (Byte) value; + } + if (value instanceof Number) + { + return ((Number) value).byteValue(); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + return Byte.parseByte(valueStr); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为byte
+ * 如果给定的值为null,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static Byte toByte(Object value) + { + return toByte(value, null); + } + + /** + * 转换为Short
+ * 如果给定的值为null,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Short toShort(Object value, Short defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof Short) + { + return (Short) value; + } + if (value instanceof Number) + { + return ((Number) value).shortValue(); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + return Short.parseShort(valueStr.trim()); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为Short
+ * 如果给定的值为null,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static Short toShort(Object value) + { + return toShort(value, null); + } + + /** + * 转换为Number
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Number toNumber(Object value, Number defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof Number) + { + return (Number) value; + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + return NumberFormat.getInstance().parse(valueStr); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为Number
+ * 如果给定的值为空,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static Number toNumber(Object value) + { + return toNumber(value, null); + } + + /** + * 转换为int
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Integer toInt(Object value, Integer defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof Integer) + { + return (Integer) value; + } + if (value instanceof Number) + { + return ((Number) value).intValue(); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + return Integer.parseInt(valueStr.trim()); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为int
+ * 如果给定的值为null,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static Integer toInt(Object value) + { + return toInt(value, null); + } + + /** + * 转换为Integer数组
+ * + * @param str 被转换的值 + * @return 结果 + */ + public static Integer[] toIntArray(String str) + { + return toIntArray(",", str); + } + + /** + * 转换为Long数组
+ * + * @param str 被转换的值 + * @return 结果 + */ + public static Long[] toLongArray(String str) + { + return toLongArray(",", str); + } + + /** + * 转换为Integer数组
+ * + * @param split 分隔符 + * @param str 被转换的值 + * @return 结果 + */ + public static Integer[] toIntArray(String split, String str) + { + if (StringUtils.isEmpty(str)) + { + return new Integer[] {}; + } + String[] arr = str.split(split); + final Integer[] ints = new Integer[arr.length]; + for (int i = 0; i < arr.length; i++) + { + final Integer v = toInt(arr[i], 0); + ints[i] = v; + } + return ints; + } + + /** + * 转换为Long数组
+ * + * @param split 分隔符 + * @param str 被转换的值 + * @return 结果 + */ + public static Long[] toLongArray(String split, String str) + { + if (StringUtils.isEmpty(str)) + { + return new Long[] {}; + } + String[] arr = str.split(split); + final Long[] longs = new Long[arr.length]; + for (int i = 0; i < arr.length; i++) + { + final Long v = toLong(arr[i], null); + longs[i] = v; + } + return longs; + } + + /** + * 转换为String数组
+ * + * @param str 被转换的值 + * @return 结果 + */ + public static String[] toStrArray(String str) + { + return toStrArray(",", str); + } + + /** + * 转换为String数组
+ * + * @param split 分隔符 + * @param str 被转换的值 + * @return 结果 + */ + public static String[] toStrArray(String split, String str) + { + return str.split(split); + } + + /** + * 转换为long
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Long toLong(Object value, Long defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof Long) + { + return (Long) value; + } + if (value instanceof Number) + { + return ((Number) value).longValue(); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + // 支持科学计数法 + return new BigDecimal(valueStr.trim()).longValue(); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为long
+ * 如果给定的值为null,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static Long toLong(Object value) + { + return toLong(value, null); + } + + /** + * 转换为double
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Double toDouble(Object value, Double defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof Double) + { + return (Double) value; + } + if (value instanceof Number) + { + return ((Number) value).doubleValue(); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + // 支持科学计数法 + return new BigDecimal(valueStr.trim()).doubleValue(); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为double
+ * 如果给定的值为空,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static Double toDouble(Object value) + { + return toDouble(value, null); + } + + /** + * 转换为Float
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Float toFloat(Object value, Float defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof Float) + { + return (Float) value; + } + if (value instanceof Number) + { + return ((Number) value).floatValue(); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + return Float.parseFloat(valueStr.trim()); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为Float
+ * 如果给定的值为空,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static Float toFloat(Object value) + { + return toFloat(value, null); + } + + /** + * 转换为boolean
+ * String支持的值为:true、false、yes、ok、no,1,0 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Boolean toBool(Object value, Boolean defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof Boolean) + { + return (Boolean) value; + } + String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + valueStr = valueStr.trim().toLowerCase(); + switch (valueStr) + { + case "true": + case "yes": + case "ok": + case "1": + return true; + case "false": + case "no": + case "0": + return false; + default: + return defaultValue; + } + } + + /** + * 转换为boolean
+ * 如果给定的值为空,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static Boolean toBool(Object value) + { + return toBool(value, null); + } + + /** + * 转换为Enum对象
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * + * @param clazz Enum的Class + * @param value 值 + * @param defaultValue 默认值 + * @return Enum + */ + public static > E toEnum(Class clazz, Object value, E defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (clazz.isAssignableFrom(value.getClass())) + { + @SuppressWarnings("unchecked") + E myE = (E) value; + return myE; + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + return Enum.valueOf(clazz, valueStr); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为Enum对象
+ * 如果给定的值为空,或者转换失败,返回默认值null
+ * + * @param clazz Enum的Class + * @param value 值 + * @return Enum + */ + public static > E toEnum(Class clazz, Object value) + { + return toEnum(clazz, value, null); + } + + /** + * 转换为BigInteger
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static BigInteger toBigInteger(Object value, BigInteger defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof BigInteger) + { + return (BigInteger) value; + } + if (value instanceof Long) + { + return BigInteger.valueOf((Long) value); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + return new BigInteger(valueStr); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为BigInteger
+ * 如果给定的值为空,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static BigInteger toBigInteger(Object value) + { + return toBigInteger(value, null); + } + + /** + * 转换为BigDecimal
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static BigDecimal toBigDecimal(Object value, BigDecimal defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof BigDecimal) + { + return (BigDecimal) value; + } + if (value instanceof Long) + { + return new BigDecimal((Long) value); + } + if (value instanceof Double) + { + return BigDecimal.valueOf((Double) value); + } + if (value instanceof Integer) + { + return new BigDecimal((Integer) value); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + return new BigDecimal(valueStr); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为BigDecimal
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static BigDecimal toBigDecimal(Object value) + { + return toBigDecimal(value, null); + } + + /** + * 将对象转为字符串
+ * 1、Byte数组和ByteBuffer会被转换为对应字符串的数组 2、对象数组会调用Arrays.toString方法 + * + * @param obj 对象 + * @return 字符串 + */ + public static String utf8Str(Object obj) + { + return str(obj, CharsetKit.CHARSET_UTF_8); + } + + /** + * 将对象转为字符串
+ * 1、Byte数组和ByteBuffer会被转换为对应字符串的数组 2、对象数组会调用Arrays.toString方法 + * + * @param obj 对象 + * @param charsetName 字符集 + * @return 字符串 + */ + public static String str(Object obj, String charsetName) + { + return str(obj, Charset.forName(charsetName)); + } + + /** + * 将对象转为字符串
+ * 1、Byte数组和ByteBuffer会被转换为对应字符串的数组 2、对象数组会调用Arrays.toString方法 + * + * @param obj 对象 + * @param charset 字符集 + * @return 字符串 + */ + public static String str(Object obj, Charset charset) + { + if (null == obj) + { + return null; + } + + if (obj instanceof String) + { + return (String) obj; + } + else if (obj instanceof byte[] || obj instanceof Byte[]) + { + if (obj instanceof byte[]) + { + return str((byte[]) obj, charset); + } + else + { + Byte[] bytes = (Byte[]) obj; + int length = bytes.length; + byte[] dest = new byte[length]; + for (int i = 0; i < length; i++) + { + dest[i] = bytes[i]; + } + return str(dest, charset); + } + } + else if (obj instanceof ByteBuffer) + { + return str((ByteBuffer) obj, charset); + } + return obj.toString(); + } + + /** + * 将byte数组转为字符串 + * + * @param bytes byte数组 + * @param charset 字符集 + * @return 字符串 + */ + public static String str(byte[] bytes, String charset) + { + return str(bytes, StringUtils.isEmpty(charset) ? Charset.defaultCharset() : Charset.forName(charset)); + } + + /** + * 解码字节码 + * + * @param data 字符串 + * @param charset 字符集,如果此字段为空,则解码的结果取决于平台 + * @return 解码后的字符串 + */ + public static String str(byte[] data, Charset charset) + { + if (data == null) + { + return null; + } + + if (null == charset) + { + return new String(data); + } + return new String(data, charset); + } + + /** + * 将编码的byteBuffer数据转换为字符串 + * + * @param data 数据 + * @param charset 字符集,如果为空使用当前系统字符集 + * @return 字符串 + */ + public static String str(ByteBuffer data, String charset) + { + if (data == null) + { + return null; + } + + return str(data, Charset.forName(charset)); + } + + /** + * 将编码的byteBuffer数据转换为字符串 + * + * @param data 数据 + * @param charset 字符集,如果为空使用当前系统字符集 + * @return 字符串 + */ + public static String str(ByteBuffer data, Charset charset) + { + if (null == charset) + { + charset = Charset.defaultCharset(); + } + return charset.decode(data).toString(); + } + + // ----------------------------------------------------------------------- 全角半角转换 + /** + * 半角转全角 + * + * @param input String. + * @return 全角字符串. + */ + public static String toSBC(String input) + { + return toSBC(input, null); + } + + /** + * 半角转全角 + * + * @param input String + * @param notConvertSet 不替换的字符集合 + * @return 全角字符串. + */ + public static String toSBC(String input, Set notConvertSet) + { + char[] c = input.toCharArray(); + for (int i = 0; i < c.length; i++) + { + if (null != notConvertSet && notConvertSet.contains(c[i])) + { + // 跳过不替换的字符 + continue; + } + + if (c[i] == ' ') + { + c[i] = '\u3000'; + } + else if (c[i] < '\177') + { + c[i] = (char) (c[i] + 65248); + + } + } + return new String(c); + } + + /** + * 全角转半角 + * + * @param input String. + * @return 半角字符串 + */ + public static String toDBC(String input) + { + return toDBC(input, null); + } + + /** + * 替换全角为半角 + * + * @param text 文本 + * @param notConvertSet 不替换的字符集合 + * @return 替换后的字符 + */ + public static String toDBC(String text, Set notConvertSet) + { + char[] c = text.toCharArray(); + for (int i = 0; i < c.length; i++) + { + if (null != notConvertSet && notConvertSet.contains(c[i])) + { + // 跳过不替换的字符 + continue; + } + + if (c[i] == '\u3000') + { + c[i] = ' '; + } + else if (c[i] > '\uFF00' && c[i] < '\uFF5F') + { + c[i] = (char) (c[i] - 65248); + } + } + return new String(c); + } + + /** + * 数字金额大写转换 先写个完整的然后将如零拾替换成零 + * + * @param n 数字 + * @return 中文大写数字 + */ + public static String digitUppercase(double n) + { + String[] fraction = { "角", "分" }; + String[] digit = { "零", "壹", "贰", "叁", "肆", "伍", "陆", "柒", "捌", "玖" }; + String[][] unit = { { "元", "万", "亿" }, { "", "拾", "佰", "仟" } }; + + String head = n < 0 ? "负" : ""; + n = Math.abs(n); + + String s = ""; + for (int i = 0; i < fraction.length; i++) + { + // 优化double计算精度丢失问题 + BigDecimal nNum = new BigDecimal(n); + BigDecimal decimal = new BigDecimal(10); + BigDecimal scale = nNum.multiply(decimal).setScale(2, RoundingMode.HALF_EVEN); + double d = scale.doubleValue(); + s += (digit[(int) (Math.floor(d * Math.pow(10, i)) % 10)] + fraction[i]).replaceAll("(零.)+", ""); + } + if (s.length() < 1) + { + s = "整"; + } + int integerPart = (int) Math.floor(n); + + for (int i = 0; i < unit[0].length && integerPart > 0; i++) + { + String p = ""; + for (int j = 0; j < unit[1].length && n > 0; j++) + { + p = digit[integerPart % 10] + unit[1][j] + p; + integerPart = integerPart / 10; + } + s = p.replaceAll("(零.)*零$", "").replaceAll("^$", "零") + unit[0][i] + s; + } + return head + s.replaceAll("(零.)*零元", "元").replaceFirst("(零.)+", "").replaceAll("(零.)+", "零").replaceAll("^整$", "零元整"); + } +} diff --git a/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/text/StrFormatter.java b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/text/StrFormatter.java new file mode 100644 index 0000000..c5ee341 --- /dev/null +++ b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/text/StrFormatter.java @@ -0,0 +1,92 @@ +package com.aiotagro.common.core.text; + +import com.aiotagro.common.core.utils.StringUtils; + +/** + * 字符串格式化 + * + * @author Carson + */ +public class StrFormatter +{ + public static final String EMPTY_JSON = "{}"; + public static final char C_BACKSLASH = '\\'; + public static final char C_DELIM_START = '{'; + public static final char C_DELIM_END = '}'; + + /** + * 格式化字符串
+ * 此方法只是简单将占位符 {} 按照顺序替换为参数
+ * 如果想输出 {} 使用 \\转义 { 即可,如果想输出 {} 之前的 \ 使用双转义符 \\\\ 即可
+ * 例:
+ * 通常使用:format("this is {} for {}", "a", "b") -> this is a for b
+ * 转义{}: format("this is \\{} for {}", "a", "b") -> this is \{} for a
+ * 转义\: format("this is \\\\{} for {}", "a", "b") -> this is \a for b
+ * + * @param strPattern 字符串模板 + * @param argArray 参数列表 + * @return 结果 + */ + public static String format(final String strPattern, final Object... argArray) + { + if (StringUtils.isEmpty(strPattern) || StringUtils.isEmpty(argArray)) + { + return strPattern; + } + final int strPatternLength = strPattern.length(); + + // 初始化定义好的长度以获得更好的性能 + StringBuilder sbuf = new StringBuilder(strPatternLength + 50); + + int handledPosition = 0; + int delimIndex;// 占位符所在位置 + for (int argIndex = 0; argIndex < argArray.length; argIndex++) + { + delimIndex = strPattern.indexOf(EMPTY_JSON, handledPosition); + if (delimIndex == -1) + { + if (handledPosition == 0) + { + return strPattern; + } + else + { // 字符串模板剩余部分不再包含占位符,加入剩余部分后返回结果 + sbuf.append(strPattern, handledPosition, strPatternLength); + return sbuf.toString(); + } + } + else + { + if (delimIndex > 0 && strPattern.charAt(delimIndex - 1) == C_BACKSLASH) + { + if (delimIndex > 1 && strPattern.charAt(delimIndex - 2) == C_BACKSLASH) + { + // 转义符之前还有一个转义符,占位符依旧有效 + sbuf.append(strPattern, handledPosition, delimIndex - 1); + sbuf.append(Convert.utf8Str(argArray[argIndex])); + handledPosition = delimIndex + 2; + } + else + { + // 占位符被转义 + argIndex--; + sbuf.append(strPattern, handledPosition, delimIndex - 1); + sbuf.append(C_DELIM_START); + handledPosition = delimIndex + 1; + } + } + else + { + // 正常占位符 + sbuf.append(strPattern, handledPosition, delimIndex); + sbuf.append(Convert.utf8Str(argArray[argIndex])); + handledPosition = delimIndex + 2; + } + } + } + // 加入最后一个占位符后所有的字符 + sbuf.append(strPattern, handledPosition, strPattern.length()); + + return sbuf.toString(); + } +} diff --git a/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/utils/DateUtils.java b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/utils/DateUtils.java new file mode 100644 index 0000000..ca6201f --- /dev/null +++ b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/utils/DateUtils.java @@ -0,0 +1,214 @@ +package com.aiotagro.common.core.utils; + +import org.apache.commons.lang3.time.DateFormatUtils; + +import java.lang.management.ManagementFactory; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.time.*; +import java.util.Date; + +/** + * 时间工具类 + * + * @author Carson + */ +public class DateUtils extends org.apache.commons.lang3.time.DateUtils { + public static String YYYY = "yyyy"; + + public static String YYYY_MM = "yyyy-MM"; + + public static String YYYY_MM_DD = "yyyy-MM-dd"; + + public static String YYYYMMDDHHMMSS = "yyyyMMddHHmmss"; + + public static String YYYY_MM_DD_HH_MM_SS = "yyyy-MM-dd HH:mm:ss"; + + private static String[] parsePatterns = { + "yyyy-MM-dd", "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm", "yyyy-MM", + "yyyy/MM/dd", "yyyy/MM/dd HH:mm:ss", "yyyy/MM/dd HH:mm", "yyyy/MM", + "yyyy.MM.dd", "yyyy.MM.dd HH:mm:ss", "yyyy.MM.dd HH:mm", "yyyy.MM"}; + + /** + * 获取当前Date型日期 + * + * @return Date() 当前日期 + */ + public static Date getNowDate() { + return new Date(); + } + + /** + * 获取当前日期, 默认格式为yyyy-MM-dd + * + * @return String + */ + public static String getDate() { + return dateTimeNow(YYYY_MM_DD); + } + + public static final String getTime() { + return dateTimeNow(YYYY_MM_DD_HH_MM_SS); + } + + public static final String dateTimeNow() { + return dateTimeNow(YYYYMMDDHHMMSS); + } + + public static final String dateTimeNow(final String format) { + return parseDateToStr(format, new Date()); + } + + public static final String dateTime(final Date date) { + return parseDateToStr(YYYY_MM_DD, date); + } + + public static final String parseDateToStr(final String format, final Date date) { + return new SimpleDateFormat(format).format(date); + } + + public static final Date dateTime(final String format, final String ts) { + try { + return new SimpleDateFormat(format).parse(ts); + } catch (ParseException e) { + throw new RuntimeException(e); + } + } + + /** + * 将字符串日期转换为日期格式 + * + * @param datestr + * @return + */ + public static Date stringToDate(String datestr, String format) { + + if (datestr == null || datestr.equals("")) { + return null; + } + Date date = new Date(); + SimpleDateFormat df = new SimpleDateFormat(format); + try { + date = df.parse(datestr); + } catch (Exception e) { + e.printStackTrace(); + } + return date; + } + + /** + * 日期路径 即年/月/日 如2018/08/08 + */ + public static final String datePath() { + Date now = new Date(); + return DateFormatUtils.format(now, "yyyy/MM/dd"); + } + + /** + * 日期路径 即年/月/日 如20180808 + */ + public static final String dateTime() { + Date now = new Date(); + return DateFormatUtils.format(now, "yyyyMMdd"); + } + + /** + * 日期型字符串转化为日期 格式 + */ + public static Date parseDate(Object str) { + if (str == null) { + return null; + } + try { + return parseDate(str.toString(), parsePatterns); + } catch (ParseException e) { + return null; + } + } + + /** + * 获取服务器启动时间 + */ + public static Date getServerStartDate() { + long time = ManagementFactory.getRuntimeMXBean().getStartTime(); + return new Date(time); + } + + /** + * 计算时间差 + * + * @param endDate 最后时间 + * @param startTime 开始时间 + * @return 时间差(天/小时/分钟) + */ + public static String timeDistance(Date endDate, Date startTime) { + long nd = 1000 * 24 * 60 * 60; + long nh = 1000 * 60 * 60; + long nm = 1000 * 60; + // long ns = 1000; + // 获得两个时间的毫秒时间差异 + long diff = endDate.getTime() - startTime.getTime(); + // 计算差多少天 + long day = diff / nd; + // 计算差多少小时 + long hour = diff % nd / nh; + // 计算差多少分钟 + long min = diff % nd % nh / nm; + // 计算差多少秒//输出结果 + // long sec = diff % nd % nh % nm / ns; + return day + "天" + hour + "小时" + min + "分钟"; + } + + /** + * 增加 LocalDateTime ==> Date + */ + public static Date toDate(LocalDateTime temporalAccessor) { + ZonedDateTime zdt = temporalAccessor.atZone(ZoneId.systemDefault()); + return Date.from(zdt.toInstant()); + } + + /** + * 增加 LocalDate ==> Date + */ + public static Date toDate(LocalDate temporalAccessor) { + LocalDateTime localDateTime = LocalDateTime.of(temporalAccessor, LocalTime.of(0, 0, 0)); + ZonedDateTime zdt = localDateTime.atZone(ZoneId.systemDefault()); + return Date.from(zdt.toInstant()); + } + + /** + * 时间比较 + * + * @param time + * @param hour + * @return + */ + public static boolean compareWithHours(Date time, Integer hour) { + if (time == null) { + return false; + } + // 三小时前的日期 + LocalDateTime threeHoursAgo = LocalDateTime.now().minusHours(hour); + LocalDateTime dateTime = LocalDateTime.ofInstant(time.toInstant(), ZoneId.systemDefault()); + if (dateTime.isAfter(threeHoursAgo) || dateTime.isEqual(threeHoursAgo)) { + return true; + } else { + return false; + } + } + + /** + * Date转为cron表达式 + * @param date + * @return + */ + public static String dateToCron(Date date){ + String dateFormat="ss mm HH"; + SimpleDateFormat sdf = new SimpleDateFormat(dateFormat); + String formatTimeStr = null; + if (date != null) { + formatTimeStr = sdf.format(date)+" * * ?"; + } + return formatTimeStr; + } +} diff --git a/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/utils/EnumUtil.java b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/utils/EnumUtil.java new file mode 100644 index 0000000..9b8fded --- /dev/null +++ b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/utils/EnumUtil.java @@ -0,0 +1,25 @@ +package com.aiotagro.common.core.utils; + +import java.lang.reflect.Field; + +/** + * @author Carson + * @package_name com.aiotagro.common.core.utils + * @date 2024/7/9 下午2:32 + */ +public class EnumUtil { + public static > T getEnumConstant(Class enumClass, int value) { + for (T enumConstant : enumClass.getEnumConstants()) { + try { + Field field = enumConstant.getClass().getDeclaredField("value"); + field.setAccessible(true); + if (field.getInt(enumConstant) == value) { + return enumConstant; + } + } catch (NoSuchFieldException | IllegalAccessException e) { + throw new IllegalArgumentException("枚举类 " + enumClass.getSimpleName() + " 没有定义 value 字段或访问权限不足", e); + } + } + throw new IllegalArgumentException("在枚举类 " + enumClass.getSimpleName() + " 中未找到对应的值: " + value); + } +} diff --git a/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/utils/ExceptionUtil.java b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/utils/ExceptionUtil.java new file mode 100644 index 0000000..e422853 --- /dev/null +++ b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/utils/ExceptionUtil.java @@ -0,0 +1,40 @@ +package com.aiotagro.common.core.utils; + +import org.apache.commons.lang3.exception.ExceptionUtils; + +import java.io.PrintWriter; +import java.io.StringWriter; + +/** + * 错误信息处理类。 + * + * @author Carson + */ +public class ExceptionUtil +{ + /** + * 获取exception的详细错误信息。 + */ + public static String getExceptionMessage(Throwable e) + { + StringWriter sw = new StringWriter(); + e.printStackTrace(new PrintWriter(sw, true)); + return sw.toString(); + } + + public static String getRootErrorMessage(Exception e) + { + Throwable root = ExceptionUtils.getRootCause(e); + root = (root == null ? e : root); + if (root == null) + { + return ""; + } + String msg = root.getMessage(); + if (msg == null) + { + return "null"; + } + return StringUtils.defaultString(msg); + } +} diff --git a/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/utils/JwtUtils.java b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/utils/JwtUtils.java new file mode 100644 index 0000000..a090acd --- /dev/null +++ b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/utils/JwtUtils.java @@ -0,0 +1,142 @@ +package com.aiotagro.common.core.utils; + +import com.aiotagro.common.core.constant.SecurityConstants; +import com.aiotagro.common.core.constant.TokenConstants; +import com.aiotagro.common.core.text.Convert; +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.SignatureAlgorithm; + +import java.util.Map; + +/** + * Jwt工具类 + * + * @author Carson + */ +public class JwtUtils +{ + public static String secret = TokenConstants.SECRET; + + /** + * 从数据声明生成令牌 + * + * @param claims 数据声明 + * @return 令牌 + */ + public static String createToken(Map claims) + { + String token = Jwts.builder().setClaims(claims).signWith(SignatureAlgorithm.HS512, secret).compact(); + return token; + } + + /** + * 从令牌中获取数据声明 + * + * @param token 令牌 + * @return 数据声明 + */ + public static Claims parseToken(String token) + { + return Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody(); + } + + /** + * 根据令牌获取用户标识 + * + * @param token 令牌 + * @return 用户ID + */ + public static String getUserKey(String token) + { + Claims claims = parseToken(token); + return getValue(claims, SecurityConstants.USER_KEY); + } + + /** + * 根据令牌获取用户标识 + * + * @param claims 身份信息 + * @return 用户ID + */ + public static String getUserKey(Claims claims) + { + return getValue(claims, SecurityConstants.USER_KEY); + } + + /** + * 根据令牌获取用户ID + * + * @param token 令牌 + * @return 用户ID + */ + public static String getUserId(String token) + { + Claims claims = parseToken(token); + return getValue(claims, SecurityConstants.DETAILS_USER_ID); + } + + /** + * 根据身份信息获取用户ID + * + * @param claims 身份信息 + * @return 用户ID + */ + public static String getUserId(Claims claims) + { + return getValue(claims, SecurityConstants.DETAILS_USER_ID); + } + + /** + * 根据令牌获取用户名 + * + * @param token 令牌 + * @return 用户名 + */ + public static String getUserName(String token) + { + Claims claims = parseToken(token); + return getValue(claims, SecurityConstants.DETAILS_USERNAME); + } + + /** + * 根据身份信息获取用户名 + * + * @param claims 身份信息 + * @return 用户名 + */ + public static String getUserName(Claims claims) + { + return getValue(claims, SecurityConstants.DETAILS_USERNAME); + } + + + /* + * 根据令牌获取租户ID + */ + public static String getTenantId(String token) { + Claims claims = parseToken(token); + return getValue(claims, SecurityConstants.DETAILS_TENANT_ID); + + } + + /** + * 根据身份信息获取租户ID + **/ + public static String getTenantId(Claims claims) { + return getValue(claims, SecurityConstants.DETAILS_TENANT_ID); + } + + + /** + * 根据身份信息获取键值 + * + * @param claims 身份信息 + * @param key 键 + * @return 值 + */ + public static String getValue(Claims claims, String key) + { + return Convert.toStr(claims.get(key), ""); + } +} diff --git a/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/utils/LngLonUtil.java b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/utils/LngLonUtil.java new file mode 100644 index 0000000..70c55c4 --- /dev/null +++ b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/utils/LngLonUtil.java @@ -0,0 +1,158 @@ +package com.aiotagro.common.core.utils; + +/** + * WGPS84、高德、百度、腾讯编码互转 + */ +public class LngLonUtil { + + public static double pi = 3.1415926535897932384626; + public static double x_pi = 3.14159265358979324 * 3000.0 / 180.0; + public static double a = 6378245.0; + public static double ee = 0.00669342162296594323; + + public static double transformLat(double x, double y) { + double ret = -100.0 + 2.0 * x + 3.0 * y + 0.2 * y * y + 0.1 * x * y + 0.2 * Math.sqrt(Math.abs(x)); + ret += (20.0 * Math.sin(6.0 * x * pi) + 20.0 * Math.sin(2.0 * x * pi)) * 2.0 / 3.0; + ret += (20.0 * Math.sin(y * pi) + 40.0 * Math.sin(y / 3.0 * pi)) * 2.0 / 3.0; + ret += (160.0 * Math.sin(y / 12.0 * pi) + 320 * Math.sin(y * pi / 30.0)) * 2.0 / 3.0; + return ret; + } + + public static double transformLon(double x, double y) { + double ret = 300.0 + x + 2.0 * y + 0.1 * x * x + 0.1 * x * y + 0.1 * Math.sqrt(Math.abs(x)); + ret += (20.0 * Math.sin(6.0 * x * pi) + 20.0 * Math.sin(2.0 * x * pi)) * 2.0 / 3.0; + ret += (20.0 * Math.sin(x * pi) + 40.0 * Math.sin(x / 3.0 * pi)) * 2.0 / 3.0; + ret += (150.0 * Math.sin(x / 12.0 * pi) + 300.0 * Math.sin(x / 30.0 * pi)) * 2.0 / 3.0; + return ret; + } + public static double[] transform(double lat, double lon) { + if (outOfChina(lat, lon)) { + return new double[]{lat,lon}; + } + double dLat = transformLat(lon - 105.0, lat - 35.0); + double dLon = transformLon(lon - 105.0, lat - 35.0); + double radLat = lat / 180.0 * pi; + double magic = Math.sin(radLat); + magic = 1 - ee * magic * magic; + double sqrtMagic = Math.sqrt(magic); + dLat = (dLat * 180.0) / ((a * (1 - ee)) / (magic * sqrtMagic) * pi); + dLon = (dLon * 180.0) / (a / sqrtMagic * Math.cos(radLat) * pi); + double mgLat = lat + dLat; + double mgLon = lon + dLon; + return new double[]{mgLat,mgLon}; + } + /** + * 判断是否在中国 + * @param lat + * @param lon + * @return + */ + public static boolean outOfChina(double lat, double lon) { + if (lon < 72.004 || lon > 137.8347) + return true; + if (lat < 0.8293 || lat > 55.8271) + return true; + return false; + } + /** + * 84 ==》 高德/腾讯 + * @param lat + * @param lon + * @return + */ + public static double[] gps84_To_Gcj02(double lat, double lon) { + if (outOfChina(lat, lon)) { + return new double[]{lat,lon}; + } + double dLat = transformLat(lon - 105.0, lat - 35.0); + double dLon = transformLon(lon - 105.0, lat - 35.0); + double radLat = lat / 180.0 * pi; + double magic = Math.sin(radLat); + magic = 1 - ee * magic * magic; + double sqrtMagic = Math.sqrt(magic); + dLat = (dLat * 180.0) / ((a * (1 - ee)) / (magic * sqrtMagic) * pi); + dLon = (dLon * 180.0) / (a / sqrtMagic * Math.cos(radLat) * pi); + double mgLat = lat + dLat; + double mgLon = lon + dLon; + return new double[]{mgLat, mgLon}; + } + + + /** + * 高德/腾讯 ==》 84 + * @param lon * @param lat * @return + * */ + public static double[] gcj02_To_Gps84(double lat, double lon) { + double[] gps = transform(lat, lon); + double lontitude = lon * 2 - gps[1]; + double latitude = lat * 2 - gps[0]; + return new double[]{latitude, lontitude}; + } + /** + * 高德/腾讯 == 》 百度 + * @param lat + * @param lon + */ + public static double[] gcj02_To_Bd09(double lat, double lon) { + double x = lon, y = lat; + double z = Math.sqrt(x * x + y * y) + 0.00002 * Math.sin(y * x_pi); + double theta = Math.atan2(y, x) + 0.000003 * Math.cos(x * x_pi); + double tempLon = z * Math.cos(theta) + 0.0065; + double tempLat = z * Math.sin(theta) + 0.006; + double[] gps = {tempLat,tempLon}; + return gps; + } + + /** + * 百度 == 》 高德/腾讯 + * @param lat + * @param lon + */ + public static double[] bd09_To_Gcj02(double lat, double lon) { + double x = lon - 0.0065, y = lat - 0.006; + double z = Math.sqrt(x * x + y * y) - 0.00002 * Math.sin(y * x_pi); + double theta = Math.atan2(y, x) - 0.000003 * Math.cos(x * x_pi); + double tempLon = z * Math.cos(theta); + double tempLat = z * Math.sin(theta); + double[] gps = {tempLat,tempLon}; + return gps; + } + + /** + * 84 == 》 百度 + * @param lat + * @param lon + * @return + */ + public static double[] gps84_To_bd09(double lat,double lon){ + double[] gcj02 = gps84_To_Gcj02(lat,lon); + double[] bd09 = gcj02_To_Bd09(gcj02[0],gcj02[1]); + return bd09; + } + + /** + * 百度 == 》 84 + * @param lat + * @param lon + * @return + */ + public static double[] bd09_To_gps84(double lat,double lon){ + double[] gcj02 = bd09_To_Gcj02(lat, lon); + double[] gps84 = gcj02_To_Gps84(gcj02[0], gcj02[1]); + //保留小数点后六位 + gps84[0] = retain6(gps84[0]); + gps84[1] = retain6(gps84[1]); + return gps84; + } + + /* + * 保留小数点后六位 + * @param num + * @return + */ + private static double retain6(double num){ + String result = String .format("%.6f", num); + return Double.valueOf(result); + } +} + diff --git a/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/utils/Md5Utils.java b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/utils/Md5Utils.java new file mode 100644 index 0000000..1e26475 --- /dev/null +++ b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/utils/Md5Utils.java @@ -0,0 +1,54 @@ +package com.aiotagro.common.core.utils; + +import java.security.MessageDigest; + +public class Md5Utils { + + private static final String hexDigIts[] = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f"}; + + public static String getMd5(String origin) { + return getMd5(origin, "utf-8"); + } + + /** + * MD5加密 + * + * @param origin 字符 + * @param charsetname 编码 + * @return + */ + public static String getMd5(String origin, String charsetname) { + String resultString = null; + try { + resultString = new String(origin); + MessageDigest md = MessageDigest.getInstance("MD5"); + if (null == charsetname || "".equals(charsetname)) { + resultString = byteArrayToHexString(md.digest(resultString.getBytes())); + } else { + resultString = byteArrayToHexString(md.digest(resultString.getBytes(charsetname))); + } + } catch (Exception e) { + } + return resultString; + } + + + private static String byteArrayToHexString(byte b[]) { + StringBuffer resultSb = new StringBuffer(); + for (int i = 0; i < b.length; i++) { + resultSb.append(byteToHexString(b[i])); + } + return resultSb.toString(); + } + + private static String byteToHexString(byte b) { + int n = b; + if (n < 0) { + n += 256; + } + int d1 = n / 16; + int d2 = n % 16; + return hexDigIts[d1] + hexDigIts[d2]; + } + +} diff --git a/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/utils/NewDateUtils.java b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/utils/NewDateUtils.java new file mode 100644 index 0000000..74e0df1 --- /dev/null +++ b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/utils/NewDateUtils.java @@ -0,0 +1,354 @@ +package com.aiotagro.common.core.utils; + +import cn.hutool.core.date.DateUtil; + +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.Date; + + +/** + * @author Carson + * @package_name com.aiotagro.common.core.utils + * @date 2024/3/7 12:23 + */ +public class NewDateUtils { + public static String DATE_FORMAT = "yyyy-MM-dd"; + + public static String DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss"; + + public static String DATE_FORMAT_SHORT = "yyyyMMdd"; + + public static String DATE_TIME_FORMAT_SHORT = "yyyyMMddHHmmss"; + + public static String DATE_TIME_FORMAT_SHORT_MSEL = "yyyyMMddHHmmssSSS"; + + public static String MONTH_FORMAT = "yyyy-MM"; + + /** + * 获取当前日期 + * + * @return + */ + public static String getCurrentDate(String format) { + String datestr = null; + SimpleDateFormat df = new SimpleDateFormat(format); + datestr = df.format(new Date()); + return datestr; + } + + + /** + * 将字符串日期转换为日期格式 + * + * @param datestr + * @return + */ + public static Date stringToDate(String datestr, String format) { + + if (datestr == null || datestr.equals("")) { + return null; + } + Date date = new Date(); + SimpleDateFormat df = new SimpleDateFormat(format); + try { + date = df.parse(datestr); + } catch (Exception e) { + e.printStackTrace(); + } + return date; + } + + + /** + * 将日期格式日期转换为字符串格式 + * + * @param date + * @return + */ + public static String dateToString(Date date, String format) { + String datestr = null; + SimpleDateFormat df = new SimpleDateFormat(format); + datestr = df.format(date); + return datestr; + } + + + /** + * 获取日期的DAY值 + * + * @param date 输入日期 + * @return + */ + public static int getDayOfDate(Date date) { + int d = 0; + Calendar cd = Calendar.getInstance(); + cd.setTime(date); + d = cd.get(Calendar.DAY_OF_MONTH); + return d; + } + + /** + * 获取日期的MONTH值 + * + * @param date 输入日期 + * @return + */ + public static int getMonthOfDate(Date date) { + int m = 0; + Calendar cd = Calendar.getInstance(); + cd.setTime(date); + m = cd.get(Calendar.MONTH) + 1; + return m; + } + + /** + * 获取日期的YEAR值 + * + * @param date 输入日期 + * @return + */ + public static int getYearOfDate(Date date) { + int y = 0; + Calendar cd = Calendar.getInstance(); + cd.setTime(date); + y = cd.get(Calendar.YEAR); + return y; + } + + /** + * 获取星期几 + * + * @param date 输入日期 + * @return + */ + public static int getWeekOfDate(Date date) { + int wd = 0; + Calendar cd = Calendar.getInstance(); + cd.setTime(date); + wd = cd.get(Calendar.DAY_OF_WEEK) - 1; + return wd; + } + + /** + * 获取输入日期的当月第一天 + * + * @param date 输入日期 + * @return + */ + public static Date getFirstDayOfMonth(Date date) { + Calendar cd = Calendar.getInstance(); + cd.setTime(date); + cd.set(Calendar.DAY_OF_MONTH, 1); + return cd.getTime(); + } + + + /** + * 判断是否是闰年 + * + * @param date 输入日期 + * @return 是true 否false + */ + public static boolean isLeapYEAR(Date date) { + + Calendar cd = Calendar.getInstance(); + cd.setTime(date); + int year = cd.get(Calendar.YEAR); + + if (year % 4 == 0 && year % 100 != 0 | year % 400 == 0) { + return true; + } else { + return false; + } + } + + /** + * 根据整型数表示的年月日,生成日期类型格式 + * + * @param year 年 + * @param month 月 + * @param day 日 + * @return + */ + public static Date getDateByYMD(int year, int month, int day) { + Calendar cd = Calendar.getInstance(); + cd.set(year, month - 1, day); + return cd.getTime(); + } + + /** + * 获取年周期对应日 + * + * @param date 输入日期 + * @param iyear 年数 負數表示之前 + * @return + */ + public static Date getYearCycleOfDate(Date date, int iyear) { + Calendar cd = Calendar.getInstance(); + cd.setTime(date); + + cd.add(Calendar.YEAR, iyear); + + return cd.getTime(); + } + + /** + * 获取月周期对应日 + * + * @param date 输入日期 + * @param i + * @return + */ + public static Date getMonthCycleOfDate(Date date, int i) { + Calendar cd = Calendar.getInstance(); + cd.setTime(date); + + cd.add(Calendar.MONTH, i); + + return cd.getTime(); + } + + /** + * 计算 fromDate 到 toDate 相差多少年 + * + * @param fromDate + * @param toDate + * @return 年数 + */ + public static int getYearByMinusDate(Date fromDate, Date toDate) { + Calendar df = Calendar.getInstance(); + df.setTime(fromDate); + + Calendar dt = Calendar.getInstance(); + dt.setTime(toDate); + + return dt.get(Calendar.YEAR) - df.get(Calendar.YEAR); + } + + /** + * 计算 fromDate 到 toDate 相差多少个月 + * + * @param fromDate + * @param toDate + * @return 月数 + */ + public static int getMonthByMinusDate(Date fromDate, Date toDate) { + Calendar df = Calendar.getInstance(); + df.setTime(fromDate); + + Calendar dt = Calendar.getInstance(); + dt.setTime(toDate); + + return dt.get(Calendar.YEAR) * 12 + dt.get(Calendar.MONTH) - + (df.get(Calendar.YEAR) * 12 + df.get(Calendar.MONTH)); + } + + /** + * 计算 fromDate 到 toDate 相差多少天 + * + * @param fromDate + * @param toDate + * @return 天数 + */ + public static long getDaysByMinusDate(Date fromDate, Date toDate) { + + long num = DateUtil.betweenDay(fromDate, toDate, true); + return num; + } + + + /** + * 计算月龄 + * + * @param birthday 生日日期 + * @param calcDate 要计算的日期点 + * @return + */ + public static int calcAgeInMonths(Date birthday, Date calcDate) { + Calendar c1 = Calendar.getInstance(); + c1.setTime(calcDate); + int cYear = c1.get(Calendar.YEAR); + int cMonth = c1.get(Calendar.MONTH) + 1; // 注意月份是从 0 开始的,所以要加 1 + int cDay = c1.get(Calendar.DAY_OF_MONTH); + + Calendar c2 = Calendar.getInstance(); + c2.setTime(birthday); + int bYear = c2.get(Calendar.YEAR); + int bMonth = c2.get(Calendar.MONTH) + 1; // 注意月份是从 0 开始的,所以要加 1 + int bDay = c2.get(Calendar.DAY_OF_MONTH); + + if (cDay >= bDay) { + return (cYear - bYear) * 12 + (cMonth - bMonth); + } else { + return (cYear - bYear) * 12 + (cMonth - bMonth) - 1; + } + } + + + /** + * 在输入日期上增加(+)或减去(-)天数 + * + * @param date 输入日期 + * @param + */ + public static Date addDay(Date date, int iday) { + Calendar cd = Calendar.getInstance(); + + cd.setTime(date); + + cd.add(Calendar.DAY_OF_MONTH, iday); + + return cd.getTime(); + } + + /** + * 在输入日期上增加(+)或减去(-)月份 + * + * @param date 输入日期 + * @param imonth 要增加或减少的月分数 + */ + public static Date addMonth(Date date, int imonth) { + Calendar cd = Calendar.getInstance(); + + cd.setTime(date); + + cd.add(Calendar.MONTH, imonth); + + return cd.getTime(); + } + + /** + * 在输入日期上增加(+)或减去(-)年份 + * + * @param date 输入日期 + * @param iyear 要增加或减少的年数 + */ + public static Date addYear(Date date, int iyear) { + Calendar cd = Calendar.getInstance(); + + cd.setTime(date); + + cd.add(Calendar.YEAR, iyear); + + return cd.getTime(); + } + + + public static long getAgeByBirthday(String date) { + + Date birthday = stringToDate(date, "yyyy-MM-dd"); + long sec = System.currentTimeMillis() - birthday.getTime(); + + long age = sec / (1000 * 60 * 60 * 24) / 365; + + return age; + } + + public static String timestampToString(Long timestamp, String format) { + Date date = new Date(timestamp * 1000L); + SimpleDateFormat df = new SimpleDateFormat(format); + return df.format(date); + } + +} diff --git a/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/utils/SecurityUtil.java b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/utils/SecurityUtil.java new file mode 100644 index 0000000..31282f3 --- /dev/null +++ b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/utils/SecurityUtil.java @@ -0,0 +1,83 @@ +package com.aiotagro.common.core.utils; + +import cn.dev33.satoken.stp.StpUtil; +import com.aiotagro.common.core.constant.RoleConstants; + +import java.util.Collections; +import java.util.List; + +/** + * @author Carson + * @package_name com.aiotagro.common.core.utils + * @date 2024/2/7 14:18 + */ +public class SecurityUtil { + + /** + * 获取登录用户ID + **/ + public static Long getUserId() { + return StpUtil.getLoginIdAsLong(); + } + + /** + * 获取登录用户ID + **/ + public static Integer getCurrentUserId() { + return StpUtil.getLoginIdAsInt(); + } + + /** + * 获取用户名称 + **/ + public static String getUserName() { + Object username = StpUtil.getTokenSession().get("username"); + return username != null ? username.toString() : ""; + } + + /** + * 获取用户手机号 + **/ + public static String getUserMobile() { + Object mobile = StpUtil.getTokenSession().get("mobile"); + return mobile != null ? mobile.toString() : ""; + } + + /** + * 获取当前用户角色ID + **/ + public static Integer getRoleId() { + Object roleId = StpUtil.getTokenSession().get("roleId"); + return roleId != null ? Integer.parseInt(roleId.toString()) : null; + } + + /** + * 获取当前用户权限列表 + **/ + @SuppressWarnings("unchecked") + public static List getPermissions() { + Object permissions = StpUtil.getTokenSession().get("permissions"); + return permissions != null ? (List) permissions : Collections.emptyList(); + } + + /** + * 判断是否超级管理员 + **/ + public static boolean isSuperAdmin() { + Integer roleId = getRoleId(); + return roleId != null && roleId.equals(RoleConstants.SUPER_ADMIN_ROLE_ID); + } + + /** + * 判断是否拥有某个权限 + **/ + public static boolean hasPermission(String permission) { + if (isSuperAdmin()) { + return true; + } + List permissions = getPermissions(); + return permissions.contains(RoleConstants.ALL_PERMISSION) || + permissions.contains(permission); + } + +} diff --git a/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/utils/ServletUtils.java b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/utils/ServletUtils.java new file mode 100644 index 0000000..012691f --- /dev/null +++ b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/utils/ServletUtils.java @@ -0,0 +1,326 @@ +package com.aiotagro.common.core.utils; + +import com.aiotagro.common.core.constant.Constants; +import com.aiotagro.common.core.text.Convert; +import org.springframework.util.LinkedCaseInsensitiveMap; +import org.springframework.web.context.request.RequestAttributes; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +import javax.servlet.ServletRequest; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; +import java.net.URLEncoder; +import java.util.Collections; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Map; + +/** + * 客户端工具类 + * + * @author Carson + */ +public class ServletUtils +{ + /** + * 获取String参数 + */ + public static String getParameter(String name) + { + return getRequest().getParameter(name); + } + + /** + * 获取String参数 + */ + public static String getParameter(String name, String defaultValue) + { + return Convert.toStr(getRequest().getParameter(name), defaultValue); + } + + /** + * 获取Integer参数 + */ + public static Integer getParameterToInt(String name) + { + return Convert.toInt(getRequest().getParameter(name)); + } + + /** + * 获取Integer参数 + */ + public static Integer getParameterToInt(String name, Integer defaultValue) + { + return Convert.toInt(getRequest().getParameter(name), defaultValue); + } + + /** + * 获取Boolean参数 + */ + public static Boolean getParameterToBool(String name) + { + return Convert.toBool(getRequest().getParameter(name)); + } + + /** + * 获取Boolean参数 + */ + public static Boolean getParameterToBool(String name, Boolean defaultValue) + { + return Convert.toBool(getRequest().getParameter(name), defaultValue); + } + + /** + * 获得所有请求参数 + * + * @param request 请求对象{@link ServletRequest} + * @return Map + */ + public static Map getParams(ServletRequest request) + { + final Map map = request.getParameterMap(); + return Collections.unmodifiableMap(map); + } + + /** + * 获得所有请求参数 + * + * @param request 请求对象{@link ServletRequest} + * @return Map + */ + public static Map getParamMap(ServletRequest request) + { + Map params = new HashMap<>(); + for (Map.Entry entry : getParams(request).entrySet()) + { + params.put(entry.getKey(), StringUtils.join(entry.getValue(), ",")); + } + return params; + } + + /** + * 获取request + */ + public static HttpServletRequest getRequest() + { + try + { + return getRequestAttributes().getRequest(); + } + catch (Exception e) + { + return null; + } + } + + /** + * 获取response + */ + public static HttpServletResponse getResponse() + { + try + { + return getRequestAttributes().getResponse(); + } + catch (Exception e) + { + return null; + } + } + + /** + * 获取session + */ + public static HttpSession getSession() + { + return getRequest().getSession(); + } + + public static ServletRequestAttributes getRequestAttributes() + { + try + { + RequestAttributes attributes = RequestContextHolder.getRequestAttributes(); + return (ServletRequestAttributes) attributes; + } + catch (Exception e) + { + return null; + } + } + + public static String getHeader(HttpServletRequest request, String name) + { + String value = request.getHeader(name); + if (StringUtils.isEmpty(value)) + { + return StringUtils.EMPTY; + } + return urlDecode(value); + } + + public static Map getHeaders(HttpServletRequest request) + { + Map map = new LinkedCaseInsensitiveMap<>(); + Enumeration enumeration = request.getHeaderNames(); + if (enumeration != null) + { + while (enumeration.hasMoreElements()) + { + String key = enumeration.nextElement(); + String value = request.getHeader(key); + map.put(key, value); + } + } + return map; + } + + /** + * 将字符串渲染到客户端 + * + * @param response 渲染对象 + * @param string 待渲染的字符串 + */ + public static void renderString(HttpServletResponse response, String string) + { + try + { + response.setStatus(200); + response.setContentType("application/json"); + response.setCharacterEncoding("utf-8"); + response.getWriter().print(string); + } + catch (IOException e) + { + e.printStackTrace(); + } + } + + /** + * 是否是Ajax异步请求 + * + * @param request + */ + public static boolean isAjaxRequest(HttpServletRequest request) + { + String accept = request.getHeader("accept"); + if (accept != null && accept.contains("application/json")) + { + return true; + } + + String xRequestedWith = request.getHeader("X-Requested-With"); + if (xRequestedWith != null && xRequestedWith.contains("XMLHttpRequest")) + { + return true; + } + + String uri = request.getRequestURI(); + if (StringUtils.inStringIgnoreCase(uri, ".json", ".xml")) + { + return true; + } + + String ajax = request.getParameter("__ajax"); + return StringUtils.inStringIgnoreCase(ajax, "json", "xml"); + } + + /** + * 内容编码 + * + * @param str 内容 + * @return 编码后的内容 + */ + public static String urlEncode(String str) + { + try + { + return URLEncoder.encode(str, Constants.UTF8); + } + catch (UnsupportedEncodingException e) + { + return StringUtils.EMPTY; + } + } + + /** + * 内容解码 + * + * @param str 内容 + * @return 解码后的内容 + */ + public static String urlDecode(String str) + { + try + { + return URLDecoder.decode(str, Constants.UTF8); + } + catch (UnsupportedEncodingException e) + { + return StringUtils.EMPTY; + } + } + + /** + * 设置webflux模型响应 + * + * @param response ServerHttpResponse + * @param value 响应内容 + * @return Mono + */ +// public static Mono webFluxResponseWriter(ServerHttpResponse response, Object value) +// { +// return webFluxResponseWriter(response, HttpStatus.OK, value, R.FAIL); +// } + + /** + * 设置webflux模型响应 + * + * @param response ServerHttpResponse + * @param code 响应状态码 + * @param value 响应内容 + * @return Mono + */ +// public static Mono webFluxResponseWriter(ServerHttpResponse response, Object value, int code) +// { +// return webFluxResponseWriter(response, HttpStatus.OK, value, code); +// } + + /** + * 设置webflux模型响应 + * + * @param response ServerHttpResponse + * @param status http状态码 + * @param code 响应状态码 + * @param value 响应内容 + * @return Mono + */ +// public static Mono webFluxResponseWriter(ServerHttpResponse response, HttpStatus status, Object value, int code) +// { +// return webFluxResponseWriter(response, MediaType.APPLICATION_JSON_VALUE, status, value, code); +// } + + /** + * 设置webflux模型响应 + * + * @param response ServerHttpResponse + * @param contentType content-type + * @param status http状态码 + * @param code 响应状态码 + * @param value 响应内容 + * @return Mono + */ +// public static Mono webFluxResponseWriter(ServerHttpResponse response, String contentType, HttpStatus status, Object value, int code) +// { +// response.setStatusCode(status); +// response.getHeaders().add(HttpHeaders.CONTENT_TYPE, contentType); +// R result = R.fail(code, value.toString()); +// DataBuffer dataBuffer = response.bufferFactory().wrap(JSON.toJSONString(result).getBytes()); +// return response.writeWith(Mono.just(dataBuffer)); +// } +} diff --git a/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/utils/SmsUtils.java b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/utils/SmsUtils.java new file mode 100644 index 0000000..f42c52a --- /dev/null +++ b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/utils/SmsUtils.java @@ -0,0 +1,67 @@ +package com.aiotagro.common.core.utils; + +import com.aiotagro.common.core.web.domain.AjaxResult; +import com.github.qcloudsms.SmsSingleSender; +import com.github.qcloudsms.SmsSingleSenderResult; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Value; + +/** + * 短信相关工具类 + */ +public class SmsUtils { + + private static final Logger logger = LoggerFactory.getLogger(SmsUtils.class); + + // 短信应用SDK AppID + private static final int APPID = 1400915506; + // 短信应用SDK AppKey + private static final String APPKEY = "dce6f4a7653b7a3506f77461446deed5"; + // 国家码 + private static final String NATIONCODE = "86"; + + + + //签名 + @Value("${sms.sign}") + private static final String SIGN = "武汉爱农云联科技"; + + /** + * 短信验证码发送 + * @param params + * @param cellphone + * @return + */ + public static boolean sendSms(String[] params, String cellphone,Integer templateCode) { + try { + SmsSingleSender sender = new SmsSingleSender(APPID, APPKEY); + SmsSingleSenderResult result = sender.sendWithParam(NATIONCODE, cellphone, + templateCode, params, SIGN, "", ""); + + return result.result == 0; + } catch (Exception e) { + logger.error("短信发送失败", e); + } + return false; + } + + /** + * 短信验证码发送 + * @param tempCode + * @param cellphone + * @return + */ + public static AjaxResult sendSms(String cellphone, Integer tempCode) { + try { + SmsSingleSender sender = new SmsSingleSender(APPID, APPKEY); + SmsSingleSenderResult result = sender.sendWithParam(NATIONCODE, cellphone, + tempCode, new String[]{}, SIGN, "", ""); + return result.result == 0 ? AjaxResult.success("发送成功") : AjaxResult.error(result.errMsg); + } catch (Exception e) { + logger.error("短信发送失败", e); + return AjaxResult.error("系统繁忙,发送失败"); + } + } + +} diff --git a/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/utils/SpringBeanUtils.java b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/utils/SpringBeanUtils.java new file mode 100644 index 0000000..dfb960f --- /dev/null +++ b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/utils/SpringBeanUtils.java @@ -0,0 +1,33 @@ +package com.aiotagro.common.core.utils; + +import org.springframework.beans.BeansException; + +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.stereotype.Component; + +/** + * Spirng Bean动态注入 + * + * @author shirukai + */ +@Component +public class SpringBeanUtils implements ApplicationContextAware { + private static ConfigurableApplicationContext context; + + @Override + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { + SpringBeanUtils.context = (ConfigurableApplicationContext) applicationContext; + } + + public static void register(String name, Object bean) { + context.getBeanFactory().registerSingleton(name, bean); + } + + public static T getBean(Class clazz) { + return context.getBean(clazz); + } + +} + diff --git a/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/utils/SpringUtils.java b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/utils/SpringUtils.java new file mode 100644 index 0000000..0424318 --- /dev/null +++ b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/utils/SpringUtils.java @@ -0,0 +1,114 @@ +package com.aiotagro.common.core.utils; + +import org.springframework.aop.framework.AopContext; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.NoSuchBeanDefinitionException; +import org.springframework.beans.factory.config.BeanFactoryPostProcessor; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.stereotype.Component; + +/** + * spring工具类 方便在非spring管理环境中获取bean + * + * @author Carson + */ +@Component +public final class SpringUtils implements BeanFactoryPostProcessor +{ + /** Spring应用上下文环境 */ + private static ConfigurableListableBeanFactory beanFactory; + + @Override + public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException + { + SpringUtils.beanFactory = beanFactory; + } + + /** + * 获取对象 + * + * @param name + * @return Object 一个以所给名字注册的bean的实例 + * @throws BeansException + * + */ + @SuppressWarnings("unchecked") + public static T getBean(String name) throws BeansException + { + return (T) beanFactory.getBean(name); + } + + /** + * 获取类型为requiredType的对象 + * + * @param clz + * @return + * @throws BeansException + * + */ + public static T getBean(Class clz) throws BeansException + { + T result = (T) beanFactory.getBean(clz); + return result; + } + + /** + * 如果BeanFactory包含一个与所给名称匹配的bean定义,则返回true + * + * @param name + * @return boolean + */ + public static boolean containsBean(String name) + { + return beanFactory.containsBean(name); + } + + /** + * 判断以给定名字注册的bean定义是一个singleton还是一个prototype。 如果与给定名字相应的bean定义没有被找到,将会抛出一个异常(NoSuchBeanDefinitionException) + * + * @param name + * @return boolean + * @throws NoSuchBeanDefinitionException + * + */ + public static boolean isSingleton(String name) throws NoSuchBeanDefinitionException + { + return beanFactory.isSingleton(name); + } + + /** + * @param name + * @return Class 注册对象的类型 + * @throws NoSuchBeanDefinitionException + * + */ + public static Class getType(String name) throws NoSuchBeanDefinitionException + { + return beanFactory.getType(name); + } + + /** + * 如果给定的bean名字在bean定义中有别名,则返回这些别名 + * + * @param name + * @return + * @throws NoSuchBeanDefinitionException + * + */ + public static String[] getAliases(String name) throws NoSuchBeanDefinitionException + { + return beanFactory.getAliases(name); + } + + /** + * 获取aop代理对象 + * + * @param invoker + * @return + */ + @SuppressWarnings("unchecked") + public static T getAopProxy(T invoker) + { + return (T) AopContext.currentProxy(); + } +} diff --git a/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/utils/StringUtils.java b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/utils/StringUtils.java new file mode 100644 index 0000000..235f2af --- /dev/null +++ b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/utils/StringUtils.java @@ -0,0 +1,562 @@ +package com.aiotagro.common.core.utils; + +import com.aiotagro.common.core.constant.Constants; +import com.aiotagro.common.core.text.StrFormatter; +import org.springframework.util.AntPathMatcher; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * 字符串工具类 + * + * @author Carson + */ +public class StringUtils extends org.apache.commons.lang3.StringUtils +{ + /** 空字符串 */ + private static final String NULLSTR = ""; + + /** 下划线 */ + private static final char SEPARATOR = '_'; + + /** + * 获取参数不为空值 + * + * @param value defaultValue 要判断的value + * @return value 返回值 + */ + public static T nvl(T value, T defaultValue) + { + return value != null ? value : defaultValue; + } + + /** + * * 判断一个Collection是否为空, 包含List,Set,Queue + * + * @param coll 要判断的Collection + * @return true:为空 false:非空 + */ + public static boolean isEmpty(Collection coll) + { + return isNull(coll) || coll.isEmpty(); + } + + /** + * * 判断一个Collection是否非空,包含List,Set,Queue + * + * @param coll 要判断的Collection + * @return true:非空 false:空 + */ + public static boolean isNotEmpty(Collection coll) + { + return !isEmpty(coll); + } + + /** + * * 判断一个对象数组是否为空 + * + * @param objects 要判断的对象数组 + ** @return true:为空 false:非空 + */ + public static boolean isEmpty(Object[] objects) + { + return isNull(objects) || (objects.length == 0); + } + + /** + * * 判断一个对象数组是否非空 + * + * @param objects 要判断的对象数组 + * @return true:非空 false:空 + */ + public static boolean isNotEmpty(Object[] objects) + { + return !isEmpty(objects); + } + + /** + * * 判断一个Map是否为空 + * + * @param map 要判断的Map + * @return true:为空 false:非空 + */ + public static boolean isEmpty(Map map) + { + return isNull(map) || map.isEmpty(); + } + + /** + * * 判断一个Map是否为空 + * + * @param map 要判断的Map + * @return true:非空 false:空 + */ + public static boolean isNotEmpty(Map map) + { + return !isEmpty(map); + } + + /** + * * 判断一个字符串是否为空串 + * + * @param str String + * @return true:为空 false:非空 + */ + public static boolean isEmpty(String str) + { + return isNull(str) || NULLSTR.equals(str.trim()); + } + + /** + * * 判断一个字符串是否为非空串 + * + * @param str String + * @return true:非空串 false:空串 + */ + public static boolean isNotEmpty(String str) + { + return !isEmpty(str); + } + + /** + * * 判断一个对象是否为空 + * + * @param object Object + * @return true:为空 false:非空 + */ + public static boolean isNull(Object object) + { + return object == null; + } + + /** + * * 判断一个对象是否非空 + * + * @param object Object + * @return true:非空 false:空 + */ + public static boolean isNotNull(Object object) + { + return !isNull(object); + } + + /** + * * 判断一个对象是否是数组类型(Java基本型别的数组) + * + * @param object 对象 + * @return true:是数组 false:不是数组 + */ + public static boolean isArray(Object object) + { + return isNotNull(object) && object.getClass().isArray(); + } + + /** + * 去空格 + */ + public static String trim(String str) + { + return (str == null ? "" : str.trim()); + } + + /** + * 截取字符串 + * + * @param str 字符串 + * @param start 开始 + * @return 结果 + */ + public static String substring(final String str, int start) + { + if (str == null) + { + return NULLSTR; + } + + if (start < 0) + { + start = str.length() + start; + } + + if (start < 0) + { + start = 0; + } + if (start > str.length()) + { + return NULLSTR; + } + + return str.substring(start); + } + + /** + * 截取字符串 + * + * @param str 字符串 + * @param start 开始 + * @param end 结束 + * @return 结果 + */ + public static String substring(final String str, int start, int end) + { + if (str == null) + { + return NULLSTR; + } + + if (end < 0) + { + end = str.length() + end; + } + if (start < 0) + { + start = str.length() + start; + } + + if (end > str.length()) + { + end = str.length(); + } + + if (start > end) + { + return NULLSTR; + } + + if (start < 0) + { + start = 0; + } + if (end < 0) + { + end = 0; + } + + return str.substring(start, end); + } + + /** + * 判断是否为空,并且不是空白字符 + * + * @param str 要判断的value + * @return 结果 + */ + public static boolean hasText(String str) + { + return (str != null && !str.isEmpty() && containsText(str)); + } + + private static boolean containsText(CharSequence str) + { + int strLen = str.length(); + for (int i = 0; i < strLen; i++) + { + if (!Character.isWhitespace(str.charAt(i))) + { + return true; + } + } + return false; + } + + /** + * 格式化文本, {} 表示占位符
+ * 此方法只是简单将占位符 {} 按照顺序替换为参数
+ * 如果想输出 {} 使用 \\转义 { 即可,如果想输出 {} 之前的 \ 使用双转义符 \\\\ 即可
+ * 例:
+ * 通常使用:format("this is {} for {}", "a", "b") -> this is a for b
+ * 转义{}: format("this is \\{} for {}", "a", "b") -> this is \{} for a
+ * 转义\: format("this is \\\\{} for {}", "a", "b") -> this is \a for b
+ * + * @param template 文本模板,被替换的部分用 {} 表示 + * @param params 参数值 + * @return 格式化后的文本 + */ + public static String format(String template, Object... params) + { + if (isEmpty(params) || isEmpty(template)) + { + return template; + } + return StrFormatter.format(template, params); + } + + /** + * 是否为http(s)://开头 + * + * @param link 链接 + * @return 结果 + */ + public static boolean ishttp(String link) + { + return StringUtils.startsWithAny(link, Constants.HTTP, Constants.HTTPS); + } + + /** + * 判断给定的collection列表中是否包含数组array 判断给定的数组array中是否包含给定的元素value + * + * @param collection 给定的集合 + * @param array 给定的数组 + * @return boolean 结果 + */ + public static boolean containsAny(Collection collection, String... array) + { + if (isEmpty(collection) || isEmpty(array)) + { + return false; + } + else + { + for (String str : array) + { + if (collection.contains(str)) + { + return true; + } + } + return false; + } + } + + /** + * 驼峰转下划线命名 + */ + public static String toUnderScoreCase(String str) + { + if (str == null) + { + return null; + } + StringBuilder sb = new StringBuilder(); + // 前置字符是否大写 + boolean preCharIsUpperCase = true; + // 当前字符是否大写 + boolean curreCharIsUpperCase = true; + // 下一字符是否大写 + boolean nexteCharIsUpperCase = true; + for (int i = 0; i < str.length(); i++) + { + char c = str.charAt(i); + if (i > 0) + { + preCharIsUpperCase = Character.isUpperCase(str.charAt(i - 1)); + } + else + { + preCharIsUpperCase = false; + } + + curreCharIsUpperCase = Character.isUpperCase(c); + + if (i < (str.length() - 1)) + { + nexteCharIsUpperCase = Character.isUpperCase(str.charAt(i + 1)); + } + + if (preCharIsUpperCase && curreCharIsUpperCase && !nexteCharIsUpperCase) + { + sb.append(SEPARATOR); + } + else if ((i != 0 && !preCharIsUpperCase) && curreCharIsUpperCase) + { + sb.append(SEPARATOR); + } + sb.append(Character.toLowerCase(c)); + } + + return sb.toString(); + } + + /** + * 是否包含字符串 + * + * @param str 验证字符串 + * @param strs 字符串组 + * @return 包含返回true + */ + public static boolean inStringIgnoreCase(String str, String... strs) + { + if (str != null && strs != null) + { + for (String s : strs) + { + if (str.equalsIgnoreCase(trim(s))) + { + return true; + } + } + } + return false; + } + + /** + * 将下划线大写方式命名的字符串转换为驼峰式。如果转换前的下划线大写方式命名的字符串为空,则返回空字符串。 例如:HELLO_WORLD->HelloWorld + * + * @param name 转换前的下划线大写方式命名的字符串 + * @return 转换后的驼峰式命名的字符串 + */ + public static String convertToCamelCase(String name) + { + StringBuilder result = new StringBuilder(); + // 快速检查 + if (name == null || name.isEmpty()) + { + // 没必要转换 + return ""; + } + else if (!name.contains("_")) + { + // 不含下划线,仅将首字母大写 + return name.substring(0, 1).toUpperCase() + name.substring(1); + } + // 用下划线将原始字符串分割 + String[] camels = name.split("_"); + for (String camel : camels) + { + // 跳过原始字符串中开头、结尾的下换线或双重下划线 + if (camel.isEmpty()) + { + continue; + } + // 首字母大写 + result.append(camel.substring(0, 1).toUpperCase()); + result.append(camel.substring(1).toLowerCase()); + } + return result.toString(); + } + + /** + * 驼峰式命名法 + * 例如:user_name->userName + */ + public static String toCamelCase(String s) + { + if (s == null) + { + return null; + } + if (s.indexOf(SEPARATOR) == -1) + { + return s; + } + s = s.toLowerCase(); + StringBuilder sb = new StringBuilder(s.length()); + boolean upperCase = false; + for (int i = 0; i < s.length(); i++) + { + char c = s.charAt(i); + + if (c == SEPARATOR) + { + upperCase = true; + } + else if (upperCase) + { + sb.append(Character.toUpperCase(c)); + upperCase = false; + } + else + { + sb.append(c); + } + } + return sb.toString(); + } + + /** + * 查找指定字符串是否匹配指定字符串列表中的任意一个字符串 + * + * @param str 指定字符串 + * @param strs 需要检查的字符串数组 + * @return 是否匹配 + */ + public static boolean matches(String str, List strs) + { + if (isEmpty(str) || isEmpty(strs)) + { + return false; + } + for (String pattern : strs) + { + if (isMatch(pattern, str)) + { + return true; + } + } + return false; + } + + /** + * 判断url是否与规则配置: + * ? 表示单个字符; + * * 表示一层路径内的任意字符串,不可跨层级; + * ** 表示任意层路径; + * + * @param pattern 匹配规则 + * @param url 需要匹配的url + * @return + */ + public static boolean isMatch(String pattern, String url) + { + AntPathMatcher matcher = new AntPathMatcher(); + return matcher.match(pattern, url); + } + + @SuppressWarnings("unchecked") + public static T cast(Object obj) + { + return (T) obj; + } + + /** + * 数字左边补齐0,使之达到指定长度。注意,如果数字转换为字符串后,长度大于size,则只保留 最后size个字符。 + * + * @param num 数字对象 + * @param size 字符串指定长度 + * @return 返回数字的字符串格式,该字符串为指定长度。 + */ + public static final String padl(final Number num, final int size) + { + return padl(num.toString(), size, '0'); + } + + /** + * 字符串左补齐。如果原始字符串s长度大于size,则只保留最后size个字符。 + * + * @param s 原始字符串 + * @param size 字符串指定长度 + * @param c 用于补齐的字符 + * @return 返回指定长度的字符串,由原字符串左补齐或截取得到。 + */ + public static final String padl(final String s, final int size, final char c) + { + final StringBuilder sb = new StringBuilder(size); + if (s != null) + { + final int len = s.length(); + if (s.length() <= size) + { + for (int i = size - len; i > 0; i--) + { + sb.append(c); + } + sb.append(s); + } + else + { + return s.substring(len - size, len); + } + } + else + { + for (int i = size; i > 0; i--) + { + sb.append(c); + } + } + return sb.toString(); + } +} diff --git a/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/utils/TencentCloudUtil.java b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/utils/TencentCloudUtil.java new file mode 100644 index 0000000..b7a041d --- /dev/null +++ b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/utils/TencentCloudUtil.java @@ -0,0 +1,85 @@ +package com.aiotagro.common.core.utils; + +import com.aiotagro.common.core.constant.TencentCloudConstants; +import com.alibaba.fastjson2.JSON; +import com.qcloud.cos.COSClient; +import com.qcloud.cos.ClientConfig; +import com.qcloud.cos.auth.BasicCOSCredentials; +import com.qcloud.cos.auth.COSCredentials; +import com.qcloud.cos.http.HttpProtocol; +import com.qcloud.cos.model.ObjectMetadata; +import com.qcloud.cos.model.PutObjectRequest; +import com.qcloud.cos.model.PutObjectResult; +import com.qcloud.cos.region.Region; +import com.tencentcloudapi.common.Credential; +import com.tencentcloudapi.common.profile.ClientProfile; +import com.tencentcloudapi.common.profile.HttpProfile; +import com.tencentcloudapi.sms.v20210111.SmsClient; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.web.multipart.MultipartFile; + +import java.io.IOException; + +/** + * @author Carson 腾讯云盘工具类 + * @package_name com.aiotagro.common.core.utils + * @date 2024/3/7 12:05 + */ +public class TencentCloudUtil { + private static final Logger logger = LoggerFactory.getLogger(TencentCloudUtil.class); + + + // 1. 初始化用户身份信息 + static COSCredentials cred = new BasicCOSCredentials(TencentCloudConstants.TENCENT_CLOUD_SECRETID, TencentCloudConstants.TENCENT_CLOUD_SECRETKEY); + static Credential cred1 = new Credential(TencentCloudConstants.TENCENT_CLOUD_AIPITE_SECRETID, TencentCloudConstants.TENCENT_CLOUD_AIPITE_SECRETKEY); + // // 2 设置 bucket 的地域, COS 地域的简称请参照 https://cloud.tencent.com/document/product/436/6224 + static Region region = new Region(TencentCloudConstants.TENCENT_CLOUD_REGION); + static ClientConfig clientConfig = new ClientConfig(region); + // 3 生成cos客户端 + static COSClient cosClient = new COSClient(cred, clientConfig); + static SmsClient smsClient; + static{ + HttpProfile httpProfile = new HttpProfile(); + httpProfile.setEndpoint("sms.tencentcloudapi.com"); + // 实例化一个client选项,可选的,没有特殊需求可以跳过 + ClientProfile clientProfile = new ClientProfile(); + clientProfile.setHttpProfile(httpProfile); + smsClient = new SmsClient(cred1, "ap-nanjing", clientProfile); + } + + /** + * 文件上传 + * @param multipartFile + * @return + * @throws IOException + */ + public static String upload(MultipartFile multipartFile) throws IOException { + + // 当前日期 + String currentDate = NewDateUtils.getCurrentDate("yyyy/MM/dd"); + //获件名 + String fileName = multipartFile.getOriginalFilename(); + // 文件名前缀 + String originalName = fileName.substring(0, fileName.lastIndexOf(".")); + //获取文件后缀名 + String suffixName = fileName.substring(fileName.lastIndexOf(".")); + // 从 5.6.54 版本开始,默认使用了 https + clientConfig.setHttpProtocol(HttpProtocol.https); + String name = originalName + NewDateUtils.getCurrentDate(NewDateUtils.DATE_TIME_FORMAT_SHORT) + suffixName; + // 指定文件上传到 COS 上的路径 + String key = "iotPlateform/" + currentDate + "/" + name; + // 创建 ObjectMetadata 对象,并设置内容长度 + ObjectMetadata objectMetadata = new ObjectMetadata(); + objectMetadata.setContentLength(multipartFile.getSize()); + PutObjectRequest putObjectRequest = new PutObjectRequest(TencentCloudConstants.TENCENT_CLOUD_BUCKET, key, multipartFile.getInputStream(), objectMetadata); + // 上传 + PutObjectResult putObjectResult = cosClient.putObject(putObjectRequest); + logger.info("腾讯文件上传结果 {}", JSON.toJSONString(putObjectResult)); + String targetUrl = "https://" + TencentCloudConstants.TENCENT_CLOUD_BUCKET + ".cos." + TencentCloudConstants.TENCENT_CLOUD_REGION + ".myqcloud.com/" + key; + logger.info("腾讯文件访问url {}", targetUrl); + return targetUrl; + } + + +} diff --git a/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/utils/bean/BeanUtils.java b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/utils/bean/BeanUtils.java new file mode 100644 index 0000000..0b80f60 --- /dev/null +++ b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/utils/bean/BeanUtils.java @@ -0,0 +1,110 @@ +package com.aiotagro.common.core.utils.bean; + +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Bean 工具类 + * + * @author Carson + */ +public class BeanUtils extends org.springframework.beans.BeanUtils +{ + /** Bean方法名中属性名开始的下标 */ + private static final int BEAN_METHOD_PROP_INDEX = 3; + + /** * 匹配getter方法的正则表达式 */ + private static final Pattern GET_PATTERN = Pattern.compile("get(\\p{javaUpperCase}\\w*)"); + + /** * 匹配setter方法的正则表达式 */ + private static final Pattern SET_PATTERN = Pattern.compile("set(\\p{javaUpperCase}\\w*)"); + + /** + * Bean属性复制工具方法。 + * + * @param dest 目标对象 + * @param src 源对象 + */ + public static void copyBeanProp(Object dest, Object src) + { + try + { + copyProperties(src, dest); + } + catch (Exception e) + { + e.printStackTrace(); + } + } + + /** + * 获取对象的setter方法。 + * + * @param obj 对象 + * @return 对象的setter方法列表 + */ + public static List getSetterMethods(Object obj) + { + // setter方法列表 + List setterMethods = new ArrayList(); + + // 获取所有方法 + Method[] methods = obj.getClass().getMethods(); + + // 查找setter方法 + + for (Method method : methods) + { + Matcher m = SET_PATTERN.matcher(method.getName()); + if (m.matches() && (method.getParameterTypes().length == 1)) + { + setterMethods.add(method); + } + } + // 返回setter方法列表 + return setterMethods; + } + + /** + * 获取对象的getter方法。 + * + * @param obj 对象 + * @return 对象的getter方法列表 + */ + + public static List getGetterMethods(Object obj) + { + // getter方法列表 + List getterMethods = new ArrayList(); + // 获取所有方法 + Method[] methods = obj.getClass().getMethods(); + // 查找getter方法 + for (Method method : methods) + { + Matcher m = GET_PATTERN.matcher(method.getName()); + if (m.matches() && (method.getParameterTypes().length == 0)) + { + getterMethods.add(method); + } + } + // 返回getter方法列表 + return getterMethods; + } + + /** + * 检查Bean方法名中的属性名是否相等。
+ * 如getName()和setName()属性名一样,getName()和setAge()属性名不一样。 + * + * @param m1 方法名1 + * @param m2 方法名2 + * @return 属性名一样返回true,否则返回false + */ + + public static boolean isMethodPropEquals(String m1, String m2) + { + return m1.substring(BEAN_METHOD_PROP_INDEX).equals(m2.substring(BEAN_METHOD_PROP_INDEX)); + } +} diff --git a/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/utils/bean/BeanValidators.java b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/utils/bean/BeanValidators.java new file mode 100644 index 0000000..f751e65 --- /dev/null +++ b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/utils/bean/BeanValidators.java @@ -0,0 +1,24 @@ +package com.aiotagro.common.core.utils.bean; + +import javax.validation.ConstraintViolation; +import javax.validation.ConstraintViolationException; +import javax.validation.Validator; +import java.util.Set; + +/** + * bean对象属性验证 + * + * @author Carson + */ +public class BeanValidators +{ + public static void validateWithException(Validator validator, Object object, Class... groups) + throws ConstraintViolationException + { + Set> constraintViolations = validator.validate(object, groups); + if (!constraintViolations.isEmpty()) + { + throw new ConstraintViolationException(constraintViolations); + } + } +} diff --git a/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/utils/file/FileTypeUtils.java b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/utils/file/FileTypeUtils.java new file mode 100644 index 0000000..ee17b41 --- /dev/null +++ b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/utils/file/FileTypeUtils.java @@ -0,0 +1,96 @@ +package com.aiotagro.common.core.utils.file; + +import org.apache.commons.io.FilenameUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.web.multipart.MultipartFile; + +import java.io.File; +import java.util.Objects; + +/** + * 文件类型工具类 + * + * @author Carson + */ +public class FileTypeUtils +{ + /** + * 获取文件类型 + *

+ * 例如: Carson.txt, 返回: txt + * + * @param file 文件名 + * @return 后缀(不含".") + */ + public static String getFileType(File file) + { + if (null == file) + { + return StringUtils.EMPTY; + } + return getFileType(file.getName()); + } + + /** + * 获取文件类型 + *

+ * 例如: Carson.txt, 返回: txt + * + * @param fileName 文件名 + * @return 后缀(不含".") + */ + public static String getFileType(String fileName) + { + int separatorIndex = fileName.lastIndexOf("."); + if (separatorIndex < 0) + { + return ""; + } + return fileName.substring(separatorIndex + 1).toLowerCase(); + } + + /** + * 获取文件名的后缀 + * + * @param file 表单文件 + * @return 后缀名 + */ + public static final String getExtension(MultipartFile file) + { + String extension = FilenameUtils.getExtension(file.getOriginalFilename()); + if (StringUtils.isEmpty(extension)) + { + extension = MimeTypeUtils.getExtension(Objects.requireNonNull(file.getContentType())); + } + return extension; + } + + /** + * 获取文件类型 + * + * @param photoByte 文件字节码 + * @return 后缀(不含".") + */ + public static String getFileExtendName(byte[] photoByte) + { + String strFileExtendName = "JPG"; + if ((photoByte[0] == 71) && (photoByte[1] == 73) && (photoByte[2] == 70) && (photoByte[3] == 56) + && ((photoByte[4] == 55) || (photoByte[4] == 57)) && (photoByte[5] == 97)) + { + strFileExtendName = "GIF"; + } + else if ((photoByte[6] == 74) && (photoByte[7] == 70) && (photoByte[8] == 73) && (photoByte[9] == 70)) + { + strFileExtendName = "JPG"; + } + else if ((photoByte[0] == 66) && (photoByte[1] == 77)) + { + strFileExtendName = "BMP"; + } + else if ((photoByte[1] == 80) && (photoByte[2] == 78) && (photoByte[3] == 71)) + { + strFileExtendName = "PNG"; + } + return strFileExtendName; + } +} \ No newline at end of file diff --git a/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/utils/file/FileUtils.java b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/utils/file/FileUtils.java new file mode 100644 index 0000000..c2f8a26 --- /dev/null +++ b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/utils/file/FileUtils.java @@ -0,0 +1,249 @@ +package com.aiotagro.common.core.utils.file; + +import com.aiotagro.common.core.utils.StringUtils; +import org.apache.commons.lang3.ArrayUtils; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.*; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; + +/** + * 文件处理工具类 + * + * @author Carson + */ +public class FileUtils +{ + /** 字符常量:斜杠 {@code '/'} */ + public static final char SLASH = '/'; + + /** 字符常量:反斜杠 {@code '\\'} */ + public static final char BACKSLASH = '\\'; + + public static String FILENAME_PATTERN = "[a-zA-Z0-9_\\-\\|\\.\\u4e00-\\u9fa5]+"; + + /** + * 输出指定文件的byte数组 + * + * @param filePath 文件路径 + * @param os 输出流 + * @return + */ + public static void writeBytes(String filePath, OutputStream os) throws IOException + { + FileInputStream fis = null; + try + { + File file = new File(filePath); + if (!file.exists()) + { + throw new FileNotFoundException(filePath); + } + fis = new FileInputStream(file); + byte[] b = new byte[1024]; + int length; + while ((length = fis.read(b)) > 0) + { + os.write(b, 0, length); + } + } + catch (IOException e) + { + throw e; + } + finally + { + if (os != null) + { + try + { + os.close(); + } + catch (IOException e1) + { + e1.printStackTrace(); + } + } + if (fis != null) + { + try + { + fis.close(); + } + catch (IOException e1) + { + e1.printStackTrace(); + } + } + } + } + + /** + * 删除文件 + * + * @param filePath 文件 + * @return + */ + public static boolean deleteFile(String filePath) + { + boolean flag = false; + File file = new File(filePath); + // 路径为文件且不为空则进行删除 + if (file.isFile() && file.exists()) + { + flag = file.delete(); + } + return flag; + } + + /** + * 文件名称验证 + * + * @param filename 文件名称 + * @return true 正常 false 非法 + */ + public static boolean isValidFilename(String filename) + { + return filename.matches(FILENAME_PATTERN); + } + + /** + * 检查文件是否可下载 + * + * @param resource 需要下载的文件 + * @return true 正常 false 非法 + */ + public static boolean checkAllowDownload(String resource) + { + // 禁止目录上跳级别 + if (StringUtils.contains(resource, "..")) + { + return false; + } + // 判断是否在允许下载的文件规则内 + return ArrayUtils.contains(MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION, FileTypeUtils.getFileType(resource)); + } + + /** + * 下载文件名重新编码 + * + * @param request 请求对象 + * @param fileName 文件名 + * @return 编码后的文件名 + */ + public static String setFileDownloadHeader(HttpServletRequest request, String fileName) throws UnsupportedEncodingException + { + final String agent = request.getHeader("USER-AGENT"); + String filename = fileName; + if (agent.contains("MSIE")) + { + // IE浏览器 + filename = URLEncoder.encode(filename, "utf-8"); + filename = filename.replace("+", " "); + } + else if (agent.contains("Firefox")) + { + // 火狐浏览器 + filename = new String(fileName.getBytes(), "ISO8859-1"); + } + else if (agent.contains("Chrome")) + { + // google浏览器 + filename = URLEncoder.encode(filename, "utf-8"); + } + else + { + // 其它浏览器 + filename = URLEncoder.encode(filename, "utf-8"); + } + return filename; + } + + /** + * 返回文件名 + * + * @param filePath 文件 + * @return 文件名 + */ + public static String getName(String filePath) + { + if (null == filePath) + { + return null; + } + int len = filePath.length(); + if (0 == len) + { + return filePath; + } + if (isFileSeparator(filePath.charAt(len - 1))) + { + // 以分隔符结尾的去掉结尾分隔符 + len--; + } + + int begin = 0; + char c; + for (int i = len - 1; i > -1; i--) + { + c = filePath.charAt(i); + if (isFileSeparator(c)) + { + // 查找最后一个路径分隔符(/或者\) + begin = i + 1; + break; + } + } + + return filePath.substring(begin, len); + } + + /** + * 是否为Windows或者Linux(Unix)文件分隔符
+ * Windows平台下分隔符为\,Linux(Unix)为/ + * + * @param c 字符 + * @return 是否为Windows或者Linux(Unix)文件分隔符 + */ + public static boolean isFileSeparator(char c) + { + return SLASH == c || BACKSLASH == c; + } + + /** + * 下载文件名重新编码 + * + * @param response 响应对象 + * @param realFileName 真实文件名 + * @return + */ + public static void setAttachmentResponseHeader(HttpServletResponse response, String realFileName) throws UnsupportedEncodingException + { + String percentEncodedFileName = percentEncode(realFileName); + + StringBuilder contentDispositionValue = new StringBuilder(); + contentDispositionValue.append("attachment; filename=") + .append(percentEncodedFileName) + .append(";") + .append("filename*=") + .append("utf-8''") + .append(percentEncodedFileName); + + response.setHeader("Content-disposition", contentDispositionValue.toString()); + response.setHeader("download-filename", percentEncodedFileName); + } + + /** + * 百分号编码工具方法 + * + * @param s 需要百分号编码的字符串 + * @return 百分号编码后的字符串 + */ + public static String percentEncode(String s) throws UnsupportedEncodingException + { + String encode = URLEncoder.encode(s, StandardCharsets.UTF_8.toString()); + return encode.replaceAll("\\+", "%20"); + } +} diff --git a/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/utils/file/ImageUtils.java b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/utils/file/ImageUtils.java new file mode 100644 index 0000000..294cd41 --- /dev/null +++ b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/utils/file/ImageUtils.java @@ -0,0 +1,85 @@ +package com.aiotagro.common.core.utils.file; + +import org.apache.poi.util.IOUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.net.URL; +import java.net.URLConnection; +import java.util.Arrays; + +/** + * 图片处理工具类 + * + * @author Carson + */ +public class ImageUtils +{ + private static final Logger log = LoggerFactory.getLogger(ImageUtils.class); + + public static byte[] getImage(String imagePath) + { + InputStream is = getFile(imagePath); + try + { + return IOUtils.toByteArray(is); + } + catch (Exception e) + { + log.error("图片加载异常 {}", e); + return null; + } + finally + { + IOUtils.closeQuietly(is); + } + } + + public static InputStream getFile(String imagePath) + { + try + { + byte[] result = readFile(imagePath); + result = Arrays.copyOf(result, result.length); + return new ByteArrayInputStream(result); + } + catch (Exception e) + { + log.error("获取图片异常 {}", e); + } + return null; + } + + /** + * 读取文件为字节数据 + * + * @param url 地址 + * @return 字节数据 + */ + public static byte[] readFile(String url) + { + InputStream in = null; + try + { + // 网络地址 + URL urlObj = new URL(url); + URLConnection urlConnection = urlObj.openConnection(); + urlConnection.setConnectTimeout(30 * 1000); + urlConnection.setReadTimeout(60 * 1000); + urlConnection.setDoInput(true); + in = urlConnection.getInputStream(); + return IOUtils.toByteArray(in); + } + catch (Exception e) + { + log.error("访问文件异常 {}", e); + return null; + } + finally + { + IOUtils.closeQuietly(in); + } + } +} diff --git a/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/utils/file/MimeTypeUtils.java b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/utils/file/MimeTypeUtils.java new file mode 100644 index 0000000..e40fea0 --- /dev/null +++ b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/utils/file/MimeTypeUtils.java @@ -0,0 +1,59 @@ +package com.aiotagro.common.core.utils.file; + +/** + * 媒体类型工具类 + * + * @author Carson + */ +public class MimeTypeUtils +{ + public static final String IMAGE_PNG = "image/png"; + + public static final String IMAGE_JPG = "image/jpg"; + + public static final String IMAGE_JPEG = "image/jpeg"; + + public static final String IMAGE_BMP = "image/bmp"; + + public static final String IMAGE_GIF = "image/gif"; + + public static final String[] IMAGE_EXTENSION = { "bmp", "gif", "jpg", "jpeg", "png" }; + + public static final String[] FLASH_EXTENSION = { "swf", "flv" }; + + public static final String[] MEDIA_EXTENSION = { "swf", "flv", "mp3", "wav", "wma", "wmv", "mid", "avi", "mpg", + "asf", "rm", "rmvb" }; + + public static final String[] VIDEO_EXTENSION = { "mp4", "avi", "rmvb" }; + + public static final String[] DEFAULT_ALLOWED_EXTENSION = { + // 图片 + "bmp", "gif", "jpg", "jpeg", "png", + // word excel powerpoint + "doc", "docx", "xls", "xlsx", "ppt", "pptx", "html", "htm", "txt", + // 压缩文件 + "rar", "zip", "gz", "bz2", + // 视频格式 + "mp4", "avi", "rmvb", + // pdf + "pdf" }; + + public static String getExtension(String prefix) + { + switch (prefix) + { + case IMAGE_PNG: + return "png"; + case IMAGE_JPG: + return "jpg"; + case IMAGE_JPEG: + return "jpeg"; + case IMAGE_BMP: + return "bmp"; + case IMAGE_GIF: + return "gif"; + default: + return ""; + } + } +} diff --git a/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/utils/html/EscapeUtil.java b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/utils/html/EscapeUtil.java new file mode 100644 index 0000000..bbb08b6 --- /dev/null +++ b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/utils/html/EscapeUtil.java @@ -0,0 +1,167 @@ +package com.aiotagro.common.core.utils.html; + +import com.aiotagro.common.core.utils.StringUtils; + +/** + * 转义和反转义工具类 + * + * @author Carson + */ +public class EscapeUtil +{ + public static final String RE_HTML_MARK = "(<[^<]*?>)|(<[\\s]*?/[^<]*?>)|(<[^<]*?/[\\s]*?>)"; + + private static final char[][] TEXT = new char[64][]; + + static + { + for (int i = 0; i < 64; i++) + { + TEXT[i] = new char[] { (char) i }; + } + + // special HTML characters + TEXT['\''] = "'".toCharArray(); // 单引号 + TEXT['"'] = """.toCharArray(); // 双引号 + TEXT['&'] = "&".toCharArray(); // &符 + TEXT['<'] = "<".toCharArray(); // 小于号 + TEXT['>'] = ">".toCharArray(); // 大于号 + } + + /** + * 转义文本中的HTML字符为安全的字符 + * + * @param text 被转义的文本 + * @return 转义后的文本 + */ + public static String escape(String text) + { + return encode(text); + } + + /** + * 还原被转义的HTML特殊字符 + * + * @param content 包含转义符的HTML内容 + * @return 转换后的字符串 + */ + public static String unescape(String content) + { + return decode(content); + } + + /** + * 清除所有HTML标签,但是不删除标签内的内容 + * + * @param content 文本 + * @return 清除标签后的文本 + */ + public static String clean(String content) + { + return new HTMLFilter().filter(content); + } + + /** + * Escape编码 + * + * @param text 被编码的文本 + * @return 编码后的字符 + */ + private static String encode(String text) + { + if (StringUtils.isEmpty(text)) + { + return StringUtils.EMPTY; + } + + final StringBuilder tmp = new StringBuilder(text.length() * 6); + char c; + for (int i = 0; i < text.length(); i++) + { + c = text.charAt(i); + if (c < 256) + { + tmp.append("%"); + if (c < 16) + { + tmp.append("0"); + } + tmp.append(Integer.toString(c, 16)); + } + else + { + tmp.append("%u"); + if (c <= 0xfff) + { + // issue#I49JU8@Gitee + tmp.append("0"); + } + tmp.append(Integer.toString(c, 16)); + } + } + return tmp.toString(); + } + + /** + * Escape解码 + * + * @param content 被转义的内容 + * @return 解码后的字符串 + */ + public static String decode(String content) + { + if (StringUtils.isEmpty(content)) + { + return content; + } + + StringBuilder tmp = new StringBuilder(content.length()); + int lastPos = 0, pos = 0; + char ch; + while (lastPos < content.length()) + { + pos = content.indexOf("%", lastPos); + if (pos == lastPos) + { + if (content.charAt(pos + 1) == 'u') + { + ch = (char) Integer.parseInt(content.substring(pos + 2, pos + 6), 16); + tmp.append(ch); + lastPos = pos + 6; + } + else + { + ch = (char) Integer.parseInt(content.substring(pos + 1, pos + 3), 16); + tmp.append(ch); + lastPos = pos + 3; + } + } + else + { + if (pos == -1) + { + tmp.append(content.substring(lastPos)); + lastPos = content.length(); + } + else + { + tmp.append(content.substring(lastPos, pos)); + lastPos = pos; + } + } + } + return tmp.toString(); + } + + public static void main(String[] args) + { + String html = ""; + String escape = EscapeUtil.escape(html); + // String html = "ipt>alert(\"XSS\")ipt>"; + // String html = "<123"; + // String html = "123>"; + System.out.println("clean: " + EscapeUtil.clean(html)); + System.out.println("escape: " + escape); + System.out.println("unescape: " + EscapeUtil.unescape(escape)); + } +} diff --git a/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/utils/html/HTMLFilter.java b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/utils/html/HTMLFilter.java new file mode 100644 index 0000000..49fc521 --- /dev/null +++ b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/utils/html/HTMLFilter.java @@ -0,0 +1,566 @@ +package com.aiotagro.common.core.utils.html; + +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * HTML过滤器,用于去除XSS漏洞隐患。 + * + * @author Carson + */ +public final class HTMLFilter +{ + /** + * regex flag union representing /si modifiers in php + **/ + private static final int REGEX_FLAGS_SI = Pattern.CASE_INSENSITIVE | Pattern.DOTALL; + private static final Pattern P_COMMENTS = Pattern.compile("", Pattern.DOTALL); + private static final Pattern P_COMMENT = Pattern.compile("^!--(.*)--$", REGEX_FLAGS_SI); + private static final Pattern P_TAGS = Pattern.compile("<(.*?)>", Pattern.DOTALL); + private static final Pattern P_END_TAG = Pattern.compile("^/([a-z0-9]+)", REGEX_FLAGS_SI); + private static final Pattern P_START_TAG = Pattern.compile("^([a-z0-9]+)(.*?)(/?)$", REGEX_FLAGS_SI); + private static final Pattern P_QUOTED_ATTRIBUTES = Pattern.compile("([a-z0-9]+)=([\"'])(.*?)\\2", REGEX_FLAGS_SI); + private static final Pattern P_UNQUOTED_ATTRIBUTES = Pattern.compile("([a-z0-9]+)(=)([^\"\\s']+)", REGEX_FLAGS_SI); + private static final Pattern P_PROTOCOL = Pattern.compile("^([^:]+):", REGEX_FLAGS_SI); + private static final Pattern P_ENTITY = Pattern.compile("&#(\\d+);?"); + private static final Pattern P_ENTITY_UNICODE = Pattern.compile("&#x([0-9a-f]+);?"); + private static final Pattern P_ENCODE = Pattern.compile("%([0-9a-f]{2});?"); + private static final Pattern P_VALID_ENTITIES = Pattern.compile("&([^&;]*)(?=(;|&|$))"); + private static final Pattern P_VALID_QUOTES = Pattern.compile("(>|^)([^<]+?)(<|$)", Pattern.DOTALL); + private static final Pattern P_END_ARROW = Pattern.compile("^>"); + private static final Pattern P_BODY_TO_END = Pattern.compile("<([^>]*?)(?=<|$)"); + private static final Pattern P_XML_CONTENT = Pattern.compile("(^|>)([^<]*?)(?=>)"); + private static final Pattern P_STRAY_LEFT_ARROW = Pattern.compile("<([^>]*?)(?=<|$)"); + private static final Pattern P_STRAY_RIGHT_ARROW = Pattern.compile("(^|>)([^<]*?)(?=>)"); + private static final Pattern P_AMP = Pattern.compile("&"); + private static final Pattern P_QUOTE = Pattern.compile("\""); + private static final Pattern P_LEFT_ARROW = Pattern.compile("<"); + private static final Pattern P_RIGHT_ARROW = Pattern.compile(">"); + private static final Pattern P_BOTH_ARROWS = Pattern.compile("<>"); + + // @xxx could grow large... maybe use sesat's ReferenceMap + private static final ConcurrentMap P_REMOVE_PAIR_BLANKS = new ConcurrentHashMap<>(); + private static final ConcurrentMap P_REMOVE_SELF_BLANKS = new ConcurrentHashMap<>(); + + /** + * set of allowed html elements, along with allowed attributes for each element + **/ + private final Map> vAllowed; + /** + * counts of open tags for each (allowable) html element + **/ + private final Map vTagCounts = new HashMap<>(); + + /** + * html elements which must always be self-closing (e.g. "") + **/ + private final String[] vSelfClosingTags; + /** + * html elements which must always have separate opening and closing tags (e.g. "") + **/ + private final String[] vNeedClosingTags; + /** + * set of disallowed html elements + **/ + private final String[] vDisallowed; + /** + * attributes which should be checked for valid protocols + **/ + private final String[] vProtocolAtts; + /** + * allowed protocols + **/ + private final String[] vAllowedProtocols; + /** + * tags which should be removed if they contain no content (e.g. "" or "") + **/ + private final String[] vRemoveBlanks; + /** + * entities allowed within html markup + **/ + private final String[] vAllowedEntities; + /** + * flag determining whether comments are allowed in input String. + */ + private final boolean stripComment; + private final boolean encodeQuotes; + /** + * flag determining whether to try to make tags when presented with "unbalanced" angle brackets (e.g. "" + * becomes " text "). If set to false, unbalanced angle brackets will be html escaped. + */ + private final boolean alwaysMakeTags; + + /** + * Default constructor. + */ + public HTMLFilter() + { + vAllowed = new HashMap<>(); + + final ArrayList a_atts = new ArrayList<>(); + a_atts.add("href"); + a_atts.add("target"); + vAllowed.put("a", a_atts); + + final ArrayList img_atts = new ArrayList<>(); + img_atts.add("src"); + img_atts.add("width"); + img_atts.add("height"); + img_atts.add("alt"); + vAllowed.put("img", img_atts); + + final ArrayList no_atts = new ArrayList<>(); + vAllowed.put("b", no_atts); + vAllowed.put("strong", no_atts); + vAllowed.put("i", no_atts); + vAllowed.put("em", no_atts); + + vSelfClosingTags = new String[] { "img" }; + vNeedClosingTags = new String[] { "a", "b", "strong", "i", "em" }; + vDisallowed = new String[] {}; + vAllowedProtocols = new String[] { "http", "mailto", "https" }; // no ftp. + vProtocolAtts = new String[] { "src", "href" }; + vRemoveBlanks = new String[] { "a", "b", "strong", "i", "em" }; + vAllowedEntities = new String[] { "amp", "gt", "lt", "quot" }; + stripComment = true; + encodeQuotes = true; + alwaysMakeTags = false; + } + + /** + * Map-parameter configurable constructor. + * + * @param conf map containing configuration. keys match field names. + */ + @SuppressWarnings("unchecked") + public HTMLFilter(final Map conf) + { + + assert conf.containsKey("vAllowed") : "configuration requires vAllowed"; + assert conf.containsKey("vSelfClosingTags") : "configuration requires vSelfClosingTags"; + assert conf.containsKey("vNeedClosingTags") : "configuration requires vNeedClosingTags"; + assert conf.containsKey("vDisallowed") : "configuration requires vDisallowed"; + assert conf.containsKey("vAllowedProtocols") : "configuration requires vAllowedProtocols"; + assert conf.containsKey("vProtocolAtts") : "configuration requires vProtocolAtts"; + assert conf.containsKey("vRemoveBlanks") : "configuration requires vRemoveBlanks"; + assert conf.containsKey("vAllowedEntities") : "configuration requires vAllowedEntities"; + + vAllowed = Collections.unmodifiableMap((HashMap>) conf.get("vAllowed")); + vSelfClosingTags = (String[]) conf.get("vSelfClosingTags"); + vNeedClosingTags = (String[]) conf.get("vNeedClosingTags"); + vDisallowed = (String[]) conf.get("vDisallowed"); + vAllowedProtocols = (String[]) conf.get("vAllowedProtocols"); + vProtocolAtts = (String[]) conf.get("vProtocolAtts"); + vRemoveBlanks = (String[]) conf.get("vRemoveBlanks"); + vAllowedEntities = (String[]) conf.get("vAllowedEntities"); + stripComment = conf.containsKey("stripComment") ? (Boolean) conf.get("stripComment") : true; + encodeQuotes = conf.containsKey("encodeQuotes") ? (Boolean) conf.get("encodeQuotes") : true; + alwaysMakeTags = conf.containsKey("alwaysMakeTags") ? (Boolean) conf.get("alwaysMakeTags") : true; + } + + private void reset() + { + vTagCounts.clear(); + } + + // --------------------------------------------------------------- + // my versions of some PHP library functions + public static String chr(final int decimal) + { + return String.valueOf((char) decimal); + } + + public static String htmlSpecialChars(final String s) + { + String result = s; + result = regexReplace(P_AMP, "&", result); + result = regexReplace(P_QUOTE, """, result); + result = regexReplace(P_LEFT_ARROW, "<", result); + result = regexReplace(P_RIGHT_ARROW, ">", result); + return result; + } + + // --------------------------------------------------------------- + + /** + * given a user submitted input String, filter out any invalid or restricted html. + * + * @param input text (i.e. submitted by a user) than may contain html + * @return "clean" version of input, with only valid, whitelisted html elements allowed + */ + public String filter(final String input) + { + reset(); + String s = input; + + s = escapeComments(s); + + s = balanceHTML(s); + + s = checkTags(s); + + s = processRemoveBlanks(s); + + // s = validateEntities(s); + + return s; + } + + public boolean isAlwaysMakeTags() + { + return alwaysMakeTags; + } + + public boolean isStripComments() + { + return stripComment; + } + + private String escapeComments(final String s) + { + final Matcher m = P_COMMENTS.matcher(s); + final StringBuffer buf = new StringBuffer(); + if (m.find()) + { + final String match = m.group(1); // (.*?) + m.appendReplacement(buf, Matcher.quoteReplacement("")); + } + m.appendTail(buf); + + return buf.toString(); + } + + private String balanceHTML(String s) + { + if (alwaysMakeTags) + { + // + // try and form html + // + s = regexReplace(P_END_ARROW, "", s); + // 不追加结束标签 + s = regexReplace(P_BODY_TO_END, "<$1>", s); + s = regexReplace(P_XML_CONTENT, "$1<$2", s); + + } + else + { + // + // escape stray brackets + // + s = regexReplace(P_STRAY_LEFT_ARROW, "<$1", s); + s = regexReplace(P_STRAY_RIGHT_ARROW, "$1$2><", s); + + // + // the last regexp causes '<>' entities to appear + // (we need to do a lookahead assertion so that the last bracket can + // be used in the next pass of the regexp) + // + s = regexReplace(P_BOTH_ARROWS, "", s); + } + + return s; + } + + private String checkTags(String s) + { + Matcher m = P_TAGS.matcher(s); + + final StringBuffer buf = new StringBuffer(); + while (m.find()) + { + String replaceStr = m.group(1); + replaceStr = processTag(replaceStr); + m.appendReplacement(buf, Matcher.quoteReplacement(replaceStr)); + } + m.appendTail(buf); + + // these get tallied in processTag + // (remember to reset before subsequent calls to filter method) + final StringBuilder sBuilder = new StringBuilder(buf.toString()); + for (String key : vTagCounts.keySet()) + { + for (int ii = 0; ii < vTagCounts.get(key); ii++) + { + sBuilder.append(""); + } + } + s = sBuilder.toString(); + + return s; + } + + private String processRemoveBlanks(final String s) + { + String result = s; + for (String tag : vRemoveBlanks) + { + if (!P_REMOVE_PAIR_BLANKS.containsKey(tag)) + { + P_REMOVE_PAIR_BLANKS.putIfAbsent(tag, Pattern.compile("<" + tag + "(\\s[^>]*)?>")); + } + result = regexReplace(P_REMOVE_PAIR_BLANKS.get(tag), "", result); + if (!P_REMOVE_SELF_BLANKS.containsKey(tag)) + { + P_REMOVE_SELF_BLANKS.putIfAbsent(tag, Pattern.compile("<" + tag + "(\\s[^>]*)?/>")); + } + result = regexReplace(P_REMOVE_SELF_BLANKS.get(tag), "", result); + } + + return result; + } + + private static String regexReplace(final Pattern regex_pattern, final String replacement, final String s) + { + Matcher m = regex_pattern.matcher(s); + return m.replaceAll(replacement); + } + + private String processTag(final String s) + { + // ending tags + Matcher m = P_END_TAG.matcher(s); + if (m.find()) + { + final String name = m.group(1).toLowerCase(); + if (allowed(name)) + { + if (!inArray(name, vSelfClosingTags)) + { + if (vTagCounts.containsKey(name)) + { + vTagCounts.put(name, vTagCounts.get(name) - 1); + return ""; + } + } + } + } + + // starting tags + m = P_START_TAG.matcher(s); + if (m.find()) + { + final String name = m.group(1).toLowerCase(); + final String body = m.group(2); + String ending = m.group(3); + + // debug( "in a starting tag, name='" + name + "'; body='" + body + "'; ending='" + ending + "'" ); + if (allowed(name)) + { + final StringBuilder params = new StringBuilder(); + + final Matcher m2 = P_QUOTED_ATTRIBUTES.matcher(body); + final Matcher m3 = P_UNQUOTED_ATTRIBUTES.matcher(body); + final List paramNames = new ArrayList<>(); + final List paramValues = new ArrayList<>(); + while (m2.find()) + { + paramNames.add(m2.group(1)); // ([a-z0-9]+) + paramValues.add(m2.group(3)); // (.*?) + } + while (m3.find()) + { + paramNames.add(m3.group(1)); // ([a-z0-9]+) + paramValues.add(m3.group(3)); // ([^\"\\s']+) + } + + String paramName, paramValue; + for (int ii = 0; ii < paramNames.size(); ii++) + { + paramName = paramNames.get(ii).toLowerCase(); + paramValue = paramValues.get(ii); + + // debug( "paramName='" + paramName + "'" ); + // debug( "paramValue='" + paramValue + "'" ); + // debug( "allowed? " + vAllowed.get( name ).contains( paramName ) ); + + if (allowedAttribute(name, paramName)) + { + if (inArray(paramName, vProtocolAtts)) + { + paramValue = processParamProtocol(paramValue); + } + params.append(' ').append(paramName).append("=\\\"").append(paramValue).append("\\\""); + } + } + + if (inArray(name, vSelfClosingTags)) + { + ending = " /"; + } + + if (inArray(name, vNeedClosingTags)) + { + ending = ""; + } + + if (ending == null || ending.length() < 1) + { + if (vTagCounts.containsKey(name)) + { + vTagCounts.put(name, vTagCounts.get(name) + 1); + } + else + { + vTagCounts.put(name, 1); + } + } + else + { + ending = " /"; + } + return "<" + name + params + ending + ">"; + } + else + { + return ""; + } + } + + // comments + m = P_COMMENT.matcher(s); + if (!stripComment && m.find()) + { + return "<" + m.group() + ">"; + } + + return ""; + } + + private String processParamProtocol(String s) + { + s = decodeEntities(s); + final Matcher m = P_PROTOCOL.matcher(s); + if (m.find()) + { + final String protocol = m.group(1); + if (!inArray(protocol, vAllowedProtocols)) + { + // bad protocol, turn into local anchor link instead + s = "#" + s.substring(protocol.length() + 1); + if (s.startsWith("#//")) + { + s = "#" + s.substring(3); + } + } + } + + return s; + } + + private String decodeEntities(String s) + { + StringBuffer buf = new StringBuffer(); + + Matcher m = P_ENTITY.matcher(s); + while (m.find()) + { + final String match = m.group(1); + final int decimal = Integer.decode(match).intValue(); + m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal))); + } + m.appendTail(buf); + s = buf.toString(); + + buf = new StringBuffer(); + m = P_ENTITY_UNICODE.matcher(s); + while (m.find()) + { + final String match = m.group(1); + final int decimal = Integer.valueOf(match, 16).intValue(); + m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal))); + } + m.appendTail(buf); + s = buf.toString(); + + buf = new StringBuffer(); + m = P_ENCODE.matcher(s); + while (m.find()) + { + final String match = m.group(1); + final int decimal = Integer.valueOf(match, 16).intValue(); + m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal))); + } + m.appendTail(buf); + s = buf.toString(); + + s = validateEntities(s); + return s; + } + + private String validateEntities(final String s) + { + StringBuffer buf = new StringBuffer(); + + // validate entities throughout the string + Matcher m = P_VALID_ENTITIES.matcher(s); + while (m.find()) + { + final String one = m.group(1); // ([^&;]*) + final String two = m.group(2); // (?=(;|&|$)) + m.appendReplacement(buf, Matcher.quoteReplacement(checkEntity(one, two))); + } + m.appendTail(buf); + + return encodeQuotes(buf.toString()); + } + + private String encodeQuotes(final String s) + { + if (encodeQuotes) + { + StringBuffer buf = new StringBuffer(); + Matcher m = P_VALID_QUOTES.matcher(s); + while (m.find()) + { + final String one = m.group(1); // (>|^) + final String two = m.group(2); // ([^<]+?) + final String three = m.group(3); // (<|$) + // 不替换双引号为",防止json格式无效 regexReplace(P_QUOTE, """, two) + m.appendReplacement(buf, Matcher.quoteReplacement(one + two + three)); + } + m.appendTail(buf); + return buf.toString(); + } + else + { + return s; + } + } + + private String checkEntity(final String preamble, final String term) + { + + return ";".equals(term) && isValidEntity(preamble) ? '&' + preamble : "&" + preamble; + } + + private boolean isValidEntity(final String entity) + { + return inArray(entity, vAllowedEntities); + } + + private static boolean inArray(final String s, final String[] array) + { + for (String item : array) + { + if (item != null && item.equals(s)) + { + return true; + } + } + return false; + } + + private boolean allowed(final String name) + { + return (vAllowed.isEmpty() || vAllowed.containsKey(name)) && !inArray(name, vDisallowed); + } + + private boolean allowedAttribute(final String name, final String paramName) + { + return allowed(name) && (vAllowed.isEmpty() || vAllowed.get(name).contains(paramName)); + } +} \ No newline at end of file diff --git a/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/utils/ip/IpUtils.java b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/utils/ip/IpUtils.java new file mode 100644 index 0000000..d432e7e --- /dev/null +++ b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/utils/ip/IpUtils.java @@ -0,0 +1,383 @@ +package com.aiotagro.common.core.utils.ip; + +import com.aiotagro.common.core.utils.ServletUtils; +import com.aiotagro.common.core.utils.StringUtils; + +import javax.servlet.http.HttpServletRequest; +import java.net.InetAddress; +import java.net.UnknownHostException; + +/** + * 获取IP方法 + * + * @author Carson + */ +public class IpUtils +{ + public final static String REGX_0_255 = "(25[0-5]|2[0-4]\\d|1\\d{2}|[1-9]\\d|\\d)"; + // 匹配 ip + public final static String REGX_IP = "((" + REGX_0_255 + "\\.){3}" + REGX_0_255 + ")"; + public final static String REGX_IP_WILDCARD = "(((\\*\\.){3}\\*)|(" + REGX_0_255 + "(\\.\\*){3})|(" + REGX_0_255 + "\\." + REGX_0_255 + ")(\\.\\*){2}" + "|((" + REGX_0_255 + "\\.){3}\\*))"; + // 匹配网段 + public final static String REGX_IP_SEG = "(" + REGX_IP + "\\-" + REGX_IP + ")"; + + /** + * 获取客户端IP + * + * @return IP地址 + */ + public static String getIpAddr() + { + return getIpAddr(ServletUtils.getRequest()); + } + + /** + * 获取客户端IP + * + * @param request 请求对象 + * @return IP地址 + */ + public static String getIpAddr(HttpServletRequest request) + { + if (request == null) + { + return "unknown"; + } + String ip = request.getHeader("x-forwarded-for"); + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) + { + ip = request.getHeader("Proxy-Client-IP"); + } + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) + { + ip = request.getHeader("X-Forwarded-For"); + } + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) + { + ip = request.getHeader("WL-Proxy-Client-IP"); + } + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) + { + ip = request.getHeader("X-Real-IP"); + } + + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) + { + ip = request.getRemoteAddr(); + } + + return "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : getMultistageReverseProxyIp(ip); + } + + /** + * 检查是否为内部IP地址 + * + * @param ip IP地址 + * @return 结果 + */ + public static boolean internalIp(String ip) + { + byte[] addr = textToNumericFormatV4(ip); + return internalIp(addr) || "127.0.0.1".equals(ip); + } + + /** + * 检查是否为内部IP地址 + * + * @param addr byte地址 + * @return 结果 + */ + private static boolean internalIp(byte[] addr) + { + if (StringUtils.isNull(addr) || addr.length < 2) + { + return true; + } + final byte b0 = addr[0]; + final byte b1 = addr[1]; + // 10.x.x.x/8 + final byte SECTION_1 = 0x0A; + // 172.16.x.x/12 + final byte SECTION_2 = (byte) 0xAC; + final byte SECTION_3 = (byte) 0x10; + final byte SECTION_4 = (byte) 0x1F; + // 192.168.x.x/16 + final byte SECTION_5 = (byte) 0xC0; + final byte SECTION_6 = (byte) 0xA8; + switch (b0) + { + case SECTION_1: + return true; + case SECTION_2: + if (b1 >= SECTION_3 && b1 <= SECTION_4) + { + return true; + } + case SECTION_5: + switch (b1) + { + case SECTION_6: + return true; + } + default: + return false; + } + } + + /** + * 将IPv4地址转换成字节 + * + * @param text IPv4地址 + * @return byte 字节 + */ + public static byte[] textToNumericFormatV4(String text) + { + if (text.length() == 0) + { + return null; + } + + byte[] bytes = new byte[4]; + String[] elements = text.split("\\.", -1); + try + { + long l; + int i; + switch (elements.length) + { + case 1: + l = Long.parseLong(elements[0]); + if ((l < 0L) || (l > 4294967295L)) + { + return null; + } + bytes[0] = (byte) (int) (l >> 24 & 0xFF); + bytes[1] = (byte) (int) ((l & 0xFFFFFF) >> 16 & 0xFF); + bytes[2] = (byte) (int) ((l & 0xFFFF) >> 8 & 0xFF); + bytes[3] = (byte) (int) (l & 0xFF); + break; + case 2: + l = Integer.parseInt(elements[0]); + if ((l < 0L) || (l > 255L)) + { + return null; + } + bytes[0] = (byte) (int) (l & 0xFF); + l = Integer.parseInt(elements[1]); + if ((l < 0L) || (l > 16777215L)) + { + return null; + } + bytes[1] = (byte) (int) (l >> 16 & 0xFF); + bytes[2] = (byte) (int) ((l & 0xFFFF) >> 8 & 0xFF); + bytes[3] = (byte) (int) (l & 0xFF); + break; + case 3: + for (i = 0; i < 2; ++i) + { + l = Integer.parseInt(elements[i]); + if ((l < 0L) || (l > 255L)) + { + return null; + } + bytes[i] = (byte) (int) (l & 0xFF); + } + l = Integer.parseInt(elements[2]); + if ((l < 0L) || (l > 65535L)) + { + return null; + } + bytes[2] = (byte) (int) (l >> 8 & 0xFF); + bytes[3] = (byte) (int) (l & 0xFF); + break; + case 4: + for (i = 0; i < 4; ++i) + { + l = Integer.parseInt(elements[i]); + if ((l < 0L) || (l > 255L)) + { + return null; + } + bytes[i] = (byte) (int) (l & 0xFF); + } + break; + default: + return null; + } + } + catch (NumberFormatException e) + { + return null; + } + return bytes; + } + + /** + * 获取IP地址 + * + * @return 本地IP地址 + */ + public static String getHostIp() + { + try + { + return InetAddress.getLocalHost().getHostAddress(); + } + catch (UnknownHostException e) + { + } + return "127.0.0.1"; + } + + /** + * 获取主机名 + * + * @return 本地主机名 + */ + public static String getHostName() + { + try + { + return InetAddress.getLocalHost().getHostName(); + } + catch (UnknownHostException e) + { + } + return "未知"; + } + + /** + * 从多级反向代理中获得第一个非unknown IP地址 + * + * @param ip 获得的IP地址 + * @return 第一个非unknown IP地址 + */ + public static String getMultistageReverseProxyIp(String ip) + { + // 多级反向代理检测 + if (ip != null && ip.indexOf(",") > 0) + { + final String[] ips = ip.trim().split(","); + for (String subIp : ips) + { + if (false == isUnknown(subIp)) + { + ip = subIp; + break; + } + } + } + return StringUtils.substring(ip, 0, 255); + } + + /** + * 检测给定字符串是否为未知,多用于检测HTTP请求相关 + * + * @param checkString 被检测的字符串 + * @return 是否未知 + */ + public static boolean isUnknown(String checkString) + { + return StringUtils.isBlank(checkString) || "unknown".equalsIgnoreCase(checkString); + } + + /** + * 是否为IP + */ + public static boolean isIP(String ip) + { + return StringUtils.isNotBlank(ip) && ip.matches(REGX_IP); + } + + /** + * 是否为IP,或 *为间隔的通配符地址 + */ + public static boolean isIpWildCard(String ip) + { + return StringUtils.isNotBlank(ip) && ip.matches(REGX_IP_WILDCARD); + } + + /** + * 检测参数是否在ip通配符里 + */ + public static boolean ipIsInWildCardNoCheck(String ipWildCard, String ip) + { + String[] s1 = ipWildCard.split("\\."); + String[] s2 = ip.split("\\."); + boolean isMatchedSeg = true; + for (int i = 0; i < s1.length && !s1[i].equals("*"); i++) + { + if (!s1[i].equals(s2[i])) + { + isMatchedSeg = false; + break; + } + } + return isMatchedSeg; + } + + /** + * 是否为特定格式如:“10.10.10.1-10.10.10.99”的ip段字符串 + */ + public static boolean isIPSegment(String ipSeg) + { + return StringUtils.isNotBlank(ipSeg) && ipSeg.matches(REGX_IP_SEG); + } + + /** + * 判断ip是否在指定网段中 + */ + public static boolean ipIsInNetNoCheck(String iparea, String ip) + { + int idx = iparea.indexOf('-'); + String[] sips = iparea.substring(0, idx).split("\\."); + String[] sipe = iparea.substring(idx + 1).split("\\."); + String[] sipt = ip.split("\\."); + long ips = 0L, ipe = 0L, ipt = 0L; + for (int i = 0; i < 4; ++i) + { + ips = ips << 8 | Integer.parseInt(sips[i]); + ipe = ipe << 8 | Integer.parseInt(sipe[i]); + ipt = ipt << 8 | Integer.parseInt(sipt[i]); + } + if (ips > ipe) + { + long t = ips; + ips = ipe; + ipe = t; + } + return ips <= ipt && ipt <= ipe; + } + + /** + * 校验ip是否符合过滤串规则 + * + * @param filter 过滤IP列表,支持后缀'*'通配,支持网段如:`10.10.10.1-10.10.10.99` + * @param ip 校验IP地址 + * @return boolean 结果 + */ + public static boolean isMatchedIp(String filter, String ip) + { + if (StringUtils.isEmpty(filter) || StringUtils.isEmpty(ip)) + { + return false; + } + String[] ips = filter.split(";"); + for (String iStr : ips) + { + if (isIP(iStr) && iStr.equals(ip)) + { + return true; + } + else if (isIpWildCard(iStr) && ipIsInWildCardNoCheck(iStr, ip)) + { + return true; + } + else if (isIPSegment(iStr) && ipIsInNetNoCheck(iStr, ip)) + { + return true; + } + } + return false; + } +} \ No newline at end of file diff --git a/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/utils/poi/EasyExcelUtil.java b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/utils/poi/EasyExcelUtil.java new file mode 100644 index 0000000..f25123e --- /dev/null +++ b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/utils/poi/EasyExcelUtil.java @@ -0,0 +1,108 @@ +package com.aiotagro.common.core.utils.poi; + +import com.alibaba.excel.EasyExcel; +import com.alibaba.excel.context.AnalysisContext; +import com.alibaba.excel.event.AnalysisEventListener; +import com.alibaba.excel.write.style.column.SimpleColumnWidthStyleStrategy; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.servlet.http.HttpServletResponse; +import java.io.*; +import java.net.URLEncoder; +import java.util.ArrayList; +import java.util.List; + +/** + * @author xiefan + * @description easyexcel工具类 + */ +public class EasyExcelUtil { + + private static final Logger logger = LoggerFactory.getLogger(EasyExcelUtil.class); + + public static List read(String filePath, final Class clazz) { + File f = new File(filePath); + try (FileInputStream fis = new FileInputStream(f)) { + return read(fis, clazz); + } catch (FileNotFoundException e) { + logger.error("文件{}不存在", filePath, e); + } catch (IOException e) { + logger.error("文件读取出错", e); + } + return null; + } + + public static List read(InputStream inputStream, final Class clazz) { + if (inputStream == null) { + throw new RuntimeException("解析出错了,文件流是null"); + } + // 有个很重要的点 DataListener 不能被spring管理,要每次读取excel都要new,然后里面用到spring可以构造方法传进去 + DataListener listener = new DataListener<>(); + // 这里 需要指定读用哪个class去读,然后读取第一个sheet 文件流会自动关闭 + EasyExcel.read(inputStream, clazz, listener).sheet().doRead(); + return listener.getRows(); + } + + public static List readSkipHead(InputStream inputStream, final Class clazz, int row) { + if (inputStream == null) { + throw new RuntimeException("解析出错了,文件流是null"); + } + // 有个很重要的点 DataListener 不能被spring管理,要每次读取excel都要new,然后里面用到spring可以构造方法传进去 + DataListener listener = new DataListener<>(); + // 这里 需要指定读用哪个class去读,然后读取第一个sheet 文件流会自动关闭 + EasyExcel.read(inputStream, clazz, listener).sheet().headRowNumber(row).doRead(); + return listener.getRows(); + } + + public static void write(String outFile, List list) { + Class clazz = list.get(0).getClass(); + EasyExcel.write(outFile, clazz).sheet().doWrite(list); + } + + public static void write(String outFile, List list, String sheetName) { + Class clazz = list.get(0).getClass(); + EasyExcel.write(outFile, clazz).sheet(sheetName).doWrite(list); + } + + public static void write(OutputStream outputStream, List list, String sheetName) { + Class clazz = list.get(0).getClass(); + // sheetName为sheet的名字,默认写第一个sheet + EasyExcel.write(outputStream, clazz).sheet(sheetName).doWrite(list); + } + + /** + * 文件下载(失败了会返回一个有部分数据的Excel),用于直接把excel返回到浏览器下载 + */ + public static void download(HttpServletResponse response, List list, String sheetName, Class clazz) throws IOException { + response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); + response.setCharacterEncoding("utf-8"); + // 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系 + String fileName = URLEncoder.encode(sheetName, "UTF-8").replaceAll("\\+", "%20"); + response.setHeader("Content-disposition", "attachment;filename*=" + fileName + ".xlsx"); + EasyExcel.write(response.getOutputStream(), clazz).head(clazz).sheet(sheetName). + registerWriteHandler(new SimpleColumnWidthStyleStrategy(17)).doWrite(list); + } +} + +class DataListener extends AnalysisEventListener { + + private static final Logger logger = LoggerFactory.getLogger(DataListener.class); + + private final List rows = new ArrayList<>(); + + @Override + public void invoke(T t, AnalysisContext analysisContext) { + rows.add(t); + } + + @Override + public void doAfterAllAnalysed(AnalysisContext context) { + logger.info("解析完成!读取{}行", rows.size()); + } + + public List getRows() { + return rows; + } + +} diff --git a/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/utils/poi/ExcelHandlerAdapter.java b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/utils/poi/ExcelHandlerAdapter.java new file mode 100644 index 0000000..f238c7f --- /dev/null +++ b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/utils/poi/ExcelHandlerAdapter.java @@ -0,0 +1,24 @@ +package com.aiotagro.common.core.utils.poi; + +import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.ss.usermodel.Workbook; + +/** + * Excel数据格式处理适配器 + * + * @author Carson + */ +public interface ExcelHandlerAdapter +{ + /** + * 格式化 + * + * @param value 单元格数据值 + * @param args excel注解args参数组 + * @param cell 单元格对象 + * @param wb 工作簿对象 + * + * @return 处理后的值 + */ + Object format(Object value, String[] args, Cell cell, Workbook wb); +} diff --git a/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/utils/poi/ExcelUtil.java b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/utils/poi/ExcelUtil.java new file mode 100644 index 0000000..f375ed6 --- /dev/null +++ b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/utils/poi/ExcelUtil.java @@ -0,0 +1,1475 @@ +package com.aiotagro.common.core.utils.poi; + +import com.aiotagro.common.core.annotation.Excel; +import com.aiotagro.common.core.annotation.Excel.ColumnType; +import com.aiotagro.common.core.annotation.Excel.Type; +import com.aiotagro.common.core.annotation.Excels; +import com.aiotagro.common.core.exception.UtilException; +import com.aiotagro.common.core.text.Convert; +import com.aiotagro.common.core.utils.DateUtils; +import com.aiotagro.common.core.utils.StringUtils; +import com.aiotagro.common.core.utils.file.FileTypeUtils; +import com.aiotagro.common.core.utils.file.ImageUtils; +import com.aiotagro.common.core.utils.reflect.ReflectUtils; +import org.apache.commons.lang3.ArrayUtils; +import org.apache.commons.lang3.RegExUtils; +import org.apache.commons.lang3.reflect.FieldUtils; +import org.apache.poi.ss.usermodel.*; +import org.apache.poi.ss.util.CellRangeAddress; +import org.apache.poi.ss.util.CellRangeAddressList; +import org.apache.poi.util.IOUtils; +import org.apache.poi.xssf.streaming.SXSSFWorkbook; +import org.apache.poi.xssf.usermodel.XSSFClientAnchor; +import org.apache.poi.xssf.usermodel.XSSFDataValidation; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.io.InputStream; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.ParameterizedType; +import java.math.BigDecimal; +import java.text.DecimalFormat; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.*; +import java.util.stream.Collectors; + +/** + * Excel相关处理 + * + * @author Carson + */ +public class ExcelUtil +{ + private static final Logger log = LoggerFactory.getLogger(ExcelUtil.class); + + public static final String FORMULA_REGEX_STR = "=|-|\\+|@"; + + public static final String[] FORMULA_STR = { "=", "-", "+", "@" }; + + /** + * Excel sheet最大行数,默认65536 + */ + public static final int sheetSize = 65536; + + /** + * 工作表名称 + */ + private String sheetName; + + /** + * 导出类型(EXPORT:导出数据;IMPORT:导入模板) + */ + private Type type; + + /** + * 工作薄对象 + */ + private Workbook wb; + + /** + * 工作表对象 + */ + private Sheet sheet; + + /** + * 样式列表 + */ + private Map styles; + + /** + * 导入导出数据列表 + */ + private List list; + + /** + * 注解列表 + */ + private List fields; + + /** + * 当前行号 + */ + private int rownum; + + /** + * 标题 + */ + private String title; + + /** + * 最大高度 + */ + private short maxHeight; + + /** + * 合并后最后行数 + */ + private int subMergedLastRowNum = 0; + + /** + * 合并后开始行数 + */ + private int subMergedFirstRowNum = 1; + + /** + * 对象的子列表方法 + */ + private Method subMethod; + + /** + * 对象的子列表属性 + */ + private List subFields; + + /** + * 统计列表 + */ + private Map statistics = new HashMap(); + + /** + * 数字格式 + */ + private static final DecimalFormat DOUBLE_FORMAT = new DecimalFormat("######0.00"); + + /** + * 实体对象 + */ + public Class clazz; + + /** + * 需要排除列属性 + */ + public String[] excludeFields; + + public ExcelUtil(Class clazz) + { + this.clazz = clazz; + } + + /** + * 隐藏Excel中列属性 + * + * @param fields 列属性名 示例[单个"name"/多个"id","name"] + * @throws Exception + */ + public void hideColumn(String... fields) + { + this.excludeFields = fields; + } + + public void init(List list, String sheetName, String title, Type type) + { + if (list == null) + { + list = new ArrayList(); + } + this.list = list; + this.sheetName = sheetName; + this.type = type; + this.title = title; + createExcelField(); + createWorkbook(); + createTitle(); + createSubHead(); + } + + /** + * 创建excel第一行标题 + */ + public void createTitle() + { + if (StringUtils.isNotEmpty(title)) + { + subMergedFirstRowNum++; + subMergedLastRowNum++; + int titleLastCol = this.fields.size() - 1; + if (isSubList()) + { + titleLastCol = titleLastCol + subFields.size() - 1; + } + Row titleRow = sheet.createRow(rownum == 0 ? rownum++ : 0); + titleRow.setHeightInPoints(30); + Cell titleCell = titleRow.createCell(0); + titleCell.setCellStyle(styles.get("title")); + titleCell.setCellValue(title); + sheet.addMergedRegion(new CellRangeAddress(titleRow.getRowNum(), titleRow.getRowNum(), titleRow.getRowNum(), titleLastCol)); + } + } + + /** + * 创建对象的子列表名称 + */ + public void createSubHead() + { + if (isSubList()) + { + subMergedFirstRowNum++; + subMergedLastRowNum++; + Row subRow = sheet.createRow(rownum); + int excelNum = 0; + for (Object[] objects : fields) + { + Excel attr = (Excel) objects[1]; + Cell headCell1 = subRow.createCell(excelNum); + headCell1.setCellValue(attr.name()); + headCell1.setCellStyle(styles.get(StringUtils.format("header_{}_{}", attr.headerColor(), attr.headerBackgroundColor()))); + excelNum++; + } + int headFirstRow = excelNum - 1; + int headLastRow = headFirstRow + subFields.size() - 1; + if (headLastRow > headFirstRow) + { + sheet.addMergedRegion(new CellRangeAddress(rownum, rownum, headFirstRow, headLastRow)); + } + rownum++; + } + } + + /** + * 对excel表单默认第一个索引名转换成list + * + * @param is 输入流 + * @return 转换后集合 + */ + public List importExcel(InputStream is) + { + List list = null; + try + { + list = importExcel(is, 0); + } + catch (Exception e) + { + log.error("导入Excel异常{}", e.getMessage()); + throw new UtilException(e.getMessage()); + } + finally + { + IOUtils.closeQuietly(is); + } + return list; + } + + /** + * 对excel表单默认第一个索引名转换成list + * + * @param is 输入流 + * @param titleNum 标题占用行数 + * @return 转换后集合 + */ + public List importExcel(InputStream is, int titleNum) throws Exception + { + return importExcel(StringUtils.EMPTY, is, titleNum); + } + + /** + * 对excel表单指定表格索引名转换成list + * + * @param sheetName 表格索引名 + * @param titleNum 标题占用行数 + * @param is 输入流 + * @return 转换后集合 + */ + public List importExcel(String sheetName, InputStream is, int titleNum) throws Exception + { + this.type = Type.IMPORT; + this.wb = WorkbookFactory.create(is); + List list = new ArrayList(); + // 如果指定sheet名,则取指定sheet中的内容 否则默认指向第1个sheet + Sheet sheet = StringUtils.isNotEmpty(sheetName) ? wb.getSheet(sheetName) : wb.getSheetAt(0); + if (sheet == null) + { + throw new IOException("文件sheet不存在"); + } + + // 获取最后一个非空行的行下标,比如总行数为n,则返回的为n-1 + int rows = sheet.getLastRowNum(); + if (rows > 0) + { + // 定义一个map用于存放excel列的序号和field. + Map cellMap = new HashMap(); + // 获取表头 + Row heard = sheet.getRow(titleNum); + for (int i = 0; i < heard.getPhysicalNumberOfCells(); i++) + { + Cell cell = heard.getCell(i); + if (StringUtils.isNotNull(cell)) + { + String value = this.getCellValue(heard, i).toString(); + cellMap.put(value, i); + } + else + { + cellMap.put(null, i); + } + } + // 有数据时才处理 得到类的所有field. + List fields = this.getFields(); + Map fieldsMap = new HashMap(); + for (Object[] objects : fields) + { + Excel attr = (Excel) objects[1]; + Integer column = cellMap.get(attr.name()); + if (column != null) + { + fieldsMap.put(column, objects); + } + } + for (int i = titleNum + 1; i <= rows; i++) + { + // 从第2行开始取数据,默认第一行是表头. + Row row = sheet.getRow(i); + // 判断当前行是否是空行 + if (isRowEmpty(row)) + { + continue; + } + T entity = null; + for (Map.Entry entry : fieldsMap.entrySet()) + { + Object val = this.getCellValue(row, entry.getKey()); + + // 如果不存在实例则新建. + entity = (entity == null ? clazz.newInstance() : entity); + // 从map中得到对应列的field. + Field field = (Field) entry.getValue()[0]; + Excel attr = (Excel) entry.getValue()[1]; + // 取得类型,并根据对象类型设置值. + Class fieldType = field.getType(); + if (String.class == fieldType) + { + String s = Convert.toStr(val); + if (StringUtils.endsWith(s, ".0")) + { + val = StringUtils.substringBefore(s, ".0"); + } + else + { + String dateFormat = field.getAnnotation(Excel.class).dateFormat(); + if (StringUtils.isNotEmpty(dateFormat)) + { + val = parseDateToStr(dateFormat, val); + } + else + { + val = Convert.toStr(val); + } + } + } + else if ((Integer.TYPE == fieldType || Integer.class == fieldType) && StringUtils.isNumeric(Convert.toStr(val))) + { + val = Convert.toInt(val); + } + else if ((Long.TYPE == fieldType || Long.class == fieldType) && StringUtils.isNumeric(Convert.toStr(val))) + { + val = Convert.toLong(val); + } + else if (Double.TYPE == fieldType || Double.class == fieldType) + { + val = Convert.toDouble(val); + } + else if (Float.TYPE == fieldType || Float.class == fieldType) + { + val = Convert.toFloat(val); + } + else if (BigDecimal.class == fieldType) + { + val = Convert.toBigDecimal(val); + } + else if (Date.class == fieldType) + { + if (val instanceof String) + { + val = DateUtils.parseDate(val); + } + else if (val instanceof Double) + { + val = DateUtil.getJavaDate((Double) val); + } + } + else if (Boolean.TYPE == fieldType || Boolean.class == fieldType) + { + val = Convert.toBool(val, false); + } + if (StringUtils.isNotNull(fieldType)) + { + String propertyName = field.getName(); + if (StringUtils.isNotEmpty(attr.targetAttr())) + { + propertyName = field.getName() + "." + attr.targetAttr(); + } + if (StringUtils.isNotEmpty(attr.readConverterExp())) + { + val = reverseByExp(Convert.toStr(val), attr.readConverterExp(), attr.separator()); + } + else if (!attr.handler().equals(ExcelHandlerAdapter.class)) + { + val = dataFormatHandlerAdapter(val, attr, null); + } + ReflectUtils.invokeSetter(entity, propertyName, val); + } + } + list.add(entity); + } + } + return list; + } + + /** + * 对list数据源将其里面的数据导入到excel表单 + * + * @param response 返回数据 + * @param list 导出数据集合 + * @param sheetName 工作表的名称 + * @return 结果 + */ + public void exportExcel(HttpServletResponse response, List list, String sheetName) + { + exportExcel(response, list, sheetName, StringUtils.EMPTY); + } + + /** + * 对list数据源将其里面的数据导入到excel表单 + * + * @param response 返回数据 + * @param list 导出数据集合 + * @param sheetName 工作表的名称 + * @param title 标题 + * @return 结果 + */ + public void exportExcel(HttpServletResponse response, List list, String sheetName, String title) + { + response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); + response.setCharacterEncoding("utf-8"); + this.init(list, sheetName, title, Type.EXPORT); + exportExcel(response); + } + + /** + * 对list数据源将其里面的数据导入到excel表单 + * + * @param sheetName 工作表的名称 + * @return 结果 + */ + public void importTemplateExcel(HttpServletResponse response, String sheetName) + { + importTemplateExcel(response, sheetName, StringUtils.EMPTY); + } + + /** + * 对list数据源将其里面的数据导入到excel表单 + * + * @param sheetName 工作表的名称 + * @param title 标题 + * @return 结果 + */ + public void importTemplateExcel(HttpServletResponse response, String sheetName, String title) + { + response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); + response.setCharacterEncoding("utf-8"); + this.init(null, sheetName, title, Type.IMPORT); + exportExcel(response); + } + + /** + * 对list数据源将其里面的数据导入到excel表单 + * + * @return 结果 + */ + public void exportExcel(HttpServletResponse response) + { + try + { + writeSheet(); + wb.write(response.getOutputStream()); + } + catch (Exception e) + { + log.error("导出Excel异常{}", e.getMessage()); + } + finally + { + IOUtils.closeQuietly(wb); + } + } + + /** + * 创建写入数据到Sheet + */ + public void writeSheet() + { + // 取出一共有多少个sheet. + int sheetNo = Math.max(1, (int) Math.ceil(list.size() * 1.0 / sheetSize)); + for (int index = 0; index < sheetNo; index++) + { + createSheet(sheetNo, index); + + // 产生一行 + Row row = sheet.createRow(rownum); + int column = 0; + // 写入各个字段的列头名称 + for (Object[] os : fields) + { + Field field = (Field) os[0]; + Excel excel = (Excel) os[1]; + if (Collection.class.isAssignableFrom(field.getType())) + { + for (Field subField : subFields) + { + Excel subExcel = subField.getAnnotation(Excel.class); + this.createHeadCell(subExcel, row, column++); + } + } + else + { + this.createHeadCell(excel, row, column++); + } + } + if (Type.EXPORT.equals(type)) + { + fillExcelData(index, row); + addStatisticsRow(); + } + } + } + + /** + * 填充excel数据 + * + * @param index 序号 + * @param row 单元格行 + */ + @SuppressWarnings("unchecked") + public void fillExcelData(int index, Row row) + { + int startNo = index * sheetSize; + int endNo = Math.min(startNo + sheetSize, list.size()); + int rowNo = (1 + rownum) - startNo; + for (int i = startNo; i < endNo; i++) + { + rowNo = isSubList() ? (i > 1 ? rowNo + 1 : rowNo + i) : i + 1 + rownum - startNo; + row = sheet.createRow(rowNo); + // 得到导出对象. + T vo = (T) list.get(i); + Collection subList = null; + if (isSubList()) + { + if (isSubListValue(vo)) + { + subList = getListCellValue(vo); + subMergedLastRowNum = subMergedLastRowNum + subList.size(); + } + else + { + subMergedFirstRowNum++; + subMergedLastRowNum++; + } + } + int column = 0; + for (Object[] os : fields) + { + Field field = (Field) os[0]; + Excel excel = (Excel) os[1]; + if (Collection.class.isAssignableFrom(field.getType()) && StringUtils.isNotNull(subList)) + { + boolean subFirst = false; + for (Object obj : subList) + { + if (subFirst) + { + rowNo++; + row = sheet.createRow(rowNo); + } + List subFields = FieldUtils.getFieldsListWithAnnotation(obj.getClass(), Excel.class); + int subIndex = 0; + for (Field subField : subFields) + { + if (subField.isAnnotationPresent(Excel.class)) + { + subField.setAccessible(true); + Excel attr = subField.getAnnotation(Excel.class); + this.addCell(attr, row, (T) obj, subField, column + subIndex); + } + subIndex++; + } + subFirst = true; + } + this.subMergedFirstRowNum = this.subMergedFirstRowNum + subList.size(); + } + else + { + this.addCell(excel, row, vo, field, column++); + } + } + } + } + + /** + * 创建表格样式 + * + * @param wb 工作薄对象 + * @return 样式列表 + */ + private Map createStyles(Workbook wb) + { + // 写入各条记录,每条记录对应excel表中的一行 + Map styles = new HashMap(); + CellStyle style = wb.createCellStyle(); + style.setAlignment(HorizontalAlignment.CENTER); + style.setVerticalAlignment(VerticalAlignment.CENTER); + Font titleFont = wb.createFont(); + titleFont.setFontName("Arial"); + titleFont.setFontHeightInPoints((short) 16); + titleFont.setBold(true); + style.setFont(titleFont); + styles.put("title", style); + + style = wb.createCellStyle(); + style.setAlignment(HorizontalAlignment.CENTER); + style.setVerticalAlignment(VerticalAlignment.CENTER); + style.setBorderRight(BorderStyle.THIN); + style.setRightBorderColor(IndexedColors.GREY_50_PERCENT.getIndex()); + style.setBorderLeft(BorderStyle.THIN); + style.setLeftBorderColor(IndexedColors.GREY_50_PERCENT.getIndex()); + style.setBorderTop(BorderStyle.THIN); + style.setTopBorderColor(IndexedColors.GREY_50_PERCENT.getIndex()); + style.setBorderBottom(BorderStyle.THIN); + style.setBottomBorderColor(IndexedColors.GREY_50_PERCENT.getIndex()); + Font dataFont = wb.createFont(); + dataFont.setFontName("Arial"); + dataFont.setFontHeightInPoints((short) 10); + style.setFont(dataFont); + styles.put("data", style); + + style = wb.createCellStyle(); + style.setAlignment(HorizontalAlignment.CENTER); + style.setVerticalAlignment(VerticalAlignment.CENTER); + Font totalFont = wb.createFont(); + totalFont.setFontName("Arial"); + totalFont.setFontHeightInPoints((short) 10); + style.setFont(totalFont); + styles.put("total", style); + + styles.putAll(annotationHeaderStyles(wb, styles)); + + styles.putAll(annotationDataStyles(wb)); + + return styles; + } + + /** + * 根据Excel注解创建表格头样式 + * + * @param wb 工作薄对象 + * @return 自定义样式列表 + */ + private Map annotationHeaderStyles(Workbook wb, Map styles) + { + Map headerStyles = new HashMap(); + for (Object[] os : fields) + { + Excel excel = (Excel) os[1]; + String key = StringUtils.format("header_{}_{}", excel.headerColor(), excel.headerBackgroundColor()); + if (!headerStyles.containsKey(key)) + { + CellStyle style = wb.createCellStyle(); + style.cloneStyleFrom(styles.get("data")); + style.setAlignment(HorizontalAlignment.CENTER); + style.setVerticalAlignment(VerticalAlignment.CENTER); + style.setFillForegroundColor(excel.headerBackgroundColor().index); + style.setFillPattern(FillPatternType.SOLID_FOREGROUND); + Font headerFont = wb.createFont(); + headerFont.setFontName("Arial"); + headerFont.setFontHeightInPoints((short) 10); + headerFont.setBold(true); + headerFont.setColor(excel.headerColor().index); + style.setFont(headerFont); + headerStyles.put(key, style); + } + } + return headerStyles; + } + + /** + * 根据Excel注解创建表格列样式 + * + * @param wb 工作薄对象 + * @return 自定义样式列表 + */ + private Map annotationDataStyles(Workbook wb) + { + Map styles = new HashMap(); + for (Object[] os : fields) + { + Excel excel = (Excel) os[1]; + String key = StringUtils.format("data_{}_{}_{}", excel.align(), excel.color(), excel.backgroundColor()); + if (!styles.containsKey(key)) + { + CellStyle style = wb.createCellStyle(); + style.setAlignment(excel.align()); + style.setVerticalAlignment(VerticalAlignment.CENTER); + style.setBorderRight(BorderStyle.THIN); + style.setRightBorderColor(IndexedColors.GREY_50_PERCENT.getIndex()); + style.setBorderLeft(BorderStyle.THIN); + style.setLeftBorderColor(IndexedColors.GREY_50_PERCENT.getIndex()); + style.setBorderTop(BorderStyle.THIN); + style.setTopBorderColor(IndexedColors.GREY_50_PERCENT.getIndex()); + style.setBorderBottom(BorderStyle.THIN); + style.setBottomBorderColor(IndexedColors.GREY_50_PERCENT.getIndex()); + style.setFillPattern(FillPatternType.SOLID_FOREGROUND); + style.setFillForegroundColor(excel.backgroundColor().getIndex()); + Font dataFont = wb.createFont(); + dataFont.setFontName("Arial"); + dataFont.setFontHeightInPoints((short) 10); + dataFont.setColor(excel.color().index); + style.setFont(dataFont); + styles.put(key, style); + } + } + return styles; + } + + /** + * 创建单元格 + */ + public Cell createHeadCell(Excel attr, Row row, int column) + { + // 创建列 + Cell cell = row.createCell(column); + // 写入列信息 + cell.setCellValue(attr.name()); + setDataValidation(attr, row, column); + cell.setCellStyle(styles.get(StringUtils.format("header_{}_{}", attr.headerColor(), attr.headerBackgroundColor()))); + if (isSubList()) + { + // 填充默认样式,防止合并单元格样式失效 + sheet.setDefaultColumnStyle(column, styles.get(StringUtils.format("data_{}_{}_{}", attr.align(), attr.color(), attr.backgroundColor()))); + if (attr.needMerge()) + { + sheet.addMergedRegion(new CellRangeAddress(rownum - 1, rownum, column, column)); + } + } + return cell; + } + + /** + * 设置单元格信息 + * + * @param value 单元格值 + * @param attr 注解相关 + * @param cell 单元格信息 + */ + public void setCellVo(Object value, Excel attr, Cell cell) + { + if (ColumnType.STRING == attr.cellType()) + { + String cellValue = Convert.toStr(value); + // 对于任何以表达式触发字符 =-+@开头的单元格,直接使用tab字符作为前缀,防止CSV注入。 + if (StringUtils.startsWithAny(cellValue, FORMULA_STR)) + { + cellValue = RegExUtils.replaceFirst(cellValue, FORMULA_REGEX_STR, "\t$0"); + } + if (value instanceof Collection && StringUtils.equals("[]", cellValue)) + { + cellValue = StringUtils.EMPTY; + } + cell.setCellValue(StringUtils.isNull(cellValue) ? attr.defaultValue() : cellValue + attr.suffix()); + } + else if (ColumnType.NUMERIC == attr.cellType()) + { + if (StringUtils.isNotNull(value)) + { + cell.setCellValue(StringUtils.contains(Convert.toStr(value), ".") ? Convert.toDouble(value) : Convert.toInt(value)); + } + } + else if (ColumnType.IMAGE == attr.cellType()) + { + ClientAnchor anchor = new XSSFClientAnchor(0, 0, 0, 0, (short) cell.getColumnIndex(), cell.getRow().getRowNum(), (short) (cell.getColumnIndex() + 1), cell.getRow().getRowNum() + 1); + String imagePath = Convert.toStr(value); + if (StringUtils.isNotEmpty(imagePath)) + { + byte[] data = ImageUtils.getImage(imagePath); + getDrawingPatriarch(cell.getSheet()).createPicture(anchor, + cell.getSheet().getWorkbook().addPicture(data, getImageType(data))); + } + } + } + + /** + * 获取画布 + */ + public static Drawing getDrawingPatriarch(Sheet sheet) + { + if (sheet.getDrawingPatriarch() == null) + { + sheet.createDrawingPatriarch(); + } + return sheet.getDrawingPatriarch(); + } + + /** + * 获取图片类型,设置图片插入类型 + */ + public int getImageType(byte[] value) + { + String type = FileTypeUtils.getFileExtendName(value); + if ("JPG".equalsIgnoreCase(type)) + { + return Workbook.PICTURE_TYPE_JPEG; + } + else if ("PNG".equalsIgnoreCase(type)) + { + return Workbook.PICTURE_TYPE_PNG; + } + return Workbook.PICTURE_TYPE_JPEG; + } + + /** + * 创建表格样式 + */ + public void setDataValidation(Excel attr, Row row, int column) + { + if (attr.name().indexOf("注:") >= 0) + { + sheet.setColumnWidth(column, 6000); + } + else + { + // 设置列宽 + sheet.setColumnWidth(column, (int) ((attr.width() + 0.72) * 256)); + } + if (StringUtils.isNotEmpty(attr.prompt()) || attr.combo().length > 0) + { + if (attr.combo().length > 15 || StringUtils.join(attr.combo()).length() > 255) + { + // 如果下拉数大于15或字符串长度大于255,则使用一个新sheet存储,避免生成的模板下拉值获取不到 + setXSSFValidationWithHidden(sheet, attr.combo(), attr.prompt(), 1, 100, column, column); + } + else + { + // 提示信息或只能选择不能输入的列内容. + setPromptOrValidation(sheet, attr.combo(), attr.prompt(), 1, 100, column, column); + } + } + } + + /** + * 添加单元格 + */ + public Cell addCell(Excel attr, Row row, T vo, Field field, int column) + { + Cell cell = null; + try + { + // 设置行高 + row.setHeight(maxHeight); + // 根据Excel中设置情况决定是否导出,有些情况需要保持为空,希望用户填写这一列. + if (attr.isExport()) + { + // 创建cell + cell = row.createCell(column); + if (isSubListValue(vo) && getListCellValue(vo).size() > 1 && attr.needMerge()) + { + CellRangeAddress cellAddress = new CellRangeAddress(subMergedFirstRowNum, subMergedLastRowNum, column, column); + sheet.addMergedRegion(cellAddress); + } + cell.setCellStyle(styles.get(StringUtils.format("data_{}_{}_{}", attr.align(), attr.color(), attr.backgroundColor()))); + + // 用于读取对象中的属性 + Object value = getTargetValue(vo, field, attr); + String dateFormat = attr.dateFormat(); + String readConverterExp = attr.readConverterExp(); + String separator = attr.separator(); + if (StringUtils.isNotEmpty(dateFormat) && StringUtils.isNotNull(value)) + { + cell.setCellValue(parseDateToStr(dateFormat, value)); + } + else if (StringUtils.isNotEmpty(readConverterExp) && StringUtils.isNotNull(value)) + { + cell.setCellValue(convertByExp(Convert.toStr(value), readConverterExp, separator)); + } + else if (value instanceof BigDecimal && -1 != attr.scale()) + { + cell.setCellValue((((BigDecimal) value).setScale(attr.scale(), attr.roundingMode())).doubleValue()); + } + else if (!attr.handler().equals(ExcelHandlerAdapter.class)) + { + cell.setCellValue(dataFormatHandlerAdapter(value, attr, cell)); + } + else + { + // 设置列类型 + setCellVo(value, attr, cell); + } + addStatisticsData(column, Convert.toStr(value), attr); + } + } + catch (Exception e) + { + log.error("导出Excel失败{}", e); + } + return cell; + } + + /** + * 设置 POI XSSFSheet 单元格提示或选择框 + * + * @param sheet 表单 + * @param textlist 下拉框显示的内容 + * @param promptContent 提示内容 + * @param firstRow 开始行 + * @param endRow 结束行 + * @param firstCol 开始列 + * @param endCol 结束列 + */ + public void setPromptOrValidation(Sheet sheet, String[] textlist, String promptContent, int firstRow, int endRow, + int firstCol, int endCol) + { + DataValidationHelper helper = sheet.getDataValidationHelper(); + DataValidationConstraint constraint = textlist.length > 0 ? helper.createExplicitListConstraint(textlist) : helper.createCustomConstraint("DD1"); + CellRangeAddressList regions = new CellRangeAddressList(firstRow, endRow, firstCol, endCol); + DataValidation dataValidation = helper.createValidation(constraint, regions); + if (StringUtils.isNotEmpty(promptContent)) + { + // 如果设置了提示信息则鼠标放上去提示 + dataValidation.createPromptBox("", promptContent); + dataValidation.setShowPromptBox(true); + } + // 处理Excel兼容性问题 + if (dataValidation instanceof XSSFDataValidation) + { + dataValidation.setSuppressDropDownArrow(true); + dataValidation.setShowErrorBox(true); + } + else + { + dataValidation.setSuppressDropDownArrow(false); + } + sheet.addValidationData(dataValidation); + } + + /** + * 设置某些列的值只能输入预制的数据,显示下拉框(兼容超出一定数量的下拉框). + * + * @param sheet 要设置的sheet. + * @param textlist 下拉框显示的内容 + * @param promptContent 提示内容 + * @param firstRow 开始行 + * @param endRow 结束行 + * @param firstCol 开始列 + * @param endCol 结束列 + */ + public void setXSSFValidationWithHidden(Sheet sheet, String[] textlist, String promptContent, int firstRow, int endRow, int firstCol, int endCol) + { + String hideSheetName = "combo_" + firstCol + "_" + endCol; + Sheet hideSheet = wb.createSheet(hideSheetName); // 用于存储 下拉菜单数据 + for (int i = 0; i < textlist.length; i++) + { + hideSheet.createRow(i).createCell(0).setCellValue(textlist[i]); + } + // 创建名称,可被其他单元格引用 + Name name = wb.createName(); + name.setNameName(hideSheetName + "_data"); + name.setRefersToFormula(hideSheetName + "!$A$1:$A$" + textlist.length); + DataValidationHelper helper = sheet.getDataValidationHelper(); + // 加载下拉列表内容 + DataValidationConstraint constraint = helper.createFormulaListConstraint(hideSheetName + "_data"); + // 设置数据有效性加载在哪个单元格上,四个参数分别是:起始行、终止行、起始列、终止列 + CellRangeAddressList regions = new CellRangeAddressList(firstRow, endRow, firstCol, endCol); + // 数据有效性对象 + DataValidation dataValidation = helper.createValidation(constraint, regions); + if (StringUtils.isNotEmpty(promptContent)) + { + // 如果设置了提示信息则鼠标放上去提示 + dataValidation.createPromptBox("", promptContent); + dataValidation.setShowPromptBox(true); + } + // 处理Excel兼容性问题 + if (dataValidation instanceof XSSFDataValidation) + { + dataValidation.setSuppressDropDownArrow(true); + dataValidation.setShowErrorBox(true); + } + else + { + dataValidation.setSuppressDropDownArrow(false); + } + + sheet.addValidationData(dataValidation); + // 设置hiddenSheet隐藏 + wb.setSheetHidden(wb.getSheetIndex(hideSheet), true); + } + + /** + * 解析导出值 0=男,1=女,2=未知 + * + * @param propertyValue 参数值 + * @param converterExp 翻译注解 + * @param separator 分隔符 + * @return 解析后值 + */ + public static String convertByExp(String propertyValue, String converterExp, String separator) + { + StringBuilder propertyString = new StringBuilder(); + String[] convertSource = converterExp.split(","); + for (String item : convertSource) + { + String[] itemArray = item.split("="); + if (StringUtils.containsAny(propertyValue, separator)) + { + for (String value : propertyValue.split(separator)) + { + if (itemArray[0].equals(value)) + { + propertyString.append(itemArray[1] + separator); + break; + } + } + } + else + { + if (itemArray[0].equals(propertyValue)) + { + return itemArray[1]; + } + } + } + return StringUtils.stripEnd(propertyString.toString(), separator); + } + + /** + * 反向解析值 男=0,女=1,未知=2 + * + * @param propertyValue 参数值 + * @param converterExp 翻译注解 + * @param separator 分隔符 + * @return 解析后值 + */ + public static String reverseByExp(String propertyValue, String converterExp, String separator) + { + StringBuilder propertyString = new StringBuilder(); + String[] convertSource = converterExp.split(","); + for (String item : convertSource) + { + String[] itemArray = item.split("="); + if (StringUtils.containsAny(propertyValue, separator)) + { + for (String value : propertyValue.split(separator)) + { + if (itemArray[1].equals(value)) + { + propertyString.append(itemArray[0] + separator); + break; + } + } + } + else + { + if (itemArray[1].equals(propertyValue)) + { + return itemArray[0]; + } + } + } + return StringUtils.stripEnd(propertyString.toString(), separator); + } + + /** + * 数据处理器 + * + * @param value 数据值 + * @param excel 数据注解 + * @return + */ + public String dataFormatHandlerAdapter(Object value, Excel excel, Cell cell) + { + try + { + Object instance = excel.handler().newInstance(); + Method formatMethod = excel.handler().getMethod("format", new Class[] { Object.class, String[].class, Cell.class, Workbook.class }); + value = formatMethod.invoke(instance, value, excel.args(), cell, this.wb); + } + catch (Exception e) + { + log.error("不能格式化数据 " + excel.handler(), e.getMessage()); + } + return Convert.toStr(value); + } + + /** + * 合计统计信息 + */ + private void addStatisticsData(Integer index, String text, Excel entity) + { + if (entity != null && entity.isStatistics()) + { + Double temp = 0D; + if (!statistics.containsKey(index)) + { + statistics.put(index, temp); + } + try + { + temp = Double.valueOf(text); + } + catch (NumberFormatException e) + { + } + statistics.put(index, statistics.get(index) + temp); + } + } + + /** + * 创建统计行 + */ + public void addStatisticsRow() + { + if (statistics.size() > 0) + { + Row row = sheet.createRow(sheet.getLastRowNum() + 1); + Set keys = statistics.keySet(); + Cell cell = row.createCell(0); + cell.setCellStyle(styles.get("total")); + cell.setCellValue("合计"); + + for (Integer key : keys) + { + cell = row.createCell(key); + cell.setCellStyle(styles.get("total")); + cell.setCellValue(DOUBLE_FORMAT.format(statistics.get(key))); + } + statistics.clear(); + } + } + + /** + * 获取bean中的属性值 + * + * @param vo 实体对象 + * @param field 字段 + * @param excel 注解 + * @return 最终的属性值 + * @throws Exception + */ + private Object getTargetValue(T vo, Field field, Excel excel) throws Exception + { + Object o = field.get(vo); + if (StringUtils.isNotEmpty(excel.targetAttr())) + { + String target = excel.targetAttr(); + if (target.contains(".")) + { + String[] targets = target.split("[.]"); + for (String name : targets) + { + o = getValue(o, name); + } + } + else + { + o = getValue(o, target); + } + } + return o; + } + + /** + * 以类的属性的get方法方法形式获取值 + * + * @param o + * @param name + * @return value + * @throws Exception + */ + private Object getValue(Object o, String name) throws Exception + { + if (StringUtils.isNotNull(o) && StringUtils.isNotEmpty(name)) + { + Class clazz = o.getClass(); + Field field = clazz.getDeclaredField(name); + field.setAccessible(true); + o = field.get(o); + } + return o; + } + + /** + * 得到所有定义字段 + */ + private void createExcelField() + { + this.fields = getFields(); + this.fields = this.fields.stream().sorted(Comparator.comparing(objects -> ((Excel) objects[1]).sort())).collect(Collectors.toList()); + this.maxHeight = getRowHeight(); + } + + /** + * 获取字段注解信息 + */ + public List getFields() + { + List fields = new ArrayList(); + List tempFields = new ArrayList<>(); + tempFields.addAll(Arrays.asList(clazz.getSuperclass().getDeclaredFields())); + tempFields.addAll(Arrays.asList(clazz.getDeclaredFields())); + for (Field field : tempFields) + { + if (!ArrayUtils.contains(this.excludeFields, field.getName())) + { + // 单注解 + if (field.isAnnotationPresent(Excel.class)) + { + Excel attr = field.getAnnotation(Excel.class); + if (attr != null && (attr.type() == Type.ALL || attr.type() == type)) + { + field.setAccessible(true); + fields.add(new Object[] { field, attr }); + } + if (Collection.class.isAssignableFrom(field.getType())) + { + subMethod = getSubMethod(field.getName(), clazz); + ParameterizedType pt = (ParameterizedType) field.getGenericType(); + Class subClass = (Class) pt.getActualTypeArguments()[0]; + this.subFields = FieldUtils.getFieldsListWithAnnotation(subClass, Excel.class); + } + } + + // 多注解 + if (field.isAnnotationPresent(Excels.class)) + { + Excels attrs = field.getAnnotation(Excels.class); + Excel[] excels = attrs.value(); + for (Excel attr : excels) + { + if (!ArrayUtils.contains(this.excludeFields, field.getName() + "." + attr.targetAttr()) + && (attr != null && (attr.type() == Type.ALL || attr.type() == type))) + { + field.setAccessible(true); + fields.add(new Object[] { field, attr }); + } + } + } + } + } + return fields; + } + + /** + * 根据注解获取最大行高 + */ + public short getRowHeight() + { + double maxHeight = 0; + for (Object[] os : this.fields) + { + Excel excel = (Excel) os[1]; + maxHeight = Math.max(maxHeight, excel.height()); + } + return (short) (maxHeight * 20); + } + + /** + * 创建一个工作簿 + */ + public void createWorkbook() + { + this.wb = new SXSSFWorkbook(500); + this.sheet = wb.createSheet(); + wb.setSheetName(0, sheetName); + this.styles = createStyles(wb); + } + + /** + * 创建工作表 + * + * @param sheetNo sheet数量 + * @param index 序号 + */ + public void createSheet(int sheetNo, int index) + { + // 设置工作表的名称. + if (sheetNo > 1 && index > 0) + { + this.sheet = wb.createSheet(); + this.createTitle(); + wb.setSheetName(index, sheetName + index); + } + } + + /** + * 获取单元格值 + * + * @param row 获取的行 + * @param column 获取单元格列号 + * @return 单元格值 + */ + public Object getCellValue(Row row, int column) + { + if (row == null) + { + return row; + } + Object val = ""; + try + { + Cell cell = row.getCell(column); + if (StringUtils.isNotNull(cell)) + { + if (cell.getCellType() == CellType.NUMERIC || cell.getCellType() == CellType.FORMULA) + { + val = cell.getNumericCellValue(); + if (DateUtil.isCellDateFormatted(cell)) + { + val = DateUtil.getJavaDate((Double) val); // POI Excel 日期格式转换 + } + else + { + if ((Double) val % 1 != 0) + { + val = new BigDecimal(val.toString()); + } + else + { + val = new DecimalFormat("0").format(val); + } + } + } + else if (cell.getCellType() == CellType.STRING) + { + val = cell.getStringCellValue(); + } + else if (cell.getCellType() == CellType.BOOLEAN) + { + val = cell.getBooleanCellValue(); + } + else if (cell.getCellType() == CellType.ERROR) + { + val = cell.getErrorCellValue(); + } + + } + } + catch (Exception e) + { + return val; + } + return val; + } + + /** + * 判断是否是空行 + * + * @param row 判断的行 + * @return + */ + private boolean isRowEmpty(Row row) + { + if (row == null) + { + return true; + } + for (int i = row.getFirstCellNum(); i < row.getLastCellNum(); i++) + { + Cell cell = row.getCell(i); + if (cell != null && cell.getCellType() != CellType.BLANK) + { + return false; + } + } + return true; + } + + /** + * 格式化不同类型的日期对象 + * + * @param dateFormat 日期格式 + * @param val 被格式化的日期对象 + * @return 格式化后的日期字符 + */ + public String parseDateToStr(String dateFormat, Object val) + { + if (val == null) + { + return ""; + } + String str; + if (val instanceof Date) + { + str = DateUtils.parseDateToStr(dateFormat, (Date) val); + } + else if (val instanceof LocalDateTime) + { + str = DateUtils.parseDateToStr(dateFormat, DateUtils.toDate((LocalDateTime) val)); + } + else if (val instanceof LocalDate) + { + str = DateUtils.parseDateToStr(dateFormat, DateUtils.toDate((LocalDate) val)); + } + else + { + str = val.toString(); + } + return str; + } + + /** + * 是否有对象的子列表 + */ + public boolean isSubList() + { + return StringUtils.isNotNull(subFields) && subFields.size() > 0; + } + + /** + * 是否有对象的子列表,集合不为空 + */ + public boolean isSubListValue(T vo) + { + return StringUtils.isNotNull(subFields) && subFields.size() > 0 && StringUtils.isNotNull(getListCellValue(vo)) && getListCellValue(vo).size() > 0; + } + + /** + * 获取集合的值 + */ + public Collection getListCellValue(Object obj) + { + Object value; + try + { + value = subMethod.invoke(obj, new Object[] {}); + } + catch (Exception e) + { + return new ArrayList(); + } + return (Collection) value; + } + + /** + * 获取对象的子列表方法 + * + * @param name 名称 + * @param pojoClass 类对象 + * @return 子列表方法 + */ + public Method getSubMethod(String name, Class pojoClass) + { + StringBuffer getMethodName = new StringBuffer("get"); + getMethodName.append(name.substring(0, 1).toUpperCase()); + getMethodName.append(name.substring(1)); + Method method = null; + try + { + method = pojoClass.getMethod(getMethodName.toString(), new Class[] {}); + } + catch (Exception e) + { + log.error("获取对象异常{}", e.getMessage()); + } + return method; + } +} diff --git a/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/utils/reflect/ReflectUtils.java b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/utils/reflect/ReflectUtils.java new file mode 100644 index 0000000..b5b5066 --- /dev/null +++ b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/utils/reflect/ReflectUtils.java @@ -0,0 +1,406 @@ +package com.aiotagro.common.core.utils.reflect; + +import com.aiotagro.common.core.text.Convert; +import com.aiotagro.common.core.utils.DateUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.Validate; +import org.apache.poi.ss.usermodel.DateUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.lang.reflect.*; +import java.util.Date; + +/** + * 反射工具类. 提供调用getter/setter方法, 访问私有变量, 调用私有方法, 获取泛型类型Class, 被AOP过的真实类等工具函数. + * + * @author Carson + */ +@SuppressWarnings("rawtypes") +public class ReflectUtils +{ + private static final String SETTER_PREFIX = "set"; + + private static final String GETTER_PREFIX = "get"; + + private static final String CGLIB_CLASS_SEPARATOR = "$$"; + + private static Logger logger = LoggerFactory.getLogger(ReflectUtils.class); + + /** + * 调用Getter方法. + * 支持多级,如:对象名.对象名.方法 + */ + @SuppressWarnings("unchecked") + public static E invokeGetter(Object obj, String propertyName) + { + Object object = obj; + for (String name : StringUtils.split(propertyName, ".")) + { + String getterMethodName = GETTER_PREFIX + StringUtils.capitalize(name); + object = invokeMethod(object, getterMethodName, new Class[] {}, new Object[] {}); + } + return (E) object; + } + + /** + * 调用Setter方法, 仅匹配方法名。 + * 支持多级,如:对象名.对象名.方法 + */ + public static void invokeSetter(Object obj, String propertyName, E value) + { + Object object = obj; + String[] names = StringUtils.split(propertyName, "."); + for (int i = 0; i < names.length; i++) + { + if (i < names.length - 1) + { + String getterMethodName = GETTER_PREFIX + StringUtils.capitalize(names[i]); + object = invokeMethod(object, getterMethodName, new Class[] {}, new Object[] {}); + } + else + { + String setterMethodName = SETTER_PREFIX + StringUtils.capitalize(names[i]); + invokeMethodByName(object, setterMethodName, new Object[] { value }); + } + } + } + + /** + * 直接读取对象属性值, 无视private/protected修饰符, 不经过getter函数. + */ + @SuppressWarnings("unchecked") + public static E getFieldValue(final Object obj, final String fieldName) + { + Field field = getAccessibleField(obj, fieldName); + if (field == null) + { + logger.debug("在 [" + obj.getClass() + "] 中,没有找到 [" + fieldName + "] 字段 "); + return null; + } + E result = null; + try + { + result = (E) field.get(obj); + } + catch (IllegalAccessException e) + { + logger.error("不可能抛出的异常{}", e.getMessage()); + } + return result; + } + + /** + * 直接设置对象属性值, 无视private/protected修饰符, 不经过setter函数. + */ + public static void setFieldValue(final Object obj, final String fieldName, final E value) + { + Field field = getAccessibleField(obj, fieldName); + if (field == null) + { + // throw new IllegalArgumentException("在 [" + obj.getClass() + "] 中,没有找到 [" + fieldName + "] 字段 "); + logger.debug("在 [" + obj.getClass() + "] 中,没有找到 [" + fieldName + "] 字段 "); + return; + } + try + { + field.set(obj, value); + } + catch (IllegalAccessException e) + { + logger.error("不可能抛出的异常: {}", e.getMessage()); + } + } + + /** + * 直接调用对象方法, 无视private/protected修饰符. + * 用于一次性调用的情况,否则应使用getAccessibleMethod()函数获得Method后反复调用. + * 同时匹配方法名+参数类型, + */ + @SuppressWarnings("unchecked") + public static E invokeMethod(final Object obj, final String methodName, final Class[] parameterTypes, + final Object[] args) + { + if (obj == null || methodName == null) + { + return null; + } + Method method = getAccessibleMethod(obj, methodName, parameterTypes); + if (method == null) + { + logger.debug("在 [" + obj.getClass() + "] 中,没有找到 [" + methodName + "] 方法 "); + return null; + } + try + { + return (E) method.invoke(obj, args); + } + catch (Exception e) + { + String msg = "method: " + method + ", obj: " + obj + ", args: " + args + ""; + throw convertReflectionExceptionToUnchecked(msg, e); + } + } + + /** + * 直接调用对象方法, 无视private/protected修饰符, + * 用于一次性调用的情况,否则应使用getAccessibleMethodByName()函数获得Method后反复调用. + * 只匹配函数名,如果有多个同名函数调用第一个。 + */ + @SuppressWarnings("unchecked") + public static E invokeMethodByName(final Object obj, final String methodName, final Object[] args) + { + Method method = getAccessibleMethodByName(obj, methodName, args.length); + if (method == null) + { + // 如果为空不报错,直接返回空。 + logger.debug("在 [" + obj.getClass() + "] 中,没有找到 [" + methodName + "] 方法 "); + return null; + } + try + { + // 类型转换(将参数数据类型转换为目标方法参数类型) + Class[] cs = method.getParameterTypes(); + for (int i = 0; i < cs.length; i++) + { + if (args[i] != null && !args[i].getClass().equals(cs[i])) + { + if (cs[i] == String.class) + { + args[i] = Convert.toStr(args[i]); + if (StringUtils.endsWith((String) args[i], ".0")) + { + args[i] = StringUtils.substringBefore((String) args[i], ".0"); + } + } + else if (cs[i] == Integer.class) + { + args[i] = Convert.toInt(args[i]); + } + else if (cs[i] == Long.class) + { + args[i] = Convert.toLong(args[i]); + } + else if (cs[i] == Double.class) + { + args[i] = Convert.toDouble(args[i]); + } + else if (cs[i] == Float.class) + { + args[i] = Convert.toFloat(args[i]); + } + else if (cs[i] == Date.class) + { + if (args[i] instanceof String) + { + args[i] = DateUtils.parseDate(args[i]); + } + else + { + args[i] = DateUtil.getJavaDate((Double) args[i]); + } + } + else if (cs[i] == boolean.class || cs[i] == Boolean.class) + { + args[i] = Convert.toBool(args[i]); + } + } + } + return (E) method.invoke(obj, args); + } + catch (Exception e) + { + String msg = "method: " + method + ", obj: " + obj + ", args: " + args + ""; + throw convertReflectionExceptionToUnchecked(msg, e); + } + } + + /** + * 循环向上转型, 获取对象的DeclaredField, 并强制设置为可访问. + * 如向上转型到Object仍无法找到, 返回null. + */ + public static Field getAccessibleField(final Object obj, final String fieldName) + { + // 为空不报错。直接返回 null + if (obj == null) + { + return null; + } + Validate.notBlank(fieldName, "fieldName can't be blank"); + for (Class superClass = obj.getClass(); superClass != Object.class; superClass = superClass.getSuperclass()) + { + try + { + Field field = superClass.getDeclaredField(fieldName); + makeAccessible(field); + return field; + } + catch (NoSuchFieldException e) + { + continue; + } + } + return null; + } + + /** + * 循环向上转型, 获取对象的DeclaredMethod,并强制设置为可访问. + * 如向上转型到Object仍无法找到, 返回null. + * 匹配函数名+参数类型。 + * 用于方法需要被多次调用的情况. 先使用本函数先取得Method,然后调用Method.invoke(Object obj, Object... args) + */ + public static Method getAccessibleMethod(final Object obj, final String methodName, + final Class... parameterTypes) + { + // 为空不报错。直接返回 null + if (obj == null) + { + return null; + } + Validate.notBlank(methodName, "methodName can't be blank"); + for (Class searchType = obj.getClass(); searchType != Object.class; searchType = searchType.getSuperclass()) + { + try + { + Method method = searchType.getDeclaredMethod(methodName, parameterTypes); + makeAccessible(method); + return method; + } + catch (NoSuchMethodException e) + { + continue; + } + } + return null; + } + + /** + * 循环向上转型, 获取对象的DeclaredMethod,并强制设置为可访问. + * 如向上转型到Object仍无法找到, 返回null. + * 只匹配函数名。 + * 用于方法需要被多次调用的情况. 先使用本函数先取得Method,然后调用Method.invoke(Object obj, Object... args) + */ + public static Method getAccessibleMethodByName(final Object obj, final String methodName, int argsNum) + { + // 为空不报错。直接返回 null + if (obj == null) + { + return null; + } + Validate.notBlank(methodName, "methodName can't be blank"); + for (Class searchType = obj.getClass(); searchType != Object.class; searchType = searchType.getSuperclass()) + { + Method[] methods = searchType.getDeclaredMethods(); + for (Method method : methods) + { + if (method.getName().equals(methodName) && method.getParameterTypes().length == argsNum) + { + makeAccessible(method); + return method; + } + } + } + return null; + } + + /** + * 改变private/protected的方法为public,尽量不调用实际改动的语句,避免JDK的SecurityManager抱怨。 + */ + public static void makeAccessible(Method method) + { + if ((!Modifier.isPublic(method.getModifiers()) || !Modifier.isPublic(method.getDeclaringClass().getModifiers())) + && !method.isAccessible()) + { + method.setAccessible(true); + } + } + + /** + * 改变private/protected的成员变量为public,尽量不调用实际改动的语句,避免JDK的SecurityManager抱怨。 + */ + public static void makeAccessible(Field field) + { + if ((!Modifier.isPublic(field.getModifiers()) || !Modifier.isPublic(field.getDeclaringClass().getModifiers()) + || Modifier.isFinal(field.getModifiers())) && !field.isAccessible()) + { + field.setAccessible(true); + } + } + + /** + * 通过反射, 获得Class定义中声明的泛型参数的类型, 注意泛型必须定义在父类处 + * 如无法找到, 返回Object.class. + */ + @SuppressWarnings("unchecked") + public static Class getClassGenricType(final Class clazz) + { + return getClassGenricType(clazz, 0); + } + + /** + * 通过反射, 获得Class定义中声明的父类的泛型参数的类型. + * 如无法找到, 返回Object.class. + */ + public static Class getClassGenricType(final Class clazz, final int index) + { + Type genType = clazz.getGenericSuperclass(); + + if (!(genType instanceof ParameterizedType)) + { + logger.debug(clazz.getSimpleName() + "'s superclass not ParameterizedType"); + return Object.class; + } + + Type[] params = ((ParameterizedType) genType).getActualTypeArguments(); + + if (index >= params.length || index < 0) + { + logger.debug("Index: " + index + ", Size of " + clazz.getSimpleName() + "'s Parameterized Type: " + + params.length); + return Object.class; + } + if (!(params[index] instanceof Class)) + { + logger.debug(clazz.getSimpleName() + " not set the actual class on superclass generic parameter"); + return Object.class; + } + + return (Class) params[index]; + } + + public static Class getUserClass(Object instance) + { + if (instance == null) + { + throw new RuntimeException("Instance must not be null"); + } + Class clazz = instance.getClass(); + if (clazz != null && clazz.getName().contains(CGLIB_CLASS_SEPARATOR)) + { + Class superClass = clazz.getSuperclass(); + if (superClass != null && !Object.class.equals(superClass)) + { + return superClass; + } + } + return clazz; + + } + + /** + * 将反射时的checked exception转换为unchecked exception. + */ + public static RuntimeException convertReflectionExceptionToUnchecked(String msg, Exception e) + { + if (e instanceof IllegalAccessException || e instanceof IllegalArgumentException + || e instanceof NoSuchMethodException) + { + return new IllegalArgumentException(msg, e); + } + else if (e instanceof InvocationTargetException) + { + return new RuntimeException(msg, ((InvocationTargetException) e).getTargetException()); + } + return new RuntimeException(msg, e); + } +} diff --git a/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/utils/sign/Base64.java b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/utils/sign/Base64.java new file mode 100644 index 0000000..d40bc80 --- /dev/null +++ b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/utils/sign/Base64.java @@ -0,0 +1,291 @@ +package com.aiotagro.common.core.utils.sign; + +/** + * Base64工具类 + * + * @author Carson + */ +public final class Base64 +{ + static private final int BASELENGTH = 128; + static private final int LOOKUPLENGTH = 64; + static private final int TWENTYFOURBITGROUP = 24; + static private final int EIGHTBIT = 8; + static private final int SIXTEENBIT = 16; + static private final int FOURBYTE = 4; + static private final int SIGN = -128; + static private final char PAD = '='; + static final private byte[] base64Alphabet = new byte[BASELENGTH]; + static final private char[] lookUpBase64Alphabet = new char[LOOKUPLENGTH]; + + static + { + for (int i = 0; i < BASELENGTH; ++i) + { + base64Alphabet[i] = -1; + } + for (int i = 'Z'; i >= 'A'; i--) + { + base64Alphabet[i] = (byte) (i - 'A'); + } + for (int i = 'z'; i >= 'a'; i--) + { + base64Alphabet[i] = (byte) (i - 'a' + 26); + } + + for (int i = '9'; i >= '0'; i--) + { + base64Alphabet[i] = (byte) (i - '0' + 52); + } + + base64Alphabet['+'] = 62; + base64Alphabet['/'] = 63; + + for (int i = 0; i <= 25; i++) + { + lookUpBase64Alphabet[i] = (char) ('A' + i); + } + + for (int i = 26, j = 0; i <= 51; i++, j++) + { + lookUpBase64Alphabet[i] = (char) ('a' + j); + } + + for (int i = 52, j = 0; i <= 61; i++, j++) + { + lookUpBase64Alphabet[i] = (char) ('0' + j); + } + lookUpBase64Alphabet[62] = (char) '+'; + lookUpBase64Alphabet[63] = (char) '/'; + } + + private static boolean isWhiteSpace(char octect) + { + return (octect == 0x20 || octect == 0xd || octect == 0xa || octect == 0x9); + } + + private static boolean isPad(char octect) + { + return (octect == PAD); + } + + private static boolean isData(char octect) + { + return (octect < BASELENGTH && base64Alphabet[octect] != -1); + } + + /** + * Encodes hex octects into Base64 + * + * @param binaryData Array containing binaryData + * @return Encoded Base64 array + */ + public static String encode(byte[] binaryData) + { + if (binaryData == null) + { + return null; + } + + int lengthDataBits = binaryData.length * EIGHTBIT; + if (lengthDataBits == 0) + { + return ""; + } + + int fewerThan24bits = lengthDataBits % TWENTYFOURBITGROUP; + int numberTriplets = lengthDataBits / TWENTYFOURBITGROUP; + int numberQuartet = fewerThan24bits != 0 ? numberTriplets + 1 : numberTriplets; + char encodedData[] = null; + + encodedData = new char[numberQuartet * 4]; + + byte k = 0, l = 0, b1 = 0, b2 = 0, b3 = 0; + + int encodedIndex = 0; + int dataIndex = 0; + + for (int i = 0; i < numberTriplets; i++) + { + b1 = binaryData[dataIndex++]; + b2 = binaryData[dataIndex++]; + b3 = binaryData[dataIndex++]; + + l = (byte) (b2 & 0x0f); + k = (byte) (b1 & 0x03); + + byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0); + byte val2 = ((b2 & SIGN) == 0) ? (byte) (b2 >> 4) : (byte) ((b2) >> 4 ^ 0xf0); + byte val3 = ((b3 & SIGN) == 0) ? (byte) (b3 >> 6) : (byte) ((b3) >> 6 ^ 0xfc); + + encodedData[encodedIndex++] = lookUpBase64Alphabet[val1]; + encodedData[encodedIndex++] = lookUpBase64Alphabet[val2 | (k << 4)]; + encodedData[encodedIndex++] = lookUpBase64Alphabet[(l << 2) | val3]; + encodedData[encodedIndex++] = lookUpBase64Alphabet[b3 & 0x3f]; + } + + // form integral number of 6-bit groups + if (fewerThan24bits == EIGHTBIT) + { + b1 = binaryData[dataIndex]; + k = (byte) (b1 & 0x03); + byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0); + encodedData[encodedIndex++] = lookUpBase64Alphabet[val1]; + encodedData[encodedIndex++] = lookUpBase64Alphabet[k << 4]; + encodedData[encodedIndex++] = PAD; + encodedData[encodedIndex++] = PAD; + } + else if (fewerThan24bits == SIXTEENBIT) + { + b1 = binaryData[dataIndex]; + b2 = binaryData[dataIndex + 1]; + l = (byte) (b2 & 0x0f); + k = (byte) (b1 & 0x03); + + byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0); + byte val2 = ((b2 & SIGN) == 0) ? (byte) (b2 >> 4) : (byte) ((b2) >> 4 ^ 0xf0); + + encodedData[encodedIndex++] = lookUpBase64Alphabet[val1]; + encodedData[encodedIndex++] = lookUpBase64Alphabet[val2 | (k << 4)]; + encodedData[encodedIndex++] = lookUpBase64Alphabet[l << 2]; + encodedData[encodedIndex++] = PAD; + } + return new String(encodedData); + } + + /** + * Decodes Base64 data into octects + * + * @param encoded string containing Base64 data + * @return Array containind decoded data. + */ + public static byte[] decode(String encoded) + { + if (encoded == null) + { + return null; + } + + char[] base64Data = encoded.toCharArray(); + // remove white spaces + int len = removeWhiteSpace(base64Data); + + if (len % FOURBYTE != 0) + { + return null;// should be divisible by four + } + + int numberQuadruple = (len / FOURBYTE); + + if (numberQuadruple == 0) + { + return new byte[0]; + } + + byte decodedData[] = null; + byte b1 = 0, b2 = 0, b3 = 0, b4 = 0; + char d1 = 0, d2 = 0, d3 = 0, d4 = 0; + + int i = 0; + int encodedIndex = 0; + int dataIndex = 0; + decodedData = new byte[(numberQuadruple) * 3]; + + for (; i < numberQuadruple - 1; i++) + { + + if (!isData((d1 = base64Data[dataIndex++])) || !isData((d2 = base64Data[dataIndex++])) + || !isData((d3 = base64Data[dataIndex++])) || !isData((d4 = base64Data[dataIndex++]))) + { + return null; + } // if found "no data" just return null + + b1 = base64Alphabet[d1]; + b2 = base64Alphabet[d2]; + b3 = base64Alphabet[d3]; + b4 = base64Alphabet[d4]; + + decodedData[encodedIndex++] = (byte) (b1 << 2 | b2 >> 4); + decodedData[encodedIndex++] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf)); + decodedData[encodedIndex++] = (byte) (b3 << 6 | b4); + } + + if (!isData((d1 = base64Data[dataIndex++])) || !isData((d2 = base64Data[dataIndex++]))) + { + return null;// if found "no data" just return null + } + + b1 = base64Alphabet[d1]; + b2 = base64Alphabet[d2]; + + d3 = base64Data[dataIndex++]; + d4 = base64Data[dataIndex++]; + if (!isData((d3)) || !isData((d4))) + {// Check if they are PAD characters + if (isPad(d3) && isPad(d4)) + { + if ((b2 & 0xf) != 0)// last 4 bits should be zero + { + return null; + } + byte[] tmp = new byte[i * 3 + 1]; + System.arraycopy(decodedData, 0, tmp, 0, i * 3); + tmp[encodedIndex] = (byte) (b1 << 2 | b2 >> 4); + return tmp; + } + else if (!isPad(d3) && isPad(d4)) + { + b3 = base64Alphabet[d3]; + if ((b3 & 0x3) != 0)// last 2 bits should be zero + { + return null; + } + byte[] tmp = new byte[i * 3 + 2]; + System.arraycopy(decodedData, 0, tmp, 0, i * 3); + tmp[encodedIndex++] = (byte) (b1 << 2 | b2 >> 4); + tmp[encodedIndex] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf)); + return tmp; + } + else + { + return null; + } + } + else + { // No PAD e.g 3cQl + b3 = base64Alphabet[d3]; + b4 = base64Alphabet[d4]; + decodedData[encodedIndex++] = (byte) (b1 << 2 | b2 >> 4); + decodedData[encodedIndex++] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf)); + decodedData[encodedIndex++] = (byte) (b3 << 6 | b4); + + } + return decodedData; + } + + /** + * remove WhiteSpace from MIME containing encoded Base64 data. + * + * @param data the byte array of base64 data (with WS) + * @return the new length + */ + private static int removeWhiteSpace(char[] data) + { + if (data == null) + { + return 0; + } + + // count characters that's not whitespace + int newSize = 0; + int len = data.length; + for (int i = 0; i < len; i++) + { + if (!isWhiteSpace(data[i])) + { + data[newSize++] = data[i]; + } + } + return newSize; + } +} diff --git a/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/utils/sql/SqlUtil.java b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/utils/sql/SqlUtil.java new file mode 100644 index 0000000..a1c3b3b --- /dev/null +++ b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/utils/sql/SqlUtil.java @@ -0,0 +1,70 @@ +package com.aiotagro.common.core.utils.sql; + +import com.aiotagro.common.core.exception.UtilException; +import com.aiotagro.common.core.utils.StringUtils; + +/** + * sql操作工具类 + * + * @author Carson + */ +public class SqlUtil +{ + /** + * 定义常用的 sql关键字 + */ + public static String SQL_REGEX = "and |extractvalue|updatexml|exec |insert |select |delete |update |drop |count |chr |mid |master |truncate |char |declare |or |+|user()"; + + /** + * 仅支持字母、数字、下划线、空格、逗号、小数点(支持多个字段排序) + */ + public static String SQL_PATTERN = "[a-zA-Z0-9_\\ \\,\\.]+"; + + /** + * 限制orderBy最大长度 + */ + private static final int ORDER_BY_MAX_LENGTH = 500; + + /** + * 检查字符,防止注入绕过 + */ + public static String escapeOrderBySql(String value) + { + if (StringUtils.isNotEmpty(value) && !isValidOrderBySql(value)) + { + throw new UtilException("参数不符合规范,不能进行查询"); + } + if (StringUtils.length(value) > ORDER_BY_MAX_LENGTH) + { + throw new UtilException("参数已超过最大限制,不能进行查询"); + } + return value; + } + + /** + * 验证 order by 语法是否符合规范 + */ + public static boolean isValidOrderBySql(String value) + { + return value.matches(SQL_PATTERN); + } + + /** + * SQL关键字检查 + */ + public static void filterKeyword(String value) + { + if (StringUtils.isEmpty(value)) + { + return; + } + String[] sqlKeywords = StringUtils.split(SQL_REGEX, "\\|"); + for (String sqlKeyword : sqlKeywords) + { + if (StringUtils.indexOfIgnoreCase(value, sqlKeyword) > -1) + { + throw new UtilException("参数存在SQL注入风险"); + } + } + } +} diff --git a/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/utils/uuid/DistinctUtil.java b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/utils/uuid/DistinctUtil.java new file mode 100644 index 0000000..806bd41 --- /dev/null +++ b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/utils/uuid/DistinctUtil.java @@ -0,0 +1,76 @@ +package com.aiotagro.common.core.utils.uuid; + +import com.alibaba.fastjson2.JSONArray; +import com.alibaba.fastjson2.JSONException; +import com.alibaba.fastjson2.JSONObject; +import lombok.extern.slf4j.Slf4j; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.HttpURLConnection; +import java.net.URL; + +/** + * @author xiefan + */ +@Slf4j +public class DistinctUtil { + + private static final String API_KEY = "f9c298affa8aedea29d604ffb26c7078"; + private static final String BASE_URL = "https://restapi.amap.com/v5/direction/driving"; + + /** + * 高德api计算两地之间的开车距离 + * + * @param startLat + * @param startLon + * @param endLat + * @param endLon + * @return + */ + public static double calculateDistance(double startLat, double startLon, double endLat, double endLon) throws IOException, JSONException { + // 比较经纬度,如果相同则直接返回0 + if (Math.abs(startLat - endLat) < 1e-6 && Math.abs(startLon - endLon) < 1e-6) { + log.info("起始地和目的地相同,距离设置为0公里"); + return 0; + } + // //进行格式拼接(当前位置/起点) + String origin = startLon + "," + startLat; + //进行格式拼接(目的地当前位置/终点) + String destination = endLon + "," + endLat; + log.info("起始地的经纬度" + origin + "目的地" + destination); + String url = BASE_URL + "?key=" + API_KEY + + "&origin=" + origin + + "&destination=" + destination + + "&output=json"; + + log.info("请求高德api计算距离url:" + url); + HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection(); + connection.setRequestMethod("GET"); + connection.connect(); + + BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream())); + StringBuilder response = new StringBuilder(); + String line; + while ((line = reader.readLine()) != null) { + response.append(line); + } + reader.close(); + connection.disconnect(); + + log.info("请求高德api计算距离返回:" + response); + + // 使用Fastjson解析JSON字符串 + JSONObject jsonResponse = JSONObject.parseObject(response.toString()); + + // 获取路径信息并提取距离 + JSONObject routes = jsonResponse.getJSONObject("route"); + JSONArray path = routes.getJSONArray("paths"); + JSONObject jsonObject = path.getJSONObject(0); + String distance = jsonObject.get("distance").toString(); + double number = Double.parseDouble(distance) / 1000; + log.info("距离:" + number + "公里"); + return number; + } +} diff --git a/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/utils/uuid/IdUtils.java b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/utils/uuid/IdUtils.java new file mode 100644 index 0000000..3030761 --- /dev/null +++ b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/utils/uuid/IdUtils.java @@ -0,0 +1,49 @@ +package com.aiotagro.common.core.utils.uuid; + +/** + * ID生成器工具类 + * + * @author Carson + */ +public class IdUtils +{ + /** + * 获取随机UUID + * + * @return 随机UUID + */ + public static String randomUUID() + { + return UUID.randomUUID().toString(); + } + + /** + * 简化的UUID,去掉了横线 + * + * @return 简化的UUID,去掉了横线 + */ + public static String simpleUUID() + { + return UUID.randomUUID().toString(true); + } + + /** + * 获取随机UUID,使用性能更好的ThreadLocalRandom生成UUID + * + * @return 随机UUID + */ + public static String fastUUID() + { + return UUID.fastUUID().toString(); + } + + /** + * 简化的UUID,去掉了横线,使用性能更好的ThreadLocalRandom生成UUID + * + * @return 简化的UUID,去掉了横线 + */ + public static String fastSimpleUUID() + { + return UUID.fastUUID().toString(true); + } +} diff --git a/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/utils/uuid/Seq.java b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/utils/uuid/Seq.java new file mode 100644 index 0000000..257284e --- /dev/null +++ b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/utils/uuid/Seq.java @@ -0,0 +1,87 @@ +package com.aiotagro.common.core.utils.uuid; + +import com.aiotagro.common.core.utils.DateUtils; +import com.aiotagro.common.core.utils.StringUtils; + +import java.util.concurrent.atomic.AtomicInteger; + +/** + * @author Carson 序列生成类 + */ +public class Seq +{ + // 通用序列类型 + public static final String commSeqType = "COMMON"; + + // 上传序列类型 + public static final String uploadSeqType = "UPLOAD"; + + // 通用接口序列数 + private static AtomicInteger commSeq = new AtomicInteger(1); + + // 上传接口序列数 + private static AtomicInteger uploadSeq = new AtomicInteger(1); + + // 机器标识 + private static final String machineCode = "A"; + + /** + * 获取通用序列号 + * + * @return 序列值 + */ + public static String getId() + { + return getId(commSeqType); + } + + /** + * 默认16位序列号 yyMMddHHmmss + 一位机器标识 + 3长度循环递增字符串 + * + * @return 序列值 + */ + public static String getId(String type) + { + AtomicInteger atomicInt = commSeq; + if (uploadSeqType.equals(type)) + { + atomicInt = uploadSeq; + } + return getId(atomicInt, 3); + } + + /** + * 通用接口序列号 yyMMddHHmmss + 一位机器标识 + length长度循环递增字符串 + * + * @param atomicInt 序列数 + * @param length 数值长度 + * @return 序列值 + */ + public static String getId(AtomicInteger atomicInt, int length) + { + String result = DateUtils.dateTimeNow(); + result += machineCode; + result += getSeq(atomicInt, length); + return result; + } + + /** + * 序列循环递增字符串[1, 10 的 (length)幂次方), 用0左补齐length位数 + * + * @return 序列值 + */ + private synchronized static String getSeq(AtomicInteger atomicInt, int length) + { + // 先取值再+1 + int value = atomicInt.getAndIncrement(); + + // 如果更新后值>=10 的 (length)幂次方则重置为1 + int maxSeq = (int) Math.pow(10, length); + if (atomicInt.get() >= maxSeq) + { + atomicInt.set(1); + } + // 转字符串,用0左补齐 + return StringUtils.padl(value, length); + } +} diff --git a/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/utils/uuid/UUID.java b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/utils/uuid/UUID.java new file mode 100644 index 0000000..8337bff --- /dev/null +++ b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/utils/uuid/UUID.java @@ -0,0 +1,485 @@ +package com.aiotagro.common.core.utils.uuid; + +import com.aiotagro.common.core.exception.UtilException; + +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.util.Random; +import java.util.concurrent.ThreadLocalRandom; + +/** + * 提供通用唯一识别码(universally unique identifier)(UUID)实现 + * + * @author Carson + */ +public final class UUID implements java.io.Serializable, Comparable +{ + private static final long serialVersionUID = -1185015143654744140L; + + /** + * SecureRandom 的单例 + * + */ + private static class Holder + { + static final SecureRandom numberGenerator = getSecureRandom(); + } + + /** 此UUID的最高64有效位 */ + private final long mostSigBits; + + /** 此UUID的最低64有效位 */ + private final long leastSigBits; + + /** + * 私有构造 + * + * @param data 数据 + */ + private UUID(byte[] data) + { + long msb = 0; + long lsb = 0; + assert data.length == 16 : "data must be 16 bytes in length"; + for (int i = 0; i < 8; i++) + { + msb = (msb << 8) | (data[i] & 0xff); + } + for (int i = 8; i < 16; i++) + { + lsb = (lsb << 8) | (data[i] & 0xff); + } + this.mostSigBits = msb; + this.leastSigBits = lsb; + } + + /** + * 使用指定的数据构造新的 UUID。 + * + * @param mostSigBits 用于 {@code UUID} 的最高有效 64 位 + * @param leastSigBits 用于 {@code UUID} 的最低有效 64 位 + */ + public UUID(long mostSigBits, long leastSigBits) + { + this.mostSigBits = mostSigBits; + this.leastSigBits = leastSigBits; + } + + /** + * 获取类型 4(伪随机生成的)UUID 的静态工厂。 + * + * @return 随机生成的 {@code UUID} + */ + public static UUID fastUUID() + { + return randomUUID(false); + } + + /** + * 获取类型 4(伪随机生成的)UUID 的静态工厂。 使用加密的强伪随机数生成器生成该 UUID。 + * + * @return 随机生成的 {@code UUID} + */ + public static UUID randomUUID() + { + return randomUUID(true); + } + + /** + * 获取类型 4(伪随机生成的)UUID 的静态工厂。 使用加密的强伪随机数生成器生成该 UUID。 + * + * @param isSecure 是否使用{@link SecureRandom}如果是可以获得更安全的随机码,否则可以得到更好的性能 + * @return 随机生成的 {@code UUID} + */ + public static UUID randomUUID(boolean isSecure) + { + final Random ng = isSecure ? Holder.numberGenerator : getRandom(); + + byte[] randomBytes = new byte[16]; + ng.nextBytes(randomBytes); + randomBytes[6] &= 0x0f; /* clear version */ + randomBytes[6] |= 0x40; /* set to version 4 */ + randomBytes[8] &= 0x3f; /* clear variant */ + randomBytes[8] |= 0x80; /* set to IETF variant */ + return new UUID(randomBytes); + } + + /** + * 根据指定的字节数组获取类型 3(基于名称的)UUID 的静态工厂。 + * + * @param name 用于构造 UUID 的字节数组。 + * + * @return 根据指定数组生成的 {@code UUID} + */ + public static UUID nameUUIDFromBytes(byte[] name) + { + MessageDigest md; + try + { + md = MessageDigest.getInstance("MD5"); + } + catch (NoSuchAlgorithmException nsae) + { + throw new InternalError("MD5 not supported"); + } + byte[] md5Bytes = md.digest(name); + md5Bytes[6] &= 0x0f; /* clear version */ + md5Bytes[6] |= 0x30; /* set to version 3 */ + md5Bytes[8] &= 0x3f; /* clear variant */ + md5Bytes[8] |= 0x80; /* set to IETF variant */ + return new UUID(md5Bytes); + } + + /** + * 根据 {@link #toString()} 方法中描述的字符串标准表示形式创建{@code UUID}。 + * + * @param name 指定 {@code UUID} 字符串 + * @return 具有指定值的 {@code UUID} + * @throws IllegalArgumentException 如果 name 与 {@link #toString} 中描述的字符串表示形式不符抛出此异常 + * + */ + public static UUID fromString(String name) + { + String[] components = name.split("-"); + if (components.length != 5) + { + throw new IllegalArgumentException("Invalid UUID string: " + name); + } + for (int i = 0; i < 5; i++) + { + components[i] = "0x" + components[i]; + } + + long mostSigBits = Long.decode(components[0]).longValue(); + mostSigBits <<= 16; + mostSigBits |= Long.decode(components[1]).longValue(); + mostSigBits <<= 16; + mostSigBits |= Long.decode(components[2]).longValue(); + + long leastSigBits = Long.decode(components[3]).longValue(); + leastSigBits <<= 48; + leastSigBits |= Long.decode(components[4]).longValue(); + + return new UUID(mostSigBits, leastSigBits); + } + + /** + * 返回此 UUID 的 128 位值中的最低有效 64 位。 + * + * @return 此 UUID 的 128 位值中的最低有效 64 位。 + */ + public long getLeastSignificantBits() + { + return leastSigBits; + } + + /** + * 返回此 UUID 的 128 位值中的最高有效 64 位。 + * + * @return 此 UUID 的 128 位值中最高有效 64 位。 + */ + public long getMostSignificantBits() + { + return mostSigBits; + } + + /** + * 与此 {@code UUID} 相关联的版本号. 版本号描述此 {@code UUID} 是如何生成的。 + *

+ * 版本号具有以下含意: + *

    + *
  • 1 基于时间的 UUID + *
  • 2 DCE 安全 UUID + *
  • 3 基于名称的 UUID + *
  • 4 随机生成的 UUID + *
+ * + * @return 此 {@code UUID} 的版本号 + */ + public int version() + { + // Version is bits masked by 0x000000000000F000 in MS long + return (int) ((mostSigBits >> 12) & 0x0f); + } + + /** + * 与此 {@code UUID} 相关联的变体号。变体号描述 {@code UUID} 的布局。 + *

+ * 变体号具有以下含意: + *

    + *
  • 0 为 NCS 向后兼容保留 + *
  • 2 IETF RFC 4122(Leach-Salz), 用于此类 + *
  • 6 保留,微软向后兼容 + *
  • 7 保留供以后定义使用 + *
+ * + * @return 此 {@code UUID} 相关联的变体号 + */ + public int variant() + { + // This field is composed of a varying number of bits. + // 0 - - Reserved for NCS backward compatibility + // 1 0 - The IETF aka Leach-Salz variant (used by this class) + // 1 1 0 Reserved, Microsoft backward compatibility + // 1 1 1 Reserved for future definition. + return (int) ((leastSigBits >>> (64 - (leastSigBits >>> 62))) & (leastSigBits >> 63)); + } + + /** + * 与此 UUID 相关联的时间戳值。 + * + *

+ * 60 位的时间戳值根据此 {@code UUID} 的 time_low、time_mid 和 time_hi 字段构造。
+ * 所得到的时间戳以 100 毫微秒为单位,从 UTC(通用协调时间) 1582 年 10 月 15 日零时开始。 + * + *

+ * 时间戳值仅在在基于时间的 UUID(其 version 类型为 1)中才有意义。
+ * 如果此 {@code UUID} 不是基于时间的 UUID,则此方法抛出 UnsupportedOperationException。 + * + * @throws UnsupportedOperationException 如果此 {@code UUID} 不是 version 为 1 的 UUID。 + */ + public long timestamp() throws UnsupportedOperationException + { + checkTimeBase(); + return (mostSigBits & 0x0FFFL) << 48// + | ((mostSigBits >> 16) & 0x0FFFFL) << 32// + | mostSigBits >>> 32; + } + + /** + * 与此 UUID 相关联的时钟序列值。 + * + *

+ * 14 位的时钟序列值根据此 UUID 的 clock_seq 字段构造。clock_seq 字段用于保证在基于时间的 UUID 中的时间唯一性。 + *

+ * {@code clockSequence} 值仅在基于时间的 UUID(其 version 类型为 1)中才有意义。 如果此 UUID 不是基于时间的 UUID,则此方法抛出 + * UnsupportedOperationException。 + * + * @return 此 {@code UUID} 的时钟序列 + * + * @throws UnsupportedOperationException 如果此 UUID 的 version 不为 1 + */ + public int clockSequence() throws UnsupportedOperationException + { + checkTimeBase(); + return (int) ((leastSigBits & 0x3FFF000000000000L) >>> 48); + } + + /** + * 与此 UUID 相关的节点值。 + * + *

+ * 48 位的节点值根据此 UUID 的 node 字段构造。此字段旨在用于保存机器的 IEEE 802 地址,该地址用于生成此 UUID 以保证空间唯一性。 + *

+ * 节点值仅在基于时间的 UUID(其 version 类型为 1)中才有意义。
+ * 如果此 UUID 不是基于时间的 UUID,则此方法抛出 UnsupportedOperationException。 + * + * @return 此 {@code UUID} 的节点值 + * + * @throws UnsupportedOperationException 如果此 UUID 的 version 不为 1 + */ + public long node() throws UnsupportedOperationException + { + checkTimeBase(); + return leastSigBits & 0x0000FFFFFFFFFFFFL; + } + + /** + * 返回此{@code UUID} 的字符串表现形式。 + * + *

+ * UUID 的字符串表示形式由此 BNF 描述: + * + *

+     * {@code
+     * UUID                   = ----
+     * time_low               = 4*
+     * time_mid               = 2*
+     * time_high_and_version  = 2*
+     * variant_and_sequence   = 2*
+     * node                   = 6*
+     * hexOctet               = 
+     * hexDigit               = [0-9a-fA-F]
+     * }
+     * 
+ * + * + * + * @return 此{@code UUID} 的字符串表现形式 + * @see #toString(boolean) + */ + @Override + public String toString() + { + return toString(false); + } + + /** + * 返回此{@code UUID} 的字符串表现形式。 + * + *

+ * UUID 的字符串表示形式由此 BNF 描述: + * + *

+     * {@code
+     * UUID                   = ----
+     * time_low               = 4*
+     * time_mid               = 2*
+     * time_high_and_version  = 2*
+     * variant_and_sequence   = 2*
+     * node                   = 6*
+     * hexOctet               = 
+     * hexDigit               = [0-9a-fA-F]
+     * }
+     * 
+ * + * + * + * @param isSimple 是否简单模式,简单模式为不带'-'的UUID字符串 + * @return 此{@code UUID} 的字符串表现形式 + */ + public String toString(boolean isSimple) + { + final StringBuilder builder = new StringBuilder(isSimple ? 32 : 36); + // time_low + builder.append(digits(mostSigBits >> 32, 8)); + if (false == isSimple) + { + builder.append('-'); + } + // time_mid + builder.append(digits(mostSigBits >> 16, 4)); + if (false == isSimple) + { + builder.append('-'); + } + // time_high_and_version + builder.append(digits(mostSigBits, 4)); + if (false == isSimple) + { + builder.append('-'); + } + // variant_and_sequence + builder.append(digits(leastSigBits >> 48, 4)); + if (false == isSimple) + { + builder.append('-'); + } + // node + builder.append(digits(leastSigBits, 12)); + + return builder.toString(); + } + + /** + * 返回此 UUID 的哈希码。 + * + * @return UUID 的哈希码值。 + */ + @Override + public int hashCode() + { + long hilo = mostSigBits ^ leastSigBits; + return ((int) (hilo >> 32)) ^ (int) hilo; + } + + /** + * 将此对象与指定对象比较。 + *

+ * 当且仅当参数不为 {@code null}、而是一个 UUID 对象、具有与此 UUID 相同的 varriant、包含相同的值(每一位均相同)时,结果才为 {@code true}。 + * + * @param obj 要与之比较的对象 + * + * @return 如果对象相同,则返回 {@code true};否则返回 {@code false} + */ + @Override + public boolean equals(Object obj) + { + if ((null == obj) || (obj.getClass() != UUID.class)) + { + return false; + } + UUID id = (UUID) obj; + return (mostSigBits == id.mostSigBits && leastSigBits == id.leastSigBits); + } + + // Comparison Operations + + /** + * 将此 UUID 与指定的 UUID 比较。 + * + *

+ * 如果两个 UUID 不同,且第一个 UUID 的最高有效字段大于第二个 UUID 的对应字段,则第一个 UUID 大于第二个 UUID。 + * + * @param val 与此 UUID 比较的 UUID + * + * @return 在此 UUID 小于、等于或大于 val 时,分别返回 -1、0 或 1。 + * + */ + @Override + public int compareTo(UUID val) + { + // The ordering is intentionally set up so that the UUIDs + // can simply be numerically compared as two numbers + return (this.mostSigBits < val.mostSigBits ? -1 : // + (this.mostSigBits > val.mostSigBits ? 1 : // + (this.leastSigBits < val.leastSigBits ? -1 : // + (this.leastSigBits > val.leastSigBits ? 1 : // + 0)))); + } + + // ------------------------------------------------------------------------------------------------------------------- + // Private method start + /** + * 返回指定数字对应的hex值 + * + * @param val 值 + * @param digits 位 + * @return 值 + */ + private static String digits(long val, int digits) + { + long hi = 1L << (digits * 4); + return Long.toHexString(hi | (val & (hi - 1))).substring(1); + } + + /** + * 检查是否为time-based版本UUID + */ + private void checkTimeBase() + { + if (version() != 1) + { + throw new UnsupportedOperationException("Not a time-based UUID"); + } + } + + /** + * 获取{@link SecureRandom},类提供加密的强随机数生成器 (RNG) + * + * @return {@link SecureRandom} + */ + public static SecureRandom getSecureRandom() + { + try + { + return SecureRandom.getInstance("SHA1PRNG"); + } + catch (NoSuchAlgorithmException e) + { + throw new UtilException(e); + } + } + + /** + * 获取随机数生成器对象
+ * ThreadLocalRandom是JDK 7之后提供并发产生随机数,能够解决多个线程发生的竞争争夺。 + * + * @return {@link ThreadLocalRandom} + */ + public static ThreadLocalRandom getRandom() + { + return ThreadLocalRandom.current(); + } +} diff --git a/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/web/domain/AjaxResult.java b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/web/domain/AjaxResult.java new file mode 100644 index 0000000..bb92716 --- /dev/null +++ b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/web/domain/AjaxResult.java @@ -0,0 +1,217 @@ +package com.aiotagro.common.core.web.domain; + +import com.aiotagro.common.core.constant.HttpStatus; +import com.aiotagro.common.core.utils.StringUtils; + +import java.util.HashMap; +import java.util.Objects; + +/** + * 操作消息提醒 + * + * @author Carson + */ +public class AjaxResult extends HashMap +{ + private static final long serialVersionUID = 1L; + + /** 状态码 */ + public static final String CODE_TAG = "code"; + + /** 返回内容 */ + public static final String MSG_TAG = "msg"; + + /** 数据对象 */ + public static final String DATA_TAG = "data"; + + /** + * 初始化一个新创建的 AjaxResult 对象,使其表示一个空消息。 + */ + public AjaxResult() + { + } + + /** + * 初始化一个新创建的 AjaxResult 对象 + * + * @param code 状态码 + * @param msg 返回内容 + */ + public AjaxResult(int code, String msg) + { + super.put(CODE_TAG, code); + super.put(MSG_TAG, msg); + } + + /** + * 初始化一个新创建的 AjaxResult 对象 + * + * @param code 状态码 + * @param msg 返回内容 + * @param data 数据对象 + */ + public AjaxResult(int code, String msg, Object data) + { + super.put(CODE_TAG, code); + super.put(MSG_TAG, msg); + if (StringUtils.isNotNull(data)) + { + super.put(DATA_TAG, data); + } + } + + /** + * 返回成功消息 + * + * @return 成功消息 + */ + public static AjaxResult success() + { + return AjaxResult.success("操作成功"); + } + + /** + * 返回成功数据 + * + * @return 成功消息 + */ + public static AjaxResult success(Object data) + { + return AjaxResult.success("操作成功", data); + } + + /** + * 返回成功消息 + * + * @param msg 返回内容 + * @return 成功消息 + */ + public static AjaxResult success(String msg) + { + return AjaxResult.success(msg, null); + } + + /** + * 返回成功消息 + * + * @param msg 返回内容 + * @param data 数据对象 + * @return 成功消息 + */ + public static AjaxResult success(String msg, Object data) + { + return new AjaxResult(HttpStatus.SUCCESS, msg, data); + } + + /** + * 返回警告消息 + * + * @param msg 返回内容 + * @return 警告消息 + */ + public static AjaxResult warn(String msg) + { + return AjaxResult.warn(msg, null); + } + + /** + * 返回警告消息 + * + * @param msg 返回内容 + * @param data 数据对象 + * @return 警告消息 + */ + public static AjaxResult warn(String msg, Object data) + { + return new AjaxResult(HttpStatus.WARN, msg, data); + } + + /** + * 返回错误消息 + * + * @return 错误消息 + */ + public static AjaxResult error() + { + return AjaxResult.error("操作失败"); + } + + /** + * 返回错误消息 + * + * @param msg 返回内容 + * @return 错误消息 + */ + public static AjaxResult error(String msg) + { + return AjaxResult.error(msg, null); + } + + /** + * 返回错误消息 + * + * @param msg 返回内容 + * @param data 数据对象 + * @return 错误消息 + */ + public static AjaxResult error(String msg, Object data) + { + return new AjaxResult(HttpStatus.ERROR, msg, data); + } + + /** + * 返回错误消息 + * + * @param code 状态码 + * @param msg 返回内容 + * @return 错误消息 + */ + public static AjaxResult error(int code, String msg) + { + return new AjaxResult(code, msg, null); + } + + /** + * 是否为成功消息 + * + * @return 结果 + */ + public boolean isSuccess() + { + return Objects.equals(HttpStatus.SUCCESS, this.get(CODE_TAG)); + } + + /** + * 是否为警告消息 + * + * @return 结果 + */ + public boolean isWarn() + { + return Objects.equals(HttpStatus.WARN, this.get(CODE_TAG)); + } + + /** + * 是否为错误消息 + * + * @return 结果 + */ + public boolean isError() + { + return Objects.equals(HttpStatus.ERROR, this.get(CODE_TAG)); + } + + /** + * 方便链式调用 + * + * @param key + * @param value + * @return + */ + @Override + public AjaxResult put(String key, Object value) + { + super.put(key, value); + return this; + } +} diff --git a/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/web/domain/BaseEntity.java b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/web/domain/BaseEntity.java new file mode 100644 index 0000000..5d54aba --- /dev/null +++ b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/web/domain/BaseEntity.java @@ -0,0 +1,119 @@ +package com.aiotagro.common.core.web.domain; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonInclude; + +import java.io.Serializable; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +/** + * Entity基类 + * + * @author Carson + */ +public class BaseEntity implements Serializable +{ + private static final long serialVersionUID = 1L; + + /** 搜索值 */ + @JsonIgnore + private String searchValue; + + /** 创建者 */ + private String createBy; + + /** 创建时间 */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; + + /** 更新者 */ + private String updateBy; + + /** 更新时间 */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date updateTime; + + /** 备注 */ + private String remark; + + /** 请求参数 */ + @JsonInclude(JsonInclude.Include.NON_EMPTY) + private Map params; + + public String getSearchValue() + { + return searchValue; + } + + public void setSearchValue(String searchValue) + { + this.searchValue = searchValue; + } + + public String getCreateBy() + { + return createBy; + } + + public void setCreateBy(String createBy) + { + this.createBy = createBy; + } + + public Date getCreateTime() + { + return createTime; + } + + public void setCreateTime(Date createTime) + { + this.createTime = createTime; + } + + public String getUpdateBy() + { + return updateBy; + } + + public void setUpdateBy(String updateBy) + { + this.updateBy = updateBy; + } + + public Date getUpdateTime() + { + return updateTime; + } + + public void setUpdateTime(Date updateTime) + { + this.updateTime = updateTime; + } + + public String getRemark() + { + return remark; + } + + public void setRemark(String remark) + { + this.remark = remark; + } + + public Map getParams() + { + if (params == null) + { + params = new HashMap<>(); + } + return params; + } + + public void setParams(Map params) + { + this.params = params; + } +} diff --git a/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/web/domain/BaseResponse.java b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/web/domain/BaseResponse.java new file mode 100644 index 0000000..5861893 --- /dev/null +++ b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/web/domain/BaseResponse.java @@ -0,0 +1,21 @@ +package com.aiotagro.common.core.web.domain; + +import lombok.Data; + +@Data +public class BaseResponse { + private int code = 200; + private String msg = "success"; + + public BaseResponse(int code, String msg) { + this.code = code; + this.msg = msg; + } + + public BaseResponse() { + } + + + + +} diff --git a/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/web/domain/PageResultResponse.java b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/web/domain/PageResultResponse.java new file mode 100644 index 0000000..59a5b21 --- /dev/null +++ b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/web/domain/PageResultResponse.java @@ -0,0 +1,90 @@ +package com.aiotagro.common.core.web.domain; + +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.List; + + + +@Data +@EqualsAndHashCode(callSuper=false) +public class PageResultResponse extends BaseResponse{ + /** + * 业务状态编码 + */ + private int code = 200; + /** + * 消息内容 + */ + private String msg = "success"; + TableData data; + + public PageResultResponse(int code, String msg) { + this.code = code; + this.msg = msg ; + } + public PageResultResponse(long total, List rows) { + this.data = new TableData(total, rows); + } + + public PageResultResponse() { + this.data = new TableData(); + } + + public PageResultResponse(Integer type, long total, List rows) { + this.data = new TableData(type, total, rows); + } + + PageResultResponse total(int total) { + this.data.setTotal(total); + return this; + } + + PageResultResponse total(List rows) { + this.data.setRows(rows); + return this; + } + + public TableData getData() { + return data; + } + + public void setData(TableData data) { + this.data = data; + } + + @SuppressWarnings("hiding") + public class TableData { + long total; + List rows; + public TableData(long total, List rows) { + this.total = total; + this.rows = rows; + } + public TableData(Integer type ,long total, List rows) { + this.total = total; + this.rows = rows; + } + + public TableData() { + } + + public long getTotal() { + return total; + } + + public void setTotal(long total) { + this.total = total; + } + + public List getRows() { + return rows; + } + + public void setRows(List rows) { + this.rows = rows; + } + + } +} diff --git a/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/web/domain/TreeEntity.java b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/web/domain/TreeEntity.java new file mode 100644 index 0000000..69b5dbd --- /dev/null +++ b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/web/domain/TreeEntity.java @@ -0,0 +1,79 @@ +package com.aiotagro.common.core.web.domain; + +import java.util.ArrayList; +import java.util.List; + +/** + * Tree基类 + * + * @author Carson + */ +public class TreeEntity extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 父菜单名称 */ + private String parentName; + + /** 父菜单ID */ + private Long parentId; + + /** 显示顺序 */ + private Integer orderNum; + + /** 祖级列表 */ + private String ancestors; + + /** 子部门 */ + private List children = new ArrayList<>(); + + public String getParentName() + { + return parentName; + } + + public void setParentName(String parentName) + { + this.parentName = parentName; + } + + public Long getParentId() + { + return parentId; + } + + public void setParentId(Long parentId) + { + this.parentId = parentId; + } + + public Integer getOrderNum() + { + return orderNum; + } + + public void setOrderNum(Integer orderNum) + { + this.orderNum = orderNum; + } + + public String getAncestors() + { + return ancestors; + } + + public void setAncestors(String ancestors) + { + this.ancestors = ancestors; + } + + public List getChildren() + { + return children; + } + + public void setChildren(List children) + { + this.children = children; + } +} diff --git a/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/web/page/PageDomain.java b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/web/page/PageDomain.java new file mode 100644 index 0000000..0d3b6bb --- /dev/null +++ b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/web/page/PageDomain.java @@ -0,0 +1,101 @@ +package com.aiotagro.common.core.web.page; + +import com.aiotagro.common.core.utils.StringUtils; + +/** + * 分页数据 + * + * @author Carson + */ +public class PageDomain +{ + /** 当前记录起始索引 */ + private Integer pageNum; + + /** 每页显示记录数 */ + private Integer pageSize; + + /** 排序列 */ + private String orderByColumn; + + /** 排序的方向desc或者asc */ + private String isAsc = "asc"; + + /** 分页参数合理化 */ + private Boolean reasonable = true; + + public String getOrderBy() + { + if (StringUtils.isEmpty(orderByColumn)) + { + return ""; + } + return StringUtils.toUnderScoreCase(orderByColumn) + " " + isAsc; + } + + public Integer getPageNum() + { + return pageNum; + } + + public void setPageNum(Integer pageNum) + { + this.pageNum = pageNum; + } + + public Integer getPageSize() + { + return pageSize; + } + + public void setPageSize(Integer pageSize) + { + this.pageSize = pageSize; + } + + public String getOrderByColumn() + { + return orderByColumn; + } + + public void setOrderByColumn(String orderByColumn) + { + this.orderByColumn = orderByColumn; + } + + public String getIsAsc() + { + return isAsc; + } + + public void setIsAsc(String isAsc) + { + if (StringUtils.isNotEmpty(isAsc)) + { + // 兼容前端排序类型 + if ("ascending".equals(isAsc)) + { + isAsc = "asc"; + } + else if ("descending".equals(isAsc)) + { + isAsc = "desc"; + } + this.isAsc = isAsc; + } + } + + public Boolean getReasonable() + { + if (StringUtils.isNull(reasonable)) + { + return Boolean.TRUE; + } + return reasonable; + } + + public void setReasonable(Boolean reasonable) + { + this.reasonable = reasonable; + } +} diff --git a/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/web/page/TableDataInfo.java b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/web/page/TableDataInfo.java new file mode 100644 index 0000000..3336313 --- /dev/null +++ b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/web/page/TableDataInfo.java @@ -0,0 +1,85 @@ +package com.aiotagro.common.core.web.page; + +import java.io.Serializable; +import java.util.List; + +/** + * 表格分页数据对象 + * + * @author Carson + */ +public class TableDataInfo implements Serializable +{ + private static final long serialVersionUID = 1L; + + /** 总记录数 */ + private long total; + + /** 列表数据 */ + private List rows; + + /** 消息状态码 */ + private int code; + + /** 消息内容 */ + private String msg; + + /** + * 表格数据对象 + */ + public TableDataInfo() + { + } + + /** + * 分页 + * + * @param list 列表数据 + * @param total 总记录数 + */ + public TableDataInfo(List list, int total) + { + this.rows = list; + this.total = total; + } + + public long getTotal() + { + return total; + } + + public void setTotal(long total) + { + this.total = total; + } + + public List getRows() + { + return rows; + } + + public void setRows(List rows) + { + this.rows = rows; + } + + public int getCode() + { + return code; + } + + public void setCode(int code) + { + this.code = code; + } + + public String getMsg() + { + return msg; + } + + public void setMsg(String msg) + { + this.msg = msg; + } +} \ No newline at end of file diff --git a/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/web/page/TableSupport.java b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/web/page/TableSupport.java new file mode 100644 index 0000000..6091d8d --- /dev/null +++ b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/web/page/TableSupport.java @@ -0,0 +1,56 @@ +package com.aiotagro.common.core.web.page; + +import com.aiotagro.common.core.text.Convert; +import com.aiotagro.common.core.utils.ServletUtils; + +/** + * 表格数据处理 + * + * @author Carson + */ +public class TableSupport +{ + /** + * 当前记录起始索引 + */ + public static final String PAGE_NUM = "pageNum"; + + /** + * 每页显示记录数 + */ + public static final String PAGE_SIZE = "pageSize"; + + /** + * 排序列 + */ + public static final String ORDER_BY_COLUMN = "orderByColumn"; + + /** + * 排序的方向 "desc" 或者 "asc". + */ + public static final String IS_ASC = "isAsc"; + + /** + * 分页参数合理化 + */ + public static final String REASONABLE = "reasonable"; + + /** + * 封装分页对象 + */ + public static PageDomain getPageDomain() + { + PageDomain pageDomain = new PageDomain(); + pageDomain.setPageNum(Convert.toInt(ServletUtils.getParameter(PAGE_NUM), 1)); + pageDomain.setPageSize(Convert.toInt(ServletUtils.getParameter(PAGE_SIZE), 10)); + pageDomain.setOrderByColumn(ServletUtils.getParameter(ORDER_BY_COLUMN)); + pageDomain.setIsAsc(ServletUtils.getParameter(IS_ASC)); + pageDomain.setReasonable(ServletUtils.getParameterToBool(REASONABLE)); + return pageDomain; + } + + public static PageDomain buildPageRequest() + { + return getPageDomain(); + } +} diff --git a/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/xss/Xss.java b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/xss/Xss.java new file mode 100644 index 0000000..ba59cbe --- /dev/null +++ b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/xss/Xss.java @@ -0,0 +1,27 @@ +package com.aiotagro.common.core.xss; + +import javax.validation.Constraint; +import javax.validation.Payload; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * 自定义xss校验注解 + * + * @author Carson + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(value = { ElementType.METHOD, ElementType.FIELD, ElementType.CONSTRUCTOR, ElementType.PARAMETER }) +@Constraint(validatedBy = { XssValidator.class }) +public @interface Xss +{ + String message() + + default "不允许任何脚本运行"; + + Class[] groups() default {}; + + Class[] payload() default {}; +} diff --git a/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/xss/XssValidator.java b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/xss/XssValidator.java new file mode 100644 index 0000000..53725f8 --- /dev/null +++ b/tradeCattle/aiotagro-core/src/main/java/com/aiotagro/common/core/xss/XssValidator.java @@ -0,0 +1,35 @@ +package com.aiotagro.common.core.xss; + +import com.aiotagro.common.core.utils.StringUtils; + +import javax.validation.ConstraintValidator; +import javax.validation.ConstraintValidatorContext; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * 自定义xss校验注解实现 + * + * @author Carson + */ +public class XssValidator implements ConstraintValidator +{ + private static final String HTML_PATTERN = "<(\\S*?)[^>]*>.*?|<.*? />"; + + @Override + public boolean isValid(String value, ConstraintValidatorContext constraintValidatorContext) + { + if (StringUtils.isBlank(value)) + { + return true; + } + return !containsHtml(value); + } + + public static boolean containsHtml(String value) + { + Pattern pattern = Pattern.compile(HTML_PATTERN); + Matcher matcher = pattern.matcher(value); + return matcher.matches(); + } +} \ No newline at end of file diff --git a/tradeCattle/aiotagro-redis/pom.xml b/tradeCattle/aiotagro-redis/pom.xml new file mode 100644 index 0000000..025a447 --- /dev/null +++ b/tradeCattle/aiotagro-redis/pom.xml @@ -0,0 +1,35 @@ + + + 4.0.0 + + com.aiotagro + cattletrade + 1.0.1 + + + aiotagro-redis + + + aiotagro-common-redis缓存服务 + + + + + + + org.springframework.boot + spring-boot-starter-data-redis + + + + + com.aiotagro + aiotagro-core + + + + + + \ No newline at end of file diff --git a/tradeCattle/aiotagro-redis/src/main/java/com/aiotagro/common/redis/configure/FastJson2JsonRedisSerializer.java b/tradeCattle/aiotagro-redis/src/main/java/com/aiotagro/common/redis/configure/FastJson2JsonRedisSerializer.java new file mode 100644 index 0000000..345f081 --- /dev/null +++ b/tradeCattle/aiotagro-redis/src/main/java/com/aiotagro/common/redis/configure/FastJson2JsonRedisSerializer.java @@ -0,0 +1,53 @@ +package com.aiotagro.common.redis.configure; + +import com.aiotagro.common.core.constant.Constants; +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONReader; +import com.alibaba.fastjson2.JSONWriter; +import com.alibaba.fastjson2.filter.Filter; +import org.springframework.data.redis.serializer.RedisSerializer; +import org.springframework.data.redis.serializer.SerializationException; + +import java.nio.charset.Charset; + +/** + * Redis使用FastJson序列化 + * + * @author aiotagro + */ +public class FastJson2JsonRedisSerializer implements RedisSerializer +{ + public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8"); + + static final Filter AUTO_TYPE_FILTER = JSONReader.autoTypeFilter(Constants.JSON_WHITELIST_STR); + + private Class clazz; + + public FastJson2JsonRedisSerializer(Class clazz) + { + super(); + this.clazz = clazz; + } + + @Override + public byte[] serialize(T t) throws SerializationException + { + if (t == null) + { + return new byte[0]; + } + return JSON.toJSONString(t, JSONWriter.Feature.WriteClassName).getBytes(DEFAULT_CHARSET); + } + + @Override + public T deserialize(byte[] bytes) throws SerializationException + { + if (bytes == null || bytes.length <= 0) + { + return null; + } + String str = new String(bytes, DEFAULT_CHARSET); + + return JSON.parseObject(str, clazz, AUTO_TYPE_FILTER); + } +} diff --git a/tradeCattle/aiotagro-redis/src/main/java/com/aiotagro/common/redis/configure/RedisConfig.java b/tradeCattle/aiotagro-redis/src/main/java/com/aiotagro/common/redis/configure/RedisConfig.java new file mode 100644 index 0000000..1335d30 --- /dev/null +++ b/tradeCattle/aiotagro-redis/src/main/java/com/aiotagro/common/redis/configure/RedisConfig.java @@ -0,0 +1,43 @@ +package com.aiotagro.common.redis.configure; + +import org.springframework.boot.autoconfigure.AutoConfigureBefore; +import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration; +import org.springframework.cache.annotation.CachingConfigurerSupport; +import org.springframework.cache.annotation.EnableCaching; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.serializer.StringRedisSerializer; + +/** + * redis配置 + * + * @author aiotagro + */ +@Configuration +@EnableCaching +@AutoConfigureBefore(RedisAutoConfiguration.class) +public class RedisConfig extends CachingConfigurerSupport +{ + @Bean + @SuppressWarnings(value = { "unchecked", "rawtypes" }) + public RedisTemplate redisTemplate(RedisConnectionFactory connectionFactory) + { + RedisTemplate template = new RedisTemplate<>(); + template.setConnectionFactory(connectionFactory); + + FastJson2JsonRedisSerializer serializer = new FastJson2JsonRedisSerializer(Object.class); + + // 使用StringRedisSerializer来序列化和反序列化redis的key值 + template.setKeySerializer(new StringRedisSerializer()); + template.setValueSerializer(serializer); + + // Hash的key也采用StringRedisSerializer的序列化方式 + template.setHashKeySerializer(new StringRedisSerializer()); + template.setHashValueSerializer(serializer); + + template.afterPropertiesSet(); + return template; + } +} diff --git a/tradeCattle/aiotagro-redis/src/main/java/com/aiotagro/common/redis/service/RedisService.java b/tradeCattle/aiotagro-redis/src/main/java/com/aiotagro/common/redis/service/RedisService.java new file mode 100644 index 0000000..2d5ff40 --- /dev/null +++ b/tradeCattle/aiotagro-redis/src/main/java/com/aiotagro/common/redis/service/RedisService.java @@ -0,0 +1,328 @@ +package com.aiotagro.common.redis.service; + +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.BoundSetOperations; +import org.springframework.data.redis.core.HashOperations; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.core.ValueOperations; +import org.springframework.data.redis.core.script.DefaultRedisScript; +import org.springframework.stereotype.Component; + +import java.util.*; +import java.util.concurrent.TimeUnit; + +/** + * spring redis 工具类 + * + * @author aiotagro + **/ +@SuppressWarnings(value = { "unchecked", "rawtypes" }) +@Component +public class RedisService +{ + @Autowired + public RedisTemplate redisTemplate; + + private static final Logger logger = LoggerFactory.getLogger(RedisService.class); + + + public static final String SETNX_SCRIPT = "return redis.call('setnx',KEYS[1], ARGV[1])"; + + /** + * 缓存基本的对象,Integer、String、实体类等 + * + * @param key 缓存的键值 + * @param value 缓存的值 + */ + public void setCacheObject(final String key, final T value) + { + redisTemplate.opsForValue().set(key, value); + } + + /** + * 缓存基本的对象,Integer、String、实体类等 + * + * @param key 缓存的键值 + * @param value 缓存的值 + * @param timeout 时间 + * @param timeUnit 时间颗粒度 + */ + public void setCacheObject(final String key, final T value, final Long timeout, final TimeUnit timeUnit) { + redisTemplate.opsForValue().set(key, value, timeout, timeUnit); + } + + /** + * 设置有效时间 + * + * @param key Redis键 + * @param timeout 超时时间 + * @return true=设置成功;false=设置失败 + */ + public boolean expire(final String key, final long timeout) + { + return expire(key, timeout, TimeUnit.SECONDS); + } + + /** + * 设置有效时间 + * + * @param key Redis键 + * @param timeout 超时时间 + * @param unit 时间单位 + * @return true=设置成功;false=设置失败 + */ + public boolean expire(final String key, final long timeout, final TimeUnit unit) + { + return redisTemplate.expire(key, timeout, unit); + } + + /** + * 获取有效时间 + * + * @param key Redis键 + * @return 有效时间 + */ + public long getExpire(final String key) + { + return redisTemplate.getExpire(key); + } + + /** + * 判断 key是否存在 + * + * @param key 键 + * @return true 存在 false不存在 + */ + public Boolean hasKey(String key) + { + return redisTemplate.hasKey(key); + } + + /** + * 获得缓存的基本对象。 + * + * @param key 缓存键值 + * @return 缓存键值对应的数据 + */ + public T getCacheObject(final String key) + { + ValueOperations operation = redisTemplate.opsForValue(); + return operation.get(key); + } + + /** + * 删除单个对象 + * + * @param key + */ + public boolean deleteObject(final String key) + { + return redisTemplate.delete(key); + } + + /** + * 删除集合对象 + * + * @param collection 多个对象 + * @return + */ + public boolean deleteObject(final Collection collection) + { + return redisTemplate.delete(collection) > 0; + } + + /** + * 缓存List数据 + * + * @param key 缓存的键值 + * @param dataList 待缓存的List数据 + * @return 缓存的对象 + */ + public long setCacheList(final String key, final List dataList) + { + Long count = redisTemplate.opsForList().rightPushAll(key, dataList); + return count == null ? 0 : count; + } + + /** + * 获得缓存的list对象 + * + * @param key 缓存的键值 + * @return 缓存键值对应的数据 + */ + public List getCacheList(final String key) + { + return redisTemplate.opsForList().range(key, 0, -1); + } + + /** + * 缓存Set + * + * @param key 缓存键值 + * @param dataSet 缓存的数据 + * @return 缓存数据的对象 + */ + public BoundSetOperations setCacheSet(final String key, final Set dataSet) + { + BoundSetOperations setOperation = redisTemplate.boundSetOps(key); + Iterator it = dataSet.iterator(); + while (it.hasNext()) + { + setOperation.add(it.next()); + } + return setOperation; + } + + /** + * 获得缓存的set + * + * @param key + * @return + */ + public Set getCacheSet(final String key) + { + return redisTemplate.opsForSet().members(key); + } + + /** + * 缓存Map + * + * @param key + * @param dataMap + */ + public void setCacheMap(final String key, final Map dataMap) + { + if (dataMap != null) { + redisTemplate.opsForHash().putAll(key, dataMap); + } + } + + /** + * 获得缓存的Map + * + * @param key + * @return + */ + public Map getCacheMap(final String key) + { + return redisTemplate.opsForHash().entries(key); + } + + /** + * 往Hash中存入数据 + * + * @param key Redis键 + * @param hKey Hash键 + * @param value 值 + */ + public void setCacheMapValue(final String key, final String hKey, final T value) + { + redisTemplate.opsForHash().put(key, hKey, value); + } + + /** + * 获取Hash中的数据 + * + * @param key Redis键 + * @param hKey Hash键 + * @return Hash中的对象 + */ + public T getCacheMapValue(final String key, final String hKey) + { + HashOperations opsForHash = redisTemplate.opsForHash(); + return opsForHash.get(key, hKey); + } + + /** + * 获取多个Hash中的数据 + * + * @param key Redis键 + * @param hKeys Hash键集合 + * @return Hash对象集合 + */ + public List getMultiCacheMapValue(final String key, final Collection hKeys) + { + return redisTemplate.opsForHash().multiGet(key, hKeys); + } + + /** + * 删除Hash中的某条数据 + * + * @param key Redis键 + * @param hKey Hash键 + * @return 是否成功 + */ + public boolean deleteCacheMapValue(final String key, final String hKey) + { + return redisTemplate.opsForHash().delete(key, hKey) > 0; + } + + /** + * 获得缓存的基本对象列表 + * + * @param pattern 字符串前缀 + * @return 对象列表 + */ + public Collection keys(final String pattern) + { + return redisTemplate.keys(pattern); + } + + + public boolean lock(String lockKey, long lockTime) { + boolean lockSuccess = false; + try { + boolean result =setnx(lockKey, String.valueOf(System.currentTimeMillis() + lockTime)); + if (result) { + lockSuccess = true; + } else { + Object oldTime =getCacheObject(lockKey); + if (oldTime !=null && StringUtils.isNumeric(String.valueOf(oldTime))) { + long vlockTime = Long.valueOf(oldTime.toString()); + if (vlockTime < System.currentTimeMillis()) { + // 可能会存在覆盖问题,前提是key相同,存入的都是时间戳,相差很短 + Object prvOldTime = getCacheObject(lockKey); + setCacheObject(lockKey, String.valueOf(System.currentTimeMillis() + lockTime)); + if (prvOldTime == null || prvOldTime.toString().equals(oldTime.toString())) { + // 第一个赋值的 + lockSuccess = true; + } + } + } + } + if (lockSuccess) { + expire(lockKey, (int) (lockTime / 1000)); + } + } catch (Exception e) { + logger.error("get lock error", e); + } + return lockSuccess; + } + + public boolean setnx(String key,String value) { + //自定义脚本 + DefaultRedisScript script = new DefaultRedisScript<>(SETNX_SCRIPT, List.class); + List rst = (List) redisTemplate.execute(script, Collections.singletonList(key), value); + if(rst.get(0) == 1){ + return true; + }else{ + return false; + } + } + + public Long lpush(String key,Object value) { + return redisTemplate.opsForList().leftPush(key,value); + } + + public Object rpop(String key) { + return redisTemplate.opsForList().rightPop(key); + } + + public Long len(String key) { + return redisTemplate.opsForList().size(key); + } + +} diff --git a/tradeCattle/aiotagro-redis/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst b/tradeCattle/aiotagro-redis/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst new file mode 100644 index 0000000..007febe --- /dev/null +++ b/tradeCattle/aiotagro-redis/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst @@ -0,0 +1,3 @@ +com\aiotagro\common\redis\configure\FastJson2JsonRedisSerializer.class +com\aiotagro\common\redis\configure\RedisConfig.class +com\aiotagro\common\redis\service\RedisService.class diff --git a/tradeCattle/aiotagro-redis/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst b/tradeCattle/aiotagro-redis/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst new file mode 100644 index 0000000..2bc643d --- /dev/null +++ b/tradeCattle/aiotagro-redis/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst @@ -0,0 +1,3 @@ +C:\cattleTransport\tradeCattle\aiotagro-redis\src\main\java\com\aiotagro\common\redis\configure\FastJson2JsonRedisSerializer.java +C:\cattleTransport\tradeCattle\aiotagro-redis\src\main\java\com\aiotagro\common\redis\configure\RedisConfig.java +C:\cattleTransport\tradeCattle\aiotagro-redis\src\main\java\com\aiotagro\common\redis\service\RedisService.java diff --git a/tradeCattle/database_migration_add_id_card_field.sql b/tradeCattle/database_migration_add_id_card_field.sql new file mode 100644 index 0000000..dde86e1 --- /dev/null +++ b/tradeCattle/database_migration_add_id_card_field.sql @@ -0,0 +1,27 @@ +-- ============================================= +-- 数据库迁移脚本:为 member_driver 表添加 id_card 字段 +-- 用途:存储司机身份证前后面照片地址 +-- 创建时间:2025-10-20 +-- ============================================= + +-- 检查字段是否已存在,如果不存在则添加 +SET @sql = ( + SELECT IF( + (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS + WHERE TABLE_SCHEMA = DATABASE() + AND TABLE_NAME = 'member_driver' + AND COLUMN_NAME = 'id_card') = 0, + 'ALTER TABLE member_driver ADD COLUMN id_card TEXT COMMENT ''身份证前后面照片地址(多个URL用逗号分隔)'' AFTER car_img;', + 'SELECT ''字段 id_card 已存在,无需添加'' AS message;' + ) +); + +PREPARE stmt FROM @sql; +EXECUTE stmt; +DEALLOCATE PREPARE stmt; + +-- 显示表结构确认字段已添加 +DESCRIBE member_driver; + +-- 显示添加结果 +SELECT 'id_card 字段添加完成' AS result; diff --git a/tradeCattle/pom.xml b/tradeCattle/pom.xml new file mode 100644 index 0000000..9ffcd68 --- /dev/null +++ b/tradeCattle/pom.xml @@ -0,0 +1,328 @@ + + + 4.0.0 + com.aiotagro + cattletrade + 1.0.1 + aiotagro-cattletrade + aiotagro-cattletrade + + 1.0.1 + 1.8 + UTF-8 + UTF-8 + 2.6.0 + 2.7.11 + 3.0.0 + 1.27.2 + 2.3.3 + 1.2.20 + 4.2.0 + 2.13.0 + 2.3 + 2.0.43 + 0.9.1 + 8.2.2 + 4.1.2 + 2.14.4 + 3.5.3.2 + 3.0.6 + 2.2.18 + 1.37.0 + 5.8.25 + 6.4.11 + 1.21 + 5.6.89 + 3.1.423 + + + + + org.springframework.boot + spring-boot-starter + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + + com.github.qcloudsms + qcloudsms + 1.0.6 + + + com.baomidou + mybatis-plus-generator + 3.5.1 + + + + org.springframework.boot + spring-boot-starter-freemarker + + + + + aiotagro-core + aiotagro-redis + aiotagro-cattle-trade + + + + + + + org.springframework.boot + spring-boot-dependencies + ${spring-boot.version} + pom + import + + + + + org.springframework.boot + spring-boot-dependencies + ${spring-boot.version} + pom + import + + + + + com.github.tobato + fastdfs-client + ${tobato.version} + + + + cn.hutool + hutool-all + ${hutool.version} + + + + + + + + commons-io + commons-io + ${commons.io.version} + + + + + org.apache.poi + poi-ooxml + ${poi.version} + + + + com.alibaba.fastjson2 + fastjson2 + ${fastjson.version} + + + + + io.jsonwebtoken + jjwt + ${jjwt.version} + + + + + com.alibaba + transmittable-thread-local + ${transmittable-thread-local.version} + + + + com.aiotagro + aiotagro-core + ${aiotagro.version} + + + + com.aiotagro + aiotagro-redis + ${aiotagro.version} + + + com.baomidou + mybatis-plus-boot-starter + ${mybatis-plus.version} + + + + + + io.github.mouzt + bizlog-sdk + ${bizlog.version} + + + + + com.github.lianjiatech + retrofit-spring-boot-starter + ${retrofit.version} + + + + + + com.alibaba + druid + ${druid.version} + + + + + cn.dev33 + sa-token-spring-boot-starter + ${saToken.version} + + + + + + cn.dev33 + sa-token-redis-jackson + ${saToken.version} + + + + + cn.dev33 + sa-token-jwt + ${saToken.version} + + + + + + com.github.oshi + oshi-core + ${oshi.version} + + + + + eu.bitwalker + UserAgentUtils + ${bitwalker.version} + + + + + + + com.tencentcloudapi + tencentcloud-sdk-java + ${tencentcloud.java.sdk} + + + + + com.qcloud + cos_api + ${tencent.cos.version} + + + + + + + + + pom + + + + org.apache.maven.plugins + maven-compiler-plugin + + ${java.version} + ${java.version} + ${project.build.sourceEncoding} + + + + + + + org.springframework.boot + spring-boot-maven-plugin + ${spring-boot.version} + + + + repackage + + + + + + + + + + + public + aliyun nexus + https://maven.aliyun.com/repository/public + + true + + + + + + + public + aliyun nexus + https://maven.aliyun.com/repository/public + + true + + + false + + + + + + + dev + + true + + + dev + + + + demo + + demo + + + + + prod + + prod + + + + diff --git a/tradeCattle/verify_id_card_field.sql b/tradeCattle/verify_id_card_field.sql new file mode 100644 index 0000000..4925ab1 --- /dev/null +++ b/tradeCattle/verify_id_card_field.sql @@ -0,0 +1,50 @@ +-- ============================================= +-- 测试脚本:验证 id_card 字段 +-- 用途:验证 member_driver 表的 id_card 字段是否正确添加 +-- ============================================= + +-- 1. 检查表是否存在 +SELECT + CASE + WHEN COUNT(*) > 0 THEN 'member_driver 表存在' + ELSE 'member_driver 表不存在' + END AS table_status +FROM INFORMATION_SCHEMA.TABLES +WHERE TABLE_SCHEMA = DATABASE() + AND TABLE_NAME = 'member_driver'; + +-- 2. 检查 id_card 字段是否存在 +SELECT + CASE + WHEN COUNT(*) > 0 THEN 'id_card 字段存在' + ELSE 'id_card 字段不存在' + END AS column_status +FROM INFORMATION_SCHEMA.COLUMNS +WHERE TABLE_SCHEMA = DATABASE() + AND TABLE_NAME = 'member_driver' + AND COLUMN_NAME = 'id_card'; + +-- 3. 显示 id_card 字段的详细信息 +SELECT + COLUMN_NAME as '字段名', + DATA_TYPE as '数据类型', + CHARACTER_MAXIMUM_LENGTH as '最大长度', + IS_NULLABLE as '允许空值', + COLUMN_DEFAULT as '默认值', + COLUMN_COMMENT as '注释', + ORDINAL_POSITION as '字段位置' +FROM INFORMATION_SCHEMA.COLUMNS +WHERE TABLE_SCHEMA = DATABASE() + AND TABLE_NAME = 'member_driver' + AND COLUMN_NAME = 'id_card'; + +-- 4. 显示 member_driver 表的完整结构 +DESCRIBE member_driver; + +-- 5. 测试插入和查询(可选,仅用于测试) +-- INSERT INTO member_driver (member_id, username, car_number, id_card, create_time) +-- VALUES (999, '测试司机', '测试车牌', 'https://example.com/id1.jpg,https://example.com/id2.jpg', NOW()); + +-- SELECT id, username, car_number, id_card FROM member_driver WHERE username = '测试司机'; + +-- DELETE FROM member_driver WHERE username = '测试司机';